当前位置:   article > 正文

Android自定义控件:时间滚轮控件的绘制_安卓wheelpicker

安卓wheelpicker

背景

Android开发中我们会常常用到类似时间滚轮的控件,这类控件到了UI设计师手中常常会被修改成各种样子,与其从网上荡个类似的Demo漫无目的地修改,不如写个我们自己的demo,对其了如指掌,修改样式自然不成问题。下图是我们的滚轮控件实现效果:
这里写图片描述

原理

我觉得对于看似复杂的控件,我们需要一步一步拆解,将功能拆解开,对应到我们所学的知识点上,然后再将这些知识点组装起来,功能也就自然出来了。

此处我们需要继承一个View,根据Touch事件,将数据按照一定的规则绘制在View上。

这里首先就是要复写View的onTouchEvent方法,捕获手指移动的距离,根据移动距离来设置显示的数据。

同时当我们松手时,应当有一个判断标准来当前中间栏应该显示的数据,并设置一个动画,让这个数据自动移动到中间位置。

关于显示文字大小的变化效果,这里参考了网上的一些代码,使用拉动距离与控件高度一半的关系,此处我们使用了一个开口向下的抛物线的函数。

说了这么多废话,先上一张示意图吧:

这里写图片描述

代码

引用控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
    <com.vonchenchen.uistudy.ui.TimePicker.WheelPicker
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:background="#ff9e9e9e"/>
</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
实现代码

控件代码比较简单,重要部分已做注释,即粘即用

/**
 * Created by vonchenchen on 2016/3/25 0025.
 */
public class WheelPicker extends View {

    public static final String TAG = "TimePicker";

    private Context mContext;

    private int mViewHeight;
    private int mViewWidth;

    /** 中间条目宽度的 */
    private int mCenterItemHeight;

    private int mCenterY;

    private float mLastDownY;  //使用偏移量为了
    private float mMoveDistanceY = 0;
    /** 当前数组索引 */
    private int mCurrentDataIndex = 0;
    /** 绘制文本画笔 */
    private Paint mTextPaint;
    /** 绘制直线画笔 */
    private Paint mLinePaint;
    /** 文字最大尺寸 */
    private int mMaxTextSize;
    /** 间隔长度比率 */
    private float mGapRatio = 0;
    /** 是否允许滑动 */
    private boolean mIsTouchEnable = true;

    /** 是否允许下拉 */
    private boolean mIsTouchDownEnable = true;
    /** 是否允许上拉 */
    private boolean mIsTouchUpEnable = true;

    private ArrayList<String> mDataList = new ArrayList<String>();

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

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

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

        initData();

        mTextPaint = new Paint();
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setColor(0xff1f1f1f);

