自定义时间选择控件(仿ios滚动效果)

1.先上自定义的控件:

/**
 * 滚轮选择器
 * author LH
 * data 2016/8/20 17:26
 */
public class WheelView extends View {

    public static final String TAG = "WheelView";

    /**
     * 自动回滚到中间的速度
     */
    public static final float SPEED = 2;

    /**
     * 除选中item外,上下各需要显示的备选项数目
     */
    public static final int SHOW_SIZE = 1;

    private Context context;

    private List itemList;
    private int itemCount;

    /**
     * item高度
     */
    private int itemHeight = 50;

    /**
     * 选中的位置,这个位置是mDataList的中心位置,一直不变
     */
    private int currentItem;

    private Paint selectPaint;
    private Paint mPaint;
    // 画背景图中单独的画笔
    private Paint centerLinePaint;

    private float centerY;
    private float centerX;

    private float mLastDownY;
    /**
     * 滑动的距离
     */
    private float mMoveLen = 0;
    private boolean isInit = false;
    private SelectListener mSelectListener;
    private Timer timer;
    private MyTimerTask mTask;

    Handler updateHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            if (Math.abs(mMoveLen) < SPEED) {
                // 如果偏移量少于最少偏移量
                mMoveLen = 0;
                if (null != timer) {
                    timer.cancel();
                    timer.purge();
                    timer = null;
                }
                if (mTask != null) {
                    mTask.cancel();
                    mTask = null;
                    performSelect();
                }
            } else {
                // 这里mMoveLen / Math.abs(mMoveLen)是为了保有mMoveLen的正负号,以实现上滚或下滚
                mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * SPEED;
            }
            invalidate();
        }

    };

    public WheelView(Context context) {
        super(context);
        init(context);
    }

    public WheelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public void setOnSelectListener(SelectListener listener) {
        mSelectListener = listener;
    }

    public void setWheelStyle(int style) {
        itemList = WheelStyle.getItemList(context, style);
        if (itemList != null) {
            itemCount = itemList.size();
            resetCurrentSelect();
            invalidate();
        } else {
            Log.i(TAG, "item is null");
        }
    }

    public void setWheelItemList(List itemList) {
        this.itemList = itemList;
        if (itemList != null) {
            itemCount = itemList.size();
            resetCurrentSelect();
        } else {
            Log.i(TAG, "item is null");
        }
    }

    private void resetCurrentSelect() {
        if (currentItem < 0) {
            currentItem = 0;
        }
        while (currentItem >= itemCount) {
            currentItem--;
        }
        if (currentItem >= 0 && currentItem < itemCount) {
            invalidate();
        } else {
            Log.i(TAG, "current item is invalid");
        }
    }

    public int getItemCount() {
        return itemCount;
    }
    /**
     * 选择选中的item的index
     * author LH
     * data 2016/9/4 11:09
     */
    public void setCurrentItem(int selected) {
        currentItem = selected;
        resetCurrentSelect();
    }

    public int getCurrentItem() {
        return currentItem;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int mViewHeight = getMeasuredHeight();
        int mViewWidth = getMeasuredWidth();
        centerX = (float) (mViewWidth / 2.0);
        centerY = (float) (mViewHeight / 2.0);

        isInit = true;
        invalidate();
    }


    private void init(Context context) {
        this.context = context;

        timer = new Timer();
        itemList = new ArrayList<>();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Style.FILL);
        mPaint.setTextAlign(Align.CENTER);
        mPaint.setColor(getResources().getColor(R.color.wheel_unselect_text));
        int size1 = (int) (SupportDisplay.getLayoutScale()*22+0.5);
        mPaint.setTextSize(size1);

        selectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        selectPaint.setStyle(Style.FILL);
        selectPaint.setTextAlign(Align.CENTER);
        selectPaint.setColor(getResources().getColor(R.color.color_1a1a1a));
        int size2 = (int) (SupportDisplay.getLayoutScale()*24+0.5);
        selectPaint.setTextSize(size2);

        centerLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        centerLinePaint.setStyle(Style.FILL);
        centerLinePaint.setTextAlign(Align.CENTER);
        centerLinePaint.setColor(getResources().getColor(R.color.wheel_unselect_text));

        // 绘制背景
        setBackground(null);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isInit) {
            drawData(canvas);
        }
    }

    private void drawData(Canvas canvas) {
        // 先绘制选中的text再往上往下绘制其余的text
        if (!itemList.isEmpty()) {
            // 绘制中间data
            drawCenterText(canvas);
            // 绘制上方data
            for (int i = 1; i < SHOW_SIZE + 1; i++) {
                drawOtherText(canvas, i, -1);
            }
            // 绘制下方data
            for (int i = 1; i < SHOW_SIZE + 1; i++) {
                drawOtherText(canvas, i, 1);
            }
        }
    }

    private void drawCenterText(Canvas canvas) {
        // text居中绘制,注意baseline的计算才能达到居中,y值是text中心坐标
        float y = centerY + mMoveLen;
        FontMetricsInt fmi = selectPaint.getFontMetricsInt();
        float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));
        canvas.drawText(itemList.get(currentItem), centerX, baseline, selectPaint);
    }

    /**
     * 绘制文本
     * author LH
     * data 2016/9/4 11:10
     * @param canvas   画布
     * @param position 距离mCurrentSelected的差值
     * @param type     1表示向下绘制,-1表示向上绘制
     */
    private void drawOtherText(Canvas canvas, int position, int type) {
        int index = currentItem + type * position;
        if (index >= itemCount) {
            index = index - itemCount;
        }
        if (index < 0) {
            index = index + itemCount;
        }
        String text = itemList.get(index);

        int itemHeight = getHeight() / (SHOW_SIZE * 2 + 1);
        float d = itemHeight * position + type * mMoveLen;
        float y = centerY + type * d;

        FontMetricsInt fmi = mPaint.getFontMetricsInt();
        float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0));
        canvas.drawText(text, centerX, baseline, mPaint);
    }

    @Override
    public void setBackground(Drawable background) {
        background = new Drawable() {
            @Override
            public void draw(Canvas canvas) {
                itemHeight = getHeight() / (SHOW_SIZE * 2 + 1);
                int width = getWidth();

                canvas.drawLine(0, itemHeight, width, itemHeight, centerLinePaint);
                canvas.drawLine(0, itemHeight * 2, width, itemHeight * 2, centerLinePaint);

                centerLinePaint.setColor(getResources().getColor(R.color.wheel_bg));
                Rect topRect = new Rect(0, 0, width, itemHeight);
                canvas.drawRect(topRect, centerLinePaint);
                Rect bottomRect = new Rect(0, itemHeight * 2, width, itemHeight * 3);
                canvas.drawRect(bottomRect, centerLinePaint);
            }

            @Override
            public void setAlpha(int alpha) {

            }

            @Override
            public void setColorFilter(ColorFilter cf) {

            }

            @Override
            public int getOpacity() {
                return 0;
            }
        };
        super.setBackground(background);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                doDown(event);
                break;
            case MotionEvent.ACTION_MOVE:
                doMove(event);
                break;
            case MotionEvent.ACTION_UP:
                doUp();
                break;
            default:
                break;
        }
        return true;
    }

    private void doDown(MotionEvent event) {
        if (mTask != null) {
            mTask.cancel();
            mTask = null;
        }
        mLastDownY = event.getY();
    }

    private void doMove(MotionEvent event) {

        mMoveLen += (event.getY() - mLastDownY);
        if (mMoveLen > itemHeight / 2) {
            // 往下滑超过离开距离
            mMoveLen = mMoveLen - itemHeight;
            currentItem--;
            if (currentItem < 0) {
                currentItem = itemCount - 1;
            }
        } else if (mMoveLen < -itemHeight / 2) {
            // 往上滑超过离开距离
            mMoveLen = mMoveLen + itemHeight;
            currentItem++;
            if (currentItem >= itemCount) {
                currentItem = 0;
            }
        }

        mLastDownY = event.getY();
        invalidate();
    }

    private void doUp() {
        // 抬起手后mCurrentSelected的位置由当前位置move到中间选中位置
        if (Math.abs(mMoveLen) < 0.0001) {
            mMoveLen = 0;
            return;
        }
        if (mTask != null) {
            mTask.cancel();
            mTask = null;
        }
        if (null == timer) {
            timer = new Timer();
        }
        mTask = new MyTimerTask(updateHandler);
        timer.schedule(mTask, 0, 10);
    }

    class MyTimerTask extends TimerTask {
        Handler handler;

        public MyTimerTask(Handler handler) {
            this.handler = handler;
        }

        @Override
        public void run() {
            handler.sendMessage(handler.obtainMessage());
        }

    }

    private void performSelect() {
        if (mSelectListener != null) {
            mSelectListener.onSelect(currentItem, itemList.get(currentItem));
        } else {
            Log.i(TAG, "null listener");
        }
    }

    public interface SelectListener {
        void onSelect(int index, String text);
    }

}
2.然后是时间选择控件的样式工具类

