美文网首页Android技术知识Android开发经验谈Android开发
界面无小事(四):来写个滚动选择器吧!

界面无小事(四):来写个滚动选择器吧!

作者: sean_depp | 来源:发表于2018-07-16 11:47 被阅读63次

    界面无小事(一): RecyclerView+CardView了解一下
    界面无小事(二): 让RecyclerView展示更多不同视图
    界面无小事(三):用RecyclerView + Toolbar做个文件选择器
    去github看源码


    目录

    • 效果图
    • 前言
    • Paint类
    • 计时器
    • 基线baseline
    • 滚动选择器实现
    • 最后

    效果图

    不废话, 先上效果图. 觉得有趣再往下看吧.
    去github看源码

    效果图效果图

    前言

    在pc时代, 输入一般都依靠键盘. 对于像选时间这种操作, win一般会列出全部日期, 然后让你点击选择. 说句实话, 土爆了. 当然了, 滚动选时间也土爆了(手动尴尬), 但是比win的操作方式已经有趣不少了. 而且滚动选择器我觉得还是有很多不错的应用场景的, 所以这次就写一个分享给大家.


    Paint类

    官方文档
    Paint还是很值得熟悉的一个类, 大部分函数都是set方法, 去文档看就好了. 本文有两个Paint实例, 一个是绘制文本用, 一个是绘线.

    mPaint = new Paint();
    // 设置抗锯齿
    mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
    // 设置文本对齐方式
    mPaint.setTextAlign(Paint.Align.CENTER);
    // 设置画笔颜色
    mPaint.setColor(UIUtil.getColor(R.color.colorText));
    
    mLinePaint = new Paint();
    mLinePaint.setColor(UIUtil.getColor(R.color.colorPrimaryTrans));
    

    后续代码中, Paint实例会依据曲线设置文本字号以及透明度. 所以, 我们需要自己设置最小最大字号, 最小最大透明度, 这样就可以在范围内依据函数曲线变化. 差不多就是下图, 但是y是大于0的.

    变化曲线变化曲线
    // 依据曲线设置字号
    float scale = gradient(mMax, mMoveLen);
    float size = (mMax - mMin) * scale + mMin;
    mPaint.setTextSize(size);
    
    // 依据曲线设置透明度
    mPaint.setAlpha((int) ((mMaxAlpha - mMinAlpha) * scale + mMinAlpha));
    

    计时器

    计时器是经常用到的, Android里面会用Timer, TimerTask, Handler三个组合使用. 思路就是Timer实例使用schedule函数, 传入TimerTask实例以及时间参数. 然后在TimerTask实例的run方法中让Handler实例调用sendMessage方法发送消息. 最后在handleMessage方法中处理. 代码如下:

    mTask = new MyTimerTask(mHandler);
    mTimer.schedule(mTask, 0, 10);
    
    private class MyTimerTask extends TimerTask {
        Handler handler;
    
        public MyTimerTask(Handler handler) {
            this.handler = handler;
        }
    
        @Override
        public void run() {
            handler.sendMessage(handler.obtainMessage());
        }
    }
    
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // 逐步回滚, 直到小于指定值, 选中目标
            if (Math.abs(mMoveLen) < BACK_SPEED) {
                // 选中
                mMoveLen = 0;
                if (mTask != null) {
                    mTask.cancel();
                    mTask = null;
                    select();
                }
            } else {
                // 滚动
                mMoveLen = mMoveLen - mMoveLen / Math.abs(mMoveLen) * BACK_SPEED;
            }
            invalidate();
        }
    };
    

    基线baseline

    最后来谈谈玄学, 基线. Android的绘制是基于基线的. 那什么是基线, 来看两张图片:

    玄学图1玄学图1 玄学图2玄学图2

    也就是说, 想要把某个文本垂直居中, 除了要获取View的高度, 还要获取文本的高度. 这里就需要Paint.FontMetrics类了, 里面有我们要的参数. 官方文档
    这里有两种思路, 依靠top和bottom算出文本高度, 或者依靠ascent和descent. 你对上面哪张图更理解, 就用哪个. 还有一点要说的就是, 所有参数都是相对于baseline的, 比方说top就可能是-100, bottom就会是30.

    float baseline = y - (fmi.top + fmi.bottom) / 2.0f;
    float baseline = y - (fmi.ascent + fmi.descent) / 2.0f;
    

    滚动选择器实现

    要想实现滚动选择器, 肯定还是要处理触摸操作的. 如果对自定义视图不熟悉的, 可以看看我之前的文章. 或者google一下. 要点就是抬手时候开启计时器, 点下记录位置, 移动重绘. 然后为了头尾衔接, 需要在到顶和到底的时候处理下List中的内容.

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN: {
                if (mTask != null) {
                    mTask.cancel();
                    mTask = null;
                }
                mLastDownY = event.getY();
            }
            break;
    
            case MotionEvent.ACTION_MOVE: {
                mMoveLen += (event.getY() - mLastDownY);
    
                if (mMoveLen > DIS * mMin / 2) {
                    tailToHead();
                    mMoveLen = mMoveLen - DIS * mMin;
                } else if (mMoveLen < -DIS * mMin / 2) {
                    headToTail();
                    mMoveLen = mMoveLen + DIS * mMin;
                }
    
                mLastDownY = event.getY();
                invalidate();
            }
            break;
    
            case MotionEvent.ACTION_UP: {
                // 移动过小就不移动
                if (Math.abs(mMoveLen) < 0.001) {
                    mMoveLen = 0;
                    break;
                }
                if (mTask != null) {
                    mTask.cancel();
                    mTask = null;
                }
    
                mTask = new MyTimerTask(mHandler);
                mTimer.schedule(mTask, 0, 10);
            }
            break;
        }
        return true;
    }
    
    private void headToTail() {
        String head = mData.get(0);
        mData.remove(0);
        mData.add(head);
    }
    
    private void tailToHead() {
        String tail = mData.get(mData.size() - 1);
        mData.remove(mData.size() - 1);
        mData.add(0, tail);
    }
    

    最后

    有段时间没写文章了, 也是忙一些乱七八糟的事情去了. 写得有点找不到感觉, 之后会努力写出有趣的分享文章来的. 喜欢可以点赞或者关注我哦~


    相关文章

      网友评论

        本文标题:界面无小事(四):来写个滚动选择器吧!

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