        mLinePaint = new Paint();
        mLinePaint.setStyle(Paint.Style.FILL);
        mLinePaint.setTextAlign(Paint.Align.CENTER);
        mLinePaint.setColor(0xff1f1f1f);
    }

    private void initData(){
        for(int i = 0; i < 24; i++){
            mDataList.add(i+"");
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        mViewHeight = getMeasuredHeight();
        mViewWidth = getMeasuredWidth();

        mCenterY = mViewHeight/2;
        mMaxTextSize = mViewHeight/8;
        mCenterItemHeight = mViewHeight/4;
        mGapRatio = 0.7f;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if(mIsTouchEnable) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    onActionDown(event);
                    break;
                case MotionEvent.ACTION_MOVE:
                    onActionMove(event);
                    break;
                case MotionEvent.ACTION_UP:
                    onActionUp(event);
                    break;
            }
        }

        return true;
    }

    private void onActionDown(MotionEvent event){
        mLastDownY  = event.getY();
        //mMoveDistanceY = 0;
    }

    private void onActionMove(MotionEvent event){
        float currentY = event.getY();
        float diff = currentY - mLastDownY;

        if(mIsTouchUpEnable && mIsTouchDownEnable) {
            mMoveDistanceY += diff;
        }else if(!mIsTouchUpEnable && mIsTouchDownEnable){   //拉到第一个 只能向下滑动
            if(diff > 0){
                mMoveDistanceY += diff;
                mIsTouchUpEnable = true;
            }
        }else if(mIsTouchUpEnable && !mIsTouchDownEnable){   //拉到最后一个 只能向上滑动
            if(diff < 0){
                mMoveDistanceY += diff;
                mIsTouchDownEnable = true;
            }
        }

        mLastDownY = currentY;

        invalidate();
    }

    private void onActionUp(MotionEvent event){

        returnBack(mMoveDistanceY, 0);
    }

    private void drawText(Canvas canvas, int currentIndex, int position){

        float length = mCenterY + mMoveDistanceY;

        if(position == 0){                   //绘制当前中间的文字
            float scale = getScale((int) mMoveDistanceY);
            mTextPaint.setTextSize((float) (mMaxTextSize * scale));
            mTextPaint.setColor(getAlphaTextColor(scale));
            Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
            int diff = (fontMetricsInt.bottom + fontMetricsInt.top)/2;

            float baseY = length-diff;
            canvas.drawText(mDataList.get(currentIndex), mViewWidth / 2, baseY, mTextPaint);

            if(mMoveDistanceY > mCenterItemHeight/2){            //向下拉 index减
                if(mCurrentDataIndex > 0) {
                    mCurrentDataIndex--;
                }else{
                    mIsTouchDownEnable = false;                   //禁止下拉
                }
                mMoveDistanceY = 0;
            }else if(mMoveDistanceY < -mCenterItemHeight/2){    //向上拉 index加
                if(mCurrentDataIndex < mDataList.size()-1) {
                    mCurrentDataIndex++;
                }else{
                    mIsTouchUpEnable = false;                     //禁止上拉
                }
                mMoveDistanceY = 0;
            }
        }else {                                    //绘制上下文字
            int realIndex = currentIndex + position;

            if(realIndex >= 0 && realIndex < mDataList.size()){
                length = length + mCenterItemHeight * mGapRatio * position;

                float scale = getScale((int) length - mCenterY);
                mTextPaint.setTextSize((float) (mMaxTextSize * scale));
                mTextPaint.setColor(getAlphaTextColor(scale));
                Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
                int diff = (fontMetricsInt.bottom + fontMetricsInt.top)/2;

                canvas.drawText(mDataList.get(realIndex), mViewWidth/2 , length-diff, mTextPaint);
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for(int i=-3; i<=3; i++){
            drawText(canvas, mCurrentDataIndex, i);
        }
        canvas.drawLine(0, mCenterY-mCenterItemHeight/2, mViewWidth, mCenterY-mCenterItemHeight/2, mLinePaint);
        canvas.drawLine(0, mCenterY + mCenterItemHeight / 2, mViewWidth, mCenterY + mCenterItemHeight / 2, mLinePaint);
    }

    private float getScale(int lengthToCenter){
        float ret = (float) (1 - Math.pow(((float)lengthToCenter)/mCenterY, 2));
        if(ret < 0){
            ret = 0;
        }
        return ret;
    }

    private void returnBack(float start, float end){
        mIsTouchEnable = false;
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
        valueAnimator.setDuration(100);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                mMoveDistanceY = (float) valueAnimator.getAnimatedValue();
                if (Math.abs(mMoveDistanceY) <= 3) {
                    mMoveDistanceY = 0;
                    mIsTouchEnable = true;
                }
                invalidate();
            }
        });
        valueAnimator.start();
    }

    /**
     * 根据比率设置字体颜色
     * @param scale
     * @return
     */
    private int getAlphaTextColor(float scale){
        int data = (int) (255 * scale);
        return (data << 24);
    }

    public String getTimeData(){
        return mDataList.get(mCurrentDataIndex);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/278823
推荐阅读
相关标签
  

闽ICP备14008679号