/**
 * 时间选择样式
 * author LH
 * data 2016/9/4 11:05
 */
public class WheelStyle {

    public static final int minYear = 1980;
    public static final int maxYear = 2020;

    /**
     * Wheel Style Hour
     */
    public static final int STYLE_HOUR = 1;
    /**
     * Wheel Style Minute
     */
    public static final int STYLE_MINUTE = 2;
    /**
     * Wheel Style Year
     */
    public static final int STYLE_YEAR = 3;
    /**
     * Wheel Style Month
     */
    public static final int STYLE_MONTH = 4;
    /**
     * Wheel Style Day
     */
    public static final int STYLE_DAY = 5;
    /**
     * Wheel Style Simple Day
     */
    public static final int STYLE_SIMPLE_DAY = 6;
    /**
     * Wheel Style Set Warn
     */
    public static final int STYLE_SET_WARN = 7;
    /**
     * Wheel Style Work Answer
     */
    public static final int STYLE_WORK_ANSWER = 8;

    private WheelStyle() {
    }

    public static List getItemList(Context context, int Style) {
        if (Style == STYLE_HOUR) {
            return createHourString();
        } else if (Style == STYLE_MINUTE) {
            return createMinuteString();
        } else if (Style == STYLE_YEAR) {
            return createYearString();
        } else if (Style == STYLE_MONTH) {
            return createMonthString();
        } else if (Style == STYLE_DAY) {
            return createDayString();
        } else if (Style == STYLE_SIMPLE_DAY) {
            return createSimpleDayString();
        } else if (Style == STYLE_SET_WARN) {
            return createSetWarnTimeString();
        } else {
            throw new IllegalArgumentException("style is illegal");
        }
    }

