1. 程式人生 > >安卓:一個簡單的日期選擇控制元件

安卓:一個簡單的日期選擇控制元件

package com.example.listpickerdialog;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

/**
 * Created by nlx on 2018-09-18, at 14:19
 * <p>
 * date picker
 */
public class DatePickerPop extends PopupWindow {


    private static ContentType[] mContentType;
    private ArrayList<String> mDayListData;
    private MyAdapter mDayListAdapter;
    private Calendar mCurrentCalendar;
    private onConfirmListener mListener;
    private ListView mDayListView;
    private String[] mDefaultDate;


    public enum ContentType {
        YEAR,
        DAY,
        MONTH;
    }

    //btn
    public static final int BACKGRO_COLOR_BTN = Color.parseColor("#b6bbc2");
    //title

    public static final int BACKGRO_COLOR_TITLE = Color.parseColor("#D7DCDF");

    //item
    public static final int HEIGHT = 700;
    public static final int ITEM_COUNT = 5;
    public static final int ITEM_HEIGHT = HEIGHT / (ITEM_COUNT + 1) + 10;

    //text
    public static final int TEXT_SIZE_FOCUSING = 28;
    public static final int TEXT_SIZE_DEFAULT = 22;
    public static final int TEXT_COLOT_DEFAULT = Color.parseColor("#b6bbc2");
    public static final int TEXT_COLOR_FOCUSING = Color.parseColor("#1a1a1a");


    //content
    public static final int YEAR_OFFSET = 50;
    private Context mContext;

    private TextView mCurrentFocusingViewInMonthList;
    private TextView mCurrentFocusingViewInYearList;
    private TextView mCurrentFocusingViewInDayList;

    public DatePickerPop(Context context) {
        super(context);
        mContext = context;
        setHeight(HEIGHT);
        setOutsideTouchable(true);
    }

    /**
     * 內容顯示什麼,引數順序決定內容佈局
     *
     * @param type
     */
    public DatePickerPop setContentType(ContentType... type) {
        mContentType = type;
        initView();
        return this;
    }

    /**
     * 點選確定的listener
     *
     * @param listener
     * @return
     */
    public DatePickerPop setOnConfirmListener(onConfirmListener listener) {
        mListener = listener;
        return this;
    }

    /**
     * 設定彈出時預設選中的時間,注意與{@link #setContentType(ContentType...)}要匹配
     * <p>
     * 如果為空那麼預設選中當前日期
     * <p>
     * 如果超過日期範圍那麼會選中最後一個
     *
     * @param defaultDate
     * @return
     */
    public DatePickerPop setDefaultDate(String... defaultDate) {
        mDefaultDate = defaultDate;
        return this;
    }

    /**
     * 如果使用popWindow的showXXX方法,可能需要將{@linkplain #initView()}的位置放在重寫後的方法中
     *
     * @param parentView
     */
    public void showPop(View parentView) {
        initView();
        this.showAtLocation(parentView, Gravity.BOTTOM, 0, 0);
    }


