项目GitHub
前言
目前App上有很多对于按钮误操作的控制。比如点击按钮后弹出确认框,但是这样的模式略显死板。为了给App赋予更多的生命力,可以借鉴网站登录滑动确认的方式。这种方式目前更多地用于web登录。以下是某网站登录时使用的滑动验证,用来取代以往的验证码模式。
我们的App可以借鉴上述的模式自定义一个滑动确认的控件,可以用于控制误操作点击的场景。当然其他的应用场景可以等待大家细细挖掘。
设计思路
我们就暂且命名这个自定义的滑动控件叫SlideView。
先来总结一下SlideView的主要功能:“按住一个进度条里的按钮往右滑,如果滑到一般松开按钮自动回到原位,如果滑到底则给出完成提示”。
貌似就是这么一句话的事。当然还有更多属性设置,比如背景的文字、颜色、滑动按钮和进度条的比例等等。
通过前面一句话的介绍,是不是让我们想起了Andoird的SeekBar控件?确实重写SeekBar控件的确可以实现我们想要实现的功能,但可定制稍微差了些,所以决定重头开始构建SlideView。
方案
既然要重构构建SlideView,那我们就要实现一个自定义的ViewGroup。添加背景图和提示文字。之后再将可以拖动的按钮加入到这个ViewGroup中。那个所谓“可以拖动的按钮”我们就叫它SlideIcon,这是一个自定义View。也可以添加背景图和提示文字,控制它的宽度与SildeView总宽度的比例,最后为这个View加上触摸事件,按下之后可以拖动,拖动到一般松开回到起点,拖到底触发一个完成的回调。
总体方案就是这样,是不是很简单,下面让我们来一步步实现这个SlideView。
可拖动的部分 - SlideIcon
SlideIcon是一个自定义View,它的主要功能就是拖动。其中我们需要做的工作跟就是测量尺寸、添加触摸事件、绘制背景图和文字。
具体代码如下:
/** * 可拖动的View */ private class SlideIcon extends View { // 用来控制触摸事件是否可用 private boolean mEnable; // 提示文字的Paint private Paint mTextPaint = null; // 提示文字的字体测量类 private Paint.FontMetrics mFontMetrics; // 回调 private MotionListener listener = null; // 手指按下时SlideIcon的X坐标 private float mDownX = 0; // SlideIcon在非拖动状态下的X坐标 private float mX = 0; // SliedIcon在拖动状态下X轴的偏移量 private float mDistanceX = 0; public SlideIcon(Context context) { this(context, null); } public SlideIcon(Context context, AttributeSet attrs) { super(context, attrs); init(); } public void setListener(MotionListener listener) { this.listener = listener; } public void setEnable(boolean enable) { this.mEnable = enable; } public boolean getEnable() { return mEnable; } private void init() { // 设置文字Paint mTextPaint = new Paint(); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setColor(mIconTextColor); mTextPaint.setTextSize(mIconTextSize); // 获取字体测量类 mFontMetrics = mTextPaint.getFontMetrics(); // 设置背景图 setBackgroundResource(mIconResId); // 设置触摸事件可用 mEnable = true; } /** * 重置SlideIcon */ public void resetIcon() { mDownX = 0; mDistanceX = 0; mX = 0; mEnable = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 宽度和宽Mode int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); // 高度和高Mode int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); switch (heightMode) { case MeasureSpec.AT_MOST: // layout_height为"wrap_content"时显示最小高度 setMeasuredDimension(MeasureSpec.makeMeasureSpec((int)(widthSize * mIconRatio), widthMode), MeasureSpec.makeMeasureSpec(mMinHeight, heightMode)); break; default: // layout_height为"match_parent"或指定具体高度时显示默认高度 setMeasuredDimension(MeasureSpec.makeMeasureSpec((int)(widthSize * mIconRatio