    private static List createHourString() {
        List wheelString = new ArrayList<>();
        for (int i = 0; i < 24; i++) {
            wheelString.add(String.format("%02d" + "时", i));
        }
        return wheelString;
    }

    private static List createMinuteString() {
        List wheelString = new ArrayList<>();
        for (int i = 0; i < 60; i++) {
            wheelString.add(String.format("%02d" + "分", i));
        }
        return wheelString;
    }

    private static List createYearString() {
        List wheelString = new ArrayList<>();
        for (int i = minYear; i <= maxYear; i++) {
            wheelString.add(Integer.toString(i));
        }
        return wheelString;
    }

    private static List createMonthString() {
        List wheelString = new ArrayList<>();
        for (int i = 1; i <= 12; i++) {
            wheelString.add(String.format("%02d" + "月", i));
        }
        return wheelString;
    }

    private static List createDayString() {
        List wheelString = new ArrayList<>();
        for (int i = 1; i <= 31; i++) {
            wheelString.add(String.format("%02d" + "日", i));
        }
        return wheelString;
    }

    private static List createSimpleDayString() {
        List wheelString = new ArrayList<>();
        wheelString.add("一天后");
        wheelString.add("两天后");
        wheelString.add("三天后");
        return wheelString;
    }

    private static List createSetWarnTimeString() {
        List wheelString = new ArrayList<>();
        wheelString.add("30分钟");
        wheelString.add("60分钟");
        wheelString.add("90分钟");
        wheelString.add("120分钟");
        return wheelString;
    }
    /**
     * 计算闰月
     *
     * @param month
     * @return
     */
    private static boolean isLeapMonth(int month) {
        return month == 1 || month == 3 || month == 5 || month == 7
                || month == 8 || month == 10 || month == 12;
    }

    /**
     * 计算闰年
     *
     * @param year
     * @return
     */
    private static boolean isLeapYear(int year) {
        return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
    }
}
3.使用的xml

4.在Java文件中设置 mWheelView .setWheelStyle(WheelStyle. STYLE_YEAR );就可以显示WheelStyle类中设置的类型了。这个类中的样式种类读者可以根据自己的需要自行添加。

5.获取当前选择的项也很简单mWheelView.getCurrentItem();就能获取到控件的当前选择的项。获取到所在的项以后剩下的就是逻辑操作了。








你可能感兴趣的:(android自定义控件,android开发)