    //addChildView
    private void initView() {

        LinearLayout view = new LinearLayout(mContext);
        view.setOrientation(LinearLayout.VERTICAL);

        FrameLayout contentPar = new FrameLayout(mContext);

        LinearLayout ll_main = new LinearLayout(mContext);
        ll_main.setOrientation(LinearLayout.HORIZONTAL);
        if (null == mContentType || mContentType.length < 0) {
            TextView child = new TextView(mContext);
            child.setText("沒有設定該顯示什麼內容,請呼叫setContentType(...)");
            child.setTextColor(Color.RED);
            child.setGravity(Gravity.CENTER);
            ll_main.addView(child);
        }

        //set current day
        mCurrentCalendar = Calendar.getInstance();
        mCurrentCalendar.setTime(new Date());
        for (int i = 0; null != mContentType && i < mContentType.length; i++) {

            if (mContentType[i] == ContentType.MONTH) {

                //MONTH
                ArrayList<String> monthLists = new ArrayList<>();
                //前後加空item,以便可以選到所有的item
                for (int i1 = 0; i1 < ITEM_COUNT / 2; i1++) {
                    monthLists.add("");
                }
                for (int j = 1; j < 13; j++) {
                    monthLists.add(j + "");
                }
                //前後加空item,以便可以選到所有的item
                for (int i1 = 0; i1 < ITEM_COUNT / 2; i1++) {
                    monthLists.add("");
                }

                ListView monthListView = new ListView(mContext);
                monthListView.setVerticalScrollBarEnabled(false);
                monthListView.setDivider(null);
                final MyAdapter monthAdapter = new MyAdapter(monthLists, ITEM_HEIGHT);

                monthListView.setAdapter(monthAdapter);
                LinearLayout.LayoutParams param0 = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
                param0.weight = 1;
                ll_main.addView(monthListView, param0);

                monthListView.setOnScrollListener(new AbsListView.OnScrollListener() {
                    @Override
                    public void onScrollStateChanged(AbsListView view, int scrollState) {
                        //when stop scrolling
                        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                            refreshDayList();
                        }
                    }

                    @Override
                    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                        mCurrentFocusingViewInMonthList = setFocusingViewStyle(firstVisibleItem, visibleItemCount, monthAdapter, mCurrentFocusingViewInMonthList);
                    }
                });


                if (null != mDefaultDate && i < mDefaultDate.length && !TextUtils.isEmpty(mDefaultDate[i])) {
                    int position = 0;
                    try {
                        position = Integer.parseInt(mDefaultDate[i]) - 1;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    monthListView.setSelection(position);
                } else {
                    //current day as default position
                    monthListView.setSelection(mCurrentCalendar.get(Calendar.MONTH));
                }

            } else if (mContentType[i] == ContentType.YEAR) {

                //YEAR
                ArrayList<String> yearList = new ArrayList<>();

                int currentYear = mCurrentCalendar.get(Calendar.YEAR);
                for (int k = 0; k < YEAR_OFFSET; k++) {
                    yearList.add(currentYear - YEAR_OFFSET + k + "");
                }

                for (int l = 0; l < YEAR_OFFSET; l++) {
                    yearList.add(currentYear + l + "");
                }

                ListView yearListView = new ListView(mContext);
                yearListView.setVerticalScrollBarEnabled(false);
                final MyAdapter yearAdapter = new MyAdapter(yearList, ITEM_HEIGHT);
                yearListView.setAdapter(yearAdapter);

                yearListView.setDivider(null);
                LinearLayout.LayoutParams param1 = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
                param1.weight = 1;
                ll_main.addView(yearListView, param1);

                yearListView.setOnScrollListener(new AbsListView.OnScrollListener() {
                    @Override
                    public void onScrollStateChanged(AbsListView view, int scrollState) {
                        //when stop scrolling
                        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                            refreshDayList();
                        }
                    }

                    @Override
                    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                        mCurrentFocusingViewInYearList = setFocusingViewStyle(firstVisibleItem, visibleItemCount, yearAdapter, mCurrentFocusingViewInYearList);
                    }
                });

                if (null != mDefaultDate && i < mDefaultDate.length && !TextUtils.isEmpty(mDefaultDate[i])) {
                    int defaultValue = 0;
                    try {
                        defaultValue = Integer.parseInt(mDefaultDate[i]) - currentYear + YEAR_OFFSET;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    yearListView.setSelection(defaultValue - ITEM_COUNT / 2);
                } else {
                    //current day as default position
                    yearListView.setSelection(YEAR_OFFSET - ITEM_COUNT / 2);
                }

            } else if (mContentType[i] == ContentType.DAY) {

                //DAY
                mDayListView = new ListView(mContext);
                mDayListData = new ArrayList<>();
                mDayListAdapter = new MyAdapter(mDayListData, ITEM_HEIGHT);
                mDayListView.setAdapter(mDayListAdapter);
                mDayListView.setDivider(null);
                mDayListView.setVerticalScrollBarEnabled(false);

                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, FrameLayout.LayoutParams.MATCH_PARENT);
                params.weight = 1;
                ll_main.addView(mDayListView, params);

