前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >界面无小事(四):来写个滚动选择器吧!

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

作者头像
sean_yang
发布2018-09-04 11:21:32
5760
发布2018-09-04 11:21:32
举报
文章被收录于专栏:Sorrower的专栏

效果图

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

效果图
效果图

效果图


前言

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


Paint类

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

代码语言:javascript
复制
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的.

变化曲线
变化曲线

变化曲线

代码语言:javascript
复制
// 依据曲线设置字号
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方法中处理. 代码如下:

代码语言:javascript
复制
mTask = new MyTimerTask(mHandler);
mTimer.schedule(mTask, 0, 10);
代码语言:javascript
复制
private class MyTimerTask extends TimerTask {
    Handler handler;

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

    @Override
    public void run() {
        handler.sendMessage(handler.obtainMessage());
    }
}
代码语言:javascript
复制
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

玄学图1

玄学图2
玄学图2

玄学图2

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

代码语言:javascript
复制
float baseline = y - (fmi.top + fmi.bottom) / 2.0f;
float baseline = y - (fmi.ascent + fmi.descent) / 2.0f;

滚动选择器实现

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

代码语言:javascript
复制
@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;
}
代码语言:javascript
复制
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);
}

最后

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


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.07.16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Paint类
  • 计时器
  • 基线baseline
  • 滚动选择器实现
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档