美文网首页Android知识
点击悬浮按钮进行日历选择并监听日历时间数据的变化

点击悬浮按钮进行日历选择并监听日历时间数据的变化

作者: 破荒之恋 | 来源:发表于2017-06-09 19:21 被阅读183次

    在Android中需要时间日历选择功能的APP不少,但是实现日历功能都比较麻烦,在这里使用一款第三方开源库快速实现日历选择功能

    先在gradle中加入下面这句,刷新工程

      compile 'com.prolificinteractive:material-calendarview:1.4.2'   //日历所在的开源库
    compile 'com.jakewharton:butterknife:8.4.0'   //注解使用的第三方库
    compile 'com.android.support:design:25.0.0'
    compile 'org.greenrobot:eventbus:3.0.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    

    封装一个BaseActivity

    
    public abstract class BaseActivity extends AppCompatActivity {
    //定义一个Activity的集合,方便管理和销毁
        private List<Activity> mActivitys=new ArrayList<>();
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(getLayoutId());
            mActivitys.add(this);
            ButterKnife.bind(this);
        }
    
    //布局让继承他的Activity去实现
        public abstract int getLayoutId();
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mActivitys.remove(this);
        }
    
    //退出所有Activity
        public void finishAll(){
           for (Activity activity:mActivitys){
               if (activity!=null){
                   activity.finish();
               }
           }
        }
    
    //设置标题栏
        protected void setToolbar(Toolbar toolbar, String title){
            toolbar.setTitle(title);
            setSupportActionBar(toolbar);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setDisplayShowHomeEnabled(true);
            toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        finishAfterTransition();
                    }else {
                        finish();
                    }
                }
            });
        }
    }
    

    有日历时间的主界面

    主界面中继承BaseActivity并且实现他的抽象方法

     
    public class MainActivity extends BaseActivity implements View.OnClickListener {
    
        private static final String TAG = "MainActivity";
        RelativeLayout rlPrevious;
        TextView tvTime;
        RelativeLayout rlNext;
        LinearLayout linTimeBar;
        FloatingActionButton floatButton;
        FloatingActionButton floatButton2;
        private Calendar calendar;
        private int currentDay;
        private int currentMonth;
        private int currentYear;
    
        @Override
        public int getLayoutId() {
    
            Log.d(TAG, "------------getLayoutId: ------------------");
            return R.layout.activity_main;
        }
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
          rlNext= (RelativeLayout) findViewById(R.id.rl_next);
            rlPrevious = (RelativeLayout) findViewById(R.id.rl_previous);
            tvTime= (TextView) findViewById(R.id.tv_time);
            linTimeBar= (LinearLayout) findViewById(R.id.lin_time_bar);
            floatButton= (FloatingActionButton) findViewById(R.id.floatButton);
            floatButton2= (FloatingActionButton) findViewById(R.id.floatButton2);
    
            Log.d(TAG, "------------onCreate: ------------------");
            rlPrevious.setOnClickListener(this);
            rlNext.setOnClickListener(this);
            tvTime.setOnClickListener(this);
            floatButton.setOnClickListener(this);
            floatButton2.setOnClickListener(this);
    
            initTime();
    
            EventBus.getDefault().register(this);
    
        }
    
        @Override
        protected void onStart() {
            super.onStart();
             /* rlPrevious.setOnClickListener(this);
            rlNext.setOnClickListener(this);
            tvTime.setOnClickListener(this);
            floatButton.setOnClickListener(this);*/
            Log.d(TAG, "------------onStart: ------------------");
        }
        //获取当前时间日历
        private void initTime() {
            calendar = Calendar.getInstance();
            currentYear = calendar.get(Calendar.YEAR);
            currentMonth = calendar.get(Calendar.MONTH) + 1;
            currentDay = calendar.get(Calendar.DAY_OF_MONTH);
            Log.d(TAG, "initTime: "+currentDay+"-----"+currentMonth+"------"+currentYear);
            setTVTime();
        }
    //监听日历时间的变化,并且接收传过来的数据
        @Subscribe
        public void onEventMainThread(CalendarDay date) {
            currentYear=date.getYear();
            currentMonth=date.getMonth()+1;
            currentDay=date.getDay();
            Log.d(TAG, "-------------------onEventMainThread: -------------------"+currentDay);
            setTVTime();
        }
        //设置标题时间
        private void setTVTime() {
    
            tvTime.setText(currentYear+"年"+currentMonth+"月"+currentDay+"日");
        }
    
        @Override
        public void onClick(View v) {
            Log.d(TAG, "-------onClick:------ ");
            switch(v.getId()){
                case R.id.rl_next:
                    Toast.makeText(this, "下一天", Toast.LENGTH_SHORT).show();
                    Log.d(TAG,"下一天");
                    setNext();
                    setTVTime();
                    break;
                case R.id.rl_previous:
                    Toast.makeText(this, "前一天", Toast.LENGTH_SHORT).show();
                    setPreVious();
                    setTVTime();
                    Log.d(TAG,"前一天");
                    break;
                case R.id.floatButton:
                    Toast.makeText(this, "悬浮按钮", Toast.LENGTH_SHORT).show();
                    Intent intent=new Intent(this,CalendarActivity.class);
                    CircularAnimUtil.startActivity(this,intent,v,R.color.colorPrimary,500);
                    //CircularAnimUtil.startActivityForResult(this,intent, 10,v, R.color.colorPrimary);
                    Log.d(TAG,"悬浮按钮");
                    break;
              
                case R.id.tv_time:
                    Toast.makeText(this, "时间", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
        private void setPreVious() {
            if (currentMonth == 3) {
                currentDay--;
                if (currentDay < 1) {
                    if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
                        //闰年
                        currentDay = 29;
                        currentMonth--;
                    } else {
                        //平年
                        currentDay = 28;
                        currentMonth--;
                    }
                }
            } else if (currentMonth == 1) {
                currentDay--;
                if (currentDay < 1) {
                    currentDay = 31;
                    currentMonth = 12;
                    currentYear--;
                }
            } else if (currentMonth == 2 || currentMonth == 4 || currentMonth == 6 || currentMonth == 8 || currentMonth == 9 || currentMonth == 11) {
                currentDay--;
                if (currentDay < 1) {
                    currentDay = 31;
                    currentMonth--;
                }
            } else {
                currentDay--;
                if (currentDay < 1) {
                    currentDay = 30;
                    currentMonth--;
                }
            }
        }
        private void setNext() {
            if (currentMonth == 2) {
                //二月
                if ((currentYear % 4 == 0 && currentYear % 100 != 0) || currentYear % 400 == 0) {
                    //闰年
                    currentDay++;
                    if (currentDay > 29) {
                        currentDay = 1;
                        currentMonth++;
                    }
                } else {
                    //平年
                    currentDay++;
                    if (currentDay > 28) {
                        currentDay = 1;
                        currentMonth++;
                    }
                }
            } else if (currentMonth == 1 || currentMonth == 3 || currentMonth == 5 || currentMonth == 6 || currentMonth == 8 || currentMonth == 10) {
                //大月
                currentDay++;
                if (currentDay > 31) {
                    currentDay = 1;
                    currentMonth++;
                }
            } else if (currentMonth == 12) {
                currentDay++;
                if (currentDay > 31) {
                    currentDay = 1;
                    currentMonth = 1;
                    currentYear++;
                }
            } else {
                //小月
                currentDay++;
                if (currentDay > 30) {
                    currentDay = 1;
                    currentMonth++;
                }
            }
        }
    }
    
    
    主界面fragment_today_in_history布局如下
    <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
    
            <LinearLayout
                android:id="@+id/ll_time_bar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@android:color/holo_blue_light"
                android:gravity="center"
                android:orientation="horizontal"
                android:paddingTop="5dp"
                android:paddingBottom="10dp"
                app:layout_scrollFlags="scroll|enterAlways">
    
                <RelativeLayout
                    android:id="@+id/rl_previous"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1">
    
                    <ImageView
                        android:layout_width="35dp"
                        android:layout_height="35dp"
                        android:layout_centerInParent="true"
                        android:src="@drawable/ic_left"
                        android:tint="@android:color/white" />
                </RelativeLayout>
    
    
                <TextView
                    android:id="@+id/tv_time"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="2"
                    android:gravity="center"
                    android:text="2016年10月15日"
                    android:textColor="@android:color/white"
                    android:textSize="20sp" />
    
                <RelativeLayout
                    android:id="@+id/rl_next"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1">
    
                    <ImageView
                        android:layout_width="35dp"
                        android:layout_height="35dp"
                        android:layout_centerInParent="true"
                        android:src="@drawable/ic_right"
                        android:tint="@android:color/white" />
                </RelativeLayout>
            </LinearLayout>
    
    <android.support.design.widget.FloatingActionButton
            android:id="@+id/floatActionBtn"
            app:elevation="6dp"
            app:fabSize="normal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|right"
            android:layout_margin="30dp"
            android:src="@drawable/ic_calender" />
        </android.support.design.widget.AppBarLayout>
    

    效果如下:

    image.png

    记得初始化EventBus

         EventBus.getDefault().register(this);
    
    点击悬浮按钮跳转到日历选择器
     Intent intent=new Intent(getActivity(),CalendarActivity.class);
     CircularAnimUtil.startActivityForResult(getActivity(),intent, Constants.CODE_CALENDAR,v, R.color.colorPrimary);
    
    工具类CircularAnimUtil的具体代码,点击悬浮按钮使用动画出现日历选择界面
    /**
     * 对 ViewAnimationUtils.createCircularReveal() 方法的封装.
     * <p/>
     * Created on 16/7/20.
     * GitHub: https://github.com/XunMengWinter
     *
     * @author ice
     */
    public class CircularAnimUtil {
    
        public static final long PERFECT_MILLS = 618;
        public static final int MINI_RADIUS = 0;
    
        /**
         * 向四周伸张,直到完成显示。
         */
        @SuppressLint("NewApi")
        public static void show(View myView, float startRadius, long durationMills) {
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
                myView.setVisibility(View.VISIBLE);
                return;
            }
    
            int cx = (myView.getLeft() + myView.getRight()) / 2;
            int cy = (myView.getTop() + myView.getBottom()) / 2;
    
            int w = myView.getWidth();
            int h = myView.getHeight();
    
            // 勾股定理 & 进一法
            int finalRadius = (int) Math.sqrt(w * w + h * h) + 1;
    
            Animator anim =
                    ViewAnimationUtils.createCircularReveal(myView, cx, cy, startRadius, finalRadius);
            myView.setVisibility(View.VISIBLE);
            anim.setDuration(durationMills);
            anim.start();
        }
    
        /**
         * 由满向中间收缩,直到隐藏。
         */
        @SuppressLint("NewApi")
        public static void hide(final View myView, float endRadius, long durationMills) {
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
                myView.setVisibility(View.INVISIBLE);
                return;
            }
    
            int cx = (myView.getLeft() + myView.getRight()) / 2;
            int cy = (myView.getTop() + myView.getBottom()) / 2;
            int w = myView.getWidth();
            int h = myView.getHeight();
    
            // 勾股定理 & 进一法
            int initialRadius = (int) Math.sqrt(w * w + h * h) + 1;
    
            Animator anim =
                    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, endRadius);
            anim.setDuration(durationMills);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    myView.setVisibility(View.INVISIBLE);
                }
            });
    
            anim.start();
        }
    
        /**
         * 从指定View开始向四周伸张(伸张颜色或图片为colorOrImageRes), 然后进入另一个Activity,
         * 返回至 @thisActivity 后显示收缩动画。
         */
        @SuppressLint("NewApi")
        public static void startActivityForResult(
                final Activity thisActivity, final Intent intent, final Integer requestCode, final Bundle bundle,
                final View triggerView, int colorOrImageRes, long durationMills) {
    
            if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
                thisActivity.startActivity(intent);
                return;
            }
    
            int[] location = new int[2];
            triggerView.getLocationInWindow(location);
            final int cx = location[0] + triggerView.getWidth() / 2;
            final int cy = location[1] + triggerView.getHeight() / 2;
            final ImageView view = new ImageView(thisActivity);
            view.setScaleType(ImageView.ScaleType.CENTER_CROP);
            view.setImageResource(colorOrImageRes);
            final ViewGroup decorView = (ViewGroup) thisActivity.getWindow().getDecorView();
            int w = decorView.getWidth();
            int h = decorView.getHeight();
            decorView.addView(view, w, h);
    
            // 计算中心点至view边界的最大距离
            int maxW = Math.max(cx, w - cx);
            int maxH = Math.max(cy, h - cy);
            final int finalRadius = (int) Math.sqrt(maxW * maxW + maxH * maxH) + 1;
            Animator
                    anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
            int maxRadius = (int) Math.sqrt(w * w + h * h) + 1;
            // 若使用默认时长,则需要根据水波扩散的距离来计算实际时间
            if (durationMills == PERFECT_MILLS) {
                // 算出实际边距与最大边距的比率
                double rate = 1d * finalRadius / maxRadius;
                // 水波扩散的距离与扩散时间成正比
                durationMills = (long) (PERFECT_MILLS * rate);
            }
            final long finalDuration = durationMills;
            anim.setDuration(finalDuration);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
    
                    if (requestCode == null)
                        thisActivity.startActivity(intent);
                    else if (bundle == null)
                        thisActivity.startActivityForResult(intent, requestCode);
                    else
                        thisActivity.startActivityForResult(intent, requestCode, bundle);
    
                    // 默认渐隐过渡动画.
                    thisActivity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
    
                    // 默认显示返回至当前Activity的动画.
                    triggerView.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Animator anim =
                                    ViewAnimationUtils.createCircularReveal(view, cx, cy, finalRadius, 0);
                            anim.setDuration(finalDuration);
                            anim.addListener(new AnimatorListenerAdapter() {
                                @Override
                                public void onAnimationEnd(Animator animation) {
                                    super.onAnimationEnd(animation);
                                    try {
                                        decorView.removeView(view);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            });
                            anim.start();
                        }
                    }, 1000);
    
                }
            });
            anim.start();
        }
    
    
        /*下面的方法全是重载,用简化上面方法的构建*/
    
    
        public static void startActivityForResult(
                Activity thisActivity, Intent intent, Integer requestCode, View triggerView, int colorOrImageRes) {
            startActivityForResult(thisActivity, intent, requestCode, null, triggerView, colorOrImageRes, PERFECT_MILLS);
        }
    
        public static void startActivity(
                Activity thisActivity, Intent intent, View triggerView, int colorOrImageRes, long durationMills) {
            startActivityForResult(thisActivity, intent, null, null, triggerView, colorOrImageRes, durationMills);
        }
    
        public static void startActivity(
                Activity thisActivity, Intent intent, View triggerView, int colorOrImageRes) {
            startActivity(thisActivity, intent, triggerView, colorOrImageRes, PERFECT_MILLS);
        }
    
        public static void startActivity(Activity thisActivity, Class<?> targetClass, View triggerView, int colorOrImageRes) {
            startActivity(thisActivity, new Intent(thisActivity, targetClass), triggerView, colorOrImageRes, PERFECT_MILLS);
        }
    
        public static void show(View myView) {
            show(myView, MINI_RADIUS, PERFECT_MILLS);
        }
    
        public static void hide(View myView) {
            hide(myView, MINI_RADIUS, PERFECT_MILLS);
        }
    
    }
    

    日历界面

    /**
     * Created by an on 2017/6/10.
     */
    
    class CalendarActivity extends BaseActivity{
        private Toolbar toolbar;
        private MaterialCalendarView materialCalendarView;
        private Button btOk;
        private CalendarDay mDatas;
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            btOk = (Button) findViewById(R.id.btn_ok);
            materialCalendarView = (MaterialCalendarView) findViewById(R.id.calendarView);
            toolbar = (Toolbar) findViewById(R.id.toolbar);
            setToolbar();
            materialCalendarView.setOnDateChangedListener(new OnDateSelectedListener() {
                @Override
                public void onDateSelected(@NonNull MaterialCalendarView widget, @NonNull CalendarDay date, boolean selected) {
                    mDatas=date;
                }
            });
            btOk.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mDatas!=null) {
                        EventBus.getDefault().post(mDatas);
                    }
                    finish();
                }
            });
        }
        private void setToolbar() {
            toolbar.setTitle("选择日期");
            setSupportActionBar(toolbar);
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setDisplayShowHomeEnabled(true);
            toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    finish();
                }
            });
        }
        @Override
        public int getLayoutId() {
            return R.layout.activity_calendar;
        }
    }
    
    

    日历界面布局如下

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <include layout="@layout/toolbar"/>
    
        <com.prolificinteractive.materialcalendarview.MaterialCalendarView
            android:id="@+id/calendarView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
        <Button
            android:layout_marginTop="20dp"
            android:textColor="@android:color/white"
            android:text="确定"
            android:background="@color/colorPrimary"
            android:id="@+id/btn_ok"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    
    <include layout="@layout/toolbar"/>中toolbar的布局如下
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.Toolbar
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/titile_bg"
        android:fitsSystemWindows="true"
        app:titleTextColor="@android:color/white" />
    

    效果图如下

    image.png

    选择日期之后,确定就会把所选的日期时间返回到上一级页面,只需要在那个界面做接受数据的处理就行了。

    在这里使用EventBus来监听数据的变化

         compile 'org.greenrobot:eventbus:3.0.0'
    

    在显示日期的界面中获取返回的时间数据代码如下

      @Subscribe
        public void onEventMainThread(CalendarDay date) {
            currentYear=date.getYear();
            currentMonth=date.getMonth()+1;
            currentDay=date.getDay();
    
            Log.d(TAG, "-------------------onEventMainThread: -------------------"+currentDay);
            setTVTime();
        }
    
    //设置时间年月日
        public void setTVTime() {
            tvTime.setText(currentYear + "年" + currentMonth + "月" + currentDay + "日");
        }
    

    此时日历选择功能基本结束了,不清楚的可以看看“历史的今天”这个项目。

    相关文章

      网友评论

        本文标题:点击悬浮按钮进行日历选择并监听日历时间数据的变化

        本文链接:https://www.haomeiwen.com/subject/uoxmfxtx.html