                mDayListView.setOnScrollListener(new AbsListView.OnScrollListener() {
                    @Override
                    public void onScrollStateChanged(AbsListView view, int scrollState) {
                    }

                    @Override
                    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                        mCurrentFocusingViewInDayList = setFocusingViewStyle(firstVisibleItem, visibleItemCount, mDayListAdapter, mCurrentFocusingViewInDayList);
                    }
                });

            }
        }

        //default set day list
        if (null != mDayListData && null != mDayListAdapter) {
            refreshDayList();
            boolean setDefaultDay;
            for (int i = 0; null != mContentType && i < mContentType.length; i++) {
                if (ContentType.DAY == mContentType[i]) {

                    setDefaultDay = true;

                    int position = 0;
                    if (setDefaultDay && null != mDefaultDate && !TextUtils.isEmpty(mDefaultDate[i])) {
                        try {
                            position = Integer.parseInt(mDefaultDate[i]) - 1;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        //current day as default position
                        position = mCurrentCalendar.get(Calendar.DAY_OF_MONTH) - ITEM_COUNT / 2 + 1;
                    }
                    if (position < 0) {
                        position = 0;
                    }
                    if (position > mDayListData.size()) {
                        position = mDayListData.size() - 1;
                    }
                    mDayListView.setSelection(position);

                    break;
                }
            }

        }

        contentPar.addView(ll_main);
        contentPar.addView(new PickerHintView(mContext, ITEM_HEIGHT));

        RelativeLayout titleRl = new RelativeLayout(mContext);
        Button btn = new Button(mContext);
        btn.setText("確定");

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != mListener) {

                    StringBuilder builder = new StringBuilder();
                    for (int i = 0; null != mContentType && i < mContentType.length; i++) {
                        if (mContentType[i] == ContentType.DAY) {
                            if (null != mCurrentFocusingViewInDayList) {
                                builder.append(mCurrentFocusingViewInDayList.getText());
                            } else {
                                builder.append("");
                            }

                        } else if (mContentType[i] == ContentType.MONTH) {
                            if (null != mCurrentFocusingViewInMonthList) {
                                builder.append(mCurrentFocusingViewInMonthList.getText());
                            } else {
                                builder.append("");
                            }
                        } else if (mContentType[i] == ContentType.YEAR) {
                            if (null != mCurrentFocusingViewInYearList) {
                                builder.append(mCurrentFocusingViewInYearList.getText());
                            } else {
                                builder.append("");
                            }
                        }
                        if (i != mContentType.length - 1) {
                            builder.append("-");
                        }



                    }
                    mListener.onConfirmClicked(builder.toString());
                }
                DatePickerPop.this.dismiss();
            }
        });
        btn.setGravity(Gravity.CENTER);
        RelativeLayout.LayoutParams paramBtn = new RelativeLayout.LayoutParams(150, RelativeLayout.LayoutParams.MATCH_PARENT);
        btn.setBackgroundColor(BACKGRO_COLOR_BTN);
        paramBtn.addRule(RelativeLayout.ALIGN_PARENT_END);
        btn.setGravity(Gravity.CENTER);
        paramBtn.setMargins(0, 0, 20, 0);
        btn.setTextSize(15 );
        titleRl.addView(btn, paramBtn);
        titleRl.setBackgroundColor(BACKGRO_COLOR_TITLE);


        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
        layoutParams.weight = 1;
        view.addView(titleRl, layoutParams);

        LinearLayout.LayoutParams layoutParams1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
        layoutParams1.weight = (float) (ITEM_COUNT - 0.1);
        view.addView(contentPar, layoutParams1);

        setContentView(view);
        setBackgroundDrawable(null);

    }





    @Nullable
    private TextView setFocusingViewStyle(int firstVisibleItem, int visibleItemCount, MyAdapter adapter, TextView lastFocusedView) {

        //加深選中區域的條目顯示
        int focusingIndex = firstVisibleItem + visibleItemCount / 2;

        TextView focusingView = (TextView) adapter.getViewReference(focusingIndex);

        if (null != focusingView) {
            focusingView.setTextSize(TEXT_SIZE_FOCUSING);
            focusingView.setTextColor(TEXT_COLOR_FOCUSING);
        }

        //revert the style of last focused item
        if (null != lastFocusedView && lastFocusedView != focusingView) {
            lastFocusedView.setTextColor(TEXT_COLOT_DEFAULT);
            lastFocusedView.setTextSize(TEXT_SIZE_DEFAULT);
        }

        return focusingView;
    }

    /**
     * show days according to current month and year;
     */
    private void refreshDayList() {

        if (mDayListData == null
                || mDayListData.size() == 1
                || mDayListData.size() == 1
                || mDayListAdapter == null) {
            return;
        }

        mDayListData.clear();

        Calendar calendar = Calendar.getInstance();

        if (null != mCurrentFocusingViewInMonthList) {
            String text = (String) mCurrentFocusingViewInMonthList.getText();
            int value = 0;
            if (!"".equals(text)) {
                try {
                    value = Integer.parseInt(text) - 1;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            calendar.set(calendar.MONTH, value);
        }

        if (null != mCurrentFocusingViewInYearList) {
            String text = (String) mCurrentFocusingViewInYearList.getText();
            int value = 0;
            if (!"".equals(text)) {
                try {
                    value = Integer.parseInt(text);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            calendar.set(Calendar.YEAR, value);
        }

        int maximum = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);

        //前後加空item,以便可以選到所有的item
        for (int i = 0; i < ITEM_COUNT / 2; i++) {
            mDayListData.add("");
        }
        for (int i = 1; i <= maximum; i++) {
            mDayListData.add(i + "");
        }
        for (int i = 0; i < ITEM_COUNT / 2; i++) {
            mDayListData.add("");
        }

        mDayListAdapter.notifyDataSetChanged();

    }


    static class MyAdapter extends BaseAdapter {

        private int mItemHeight;
        ArrayList<String> mDatas;
        HashMap<Integer, View> mViewMap = new HashMap<>();

        public MyAdapter(ArrayList<String> mDatas, int itemHeight) {
            this.mDatas = mDatas;
            this.mItemHeight = itemHeight;
        }

        @Override
        public int getCount() {
            return mDatas.size();
        }

        @Override
        public String getItem(int position) {
            return mDatas.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            if (convertView == null) {
                convertView = new TextView(parent.getContext());
                convertView.setTag(convertView);
            }

            TextView tv = (TextView) convertView.getTag();
            tv.setText(mDatas.get(position));
            tv.setGravity(Gravity.CENTER);
            tv.setMinHeight(mItemHeight);
            tv.setBackgroundColor(Color.WHITE);
            tv.setTextColor(TEXT_COLOT_DEFAULT);
            tv.setTextSize(TEXT_SIZE_DEFAULT);
            mViewMap.put(position, tv);
            return convertView;
        }

        View getViewReference(int position) {
            return mViewMap.get(position);
        }

    }

    //兩條橫線作為選中提示框
    static class PickerHintView extends View {

        public static final int LINE_PADDING = 25;
        private int mItemHeight;
        private int mWidth;
        private int mHeight;

        public PickerHintView(Context context, int itemHeight) {
            super(context);
            setBackgroundColor(Color.TRANSPARENT);
            mItemHeight = itemHeight;
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            //center y
            int line1Y = mHeight / 2 - mItemHeight / 2;
            int line2Y = line1Y + mItemHeight;

            Paint paint = new Paint();
            paint.setColor(Color.GRAY);
            paint.setStrokeWidth(3);
            if (mContentType.length == 2) {
                //left  line1
                int endXLeft = mWidth / 2 - LINE_PADDING;
                canvas.drawLine(LINE_PADDING, line1Y, endXLeft, line1Y, paint);
                //left line2
                canvas.drawLine(LINE_PADDING, line2Y, endXLeft, line2Y, paint);

                //right line1
                canvas.drawLine(endXLeft + LINE_PADDING, line1Y, mWidth - LINE_PADDING, line1Y, paint);
                //right line2
                canvas.drawLine(endXLeft + LINE_PADDING, line2Y, mWidth - LINE_PADDING, line2Y, paint);
            } else if (mContentType.length == 3) {
                //left line1
                int stopXLine1 = mWidth / 3 - LINE_PADDING;
                canvas.drawLine(LINE_PADDING, line1Y, stopXLine1, line1Y, paint);
                //left line2
                canvas.drawLine(LINE_PADDING, line2Y, stopXLine1, line2Y, paint);

                //middle line1
                canvas.drawLine(stopXLine1 + 2 * LINE_PADDING, line1Y, stopXLine1 + LINE_PADDING + mWidth / 3, line1Y, paint);
                //middle lin2
                canvas.drawLine(stopXLine1 + 2 * LINE_PADDING, line2Y, stopXLine1 + LINE_PADDING + mWidth / 3, line2Y, paint);

                //right line1
                canvas.drawLine(mWidth / 3 * 2 + LINE_PADDING, line1Y, mWidth - LINE_PADDING, line1Y, paint);
                //right line2
                canvas.drawLine(mWidth / 3 * 2 + LINE_PADDING, line2Y, mWidth - LINE_PADDING, line2Y, paint);

            } else if (mContentType.length == 1) {
                //line1
                canvas.drawLine(LINE_PADDING, line1Y, mWidth - LINE_PADDING, line1Y, paint);
                //line2
                canvas.drawLine(LINE_PADDING, line2Y, mWidth - LINE_PADDING, line2Y, paint);
            }


        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidth = w;
            mHeight = h;
        }

    }

    public interface onConfirmListener {
        /**
         * 按照傳入的contentType 返回字串 用-分隔!
         * @param string
         */
        void onConfirmClicked(String string);

    }



}