Android 自定义仿SeekBar滑动验证
最近在项目中遇到一个需要滑动验证的功能,样式如下:
在未滑动时滑块为箭头形式,滑动到末端后变为对勾样式,滑动过的路径变为红色,遇到问题当然是先百度一番啦,没有对象,只能面向百度编程了,结果弄了半天都没找到合适的,大部分都是通过SeekBar来做的,但动态设置thumb的时候滑到首尾上面的thumb总是被覆盖掉一半,然后找了半天也没得到解决,没想到这东西资料这么少,没办法,只能自己来了,先看一下效果:
效果大概就是这个样子,不是很完美,勉强够用,没滑到底松开会自动回到起点,
下面来说一下具体实现:
1. 自定义VerifiSeekBar(就这个控件)继承自View。
2.画背景灰色圆角矩形,画上方红色圆角矩形,画初始状态滑块(带箭头),画结束状态滑块。
3.TouchEvent监听手势,主要是X轴位置,根据屏幕上的坐标动态画红色矩形和滑块
4.监听滑动距离是否到最后,到最后后画完成状态滑块(对勾)
思路就是这个样子咯,思路对了的话,代码就很好写了,下面贴一下代码,写得不是很好,有啥不对的地方欢迎大家指教。
```
public class VerifiSeekBar extends View {
private Contextcontext;
private Paintpaint;
private int movex;
private RectFbgRect;
private RectFupRect;
private BitmapinBitmap;
private BitmapoutBitmap;
private int bitmapWidth;
private int bitmapHeight;
//防止多次调用
private boolean isNewdown =true;
private OnSeekbarCompleListenneronSeekbarCompleListenner;
public VerifiSeekBar(Context context) {
super(context);
this.context = context;
initView();
}
public VerifiSeekBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
initView();
}
public VerifiSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView();
}
public void setProgress(int progress) {
movex = progress;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(ContextCompat.getColor(context, R.color.seekbar_notclick));
bgRect.set(0, 0, getWidth(), bitmapHeight + SizeUtils.dp2px(6));
canvas.drawRoundRect(bgRect, SizeUtils.dp2px(5), SizeUtils.dp2px(5), paint);
if (movex > getWidth()) {
movex = getWidth();
}
if (movex <=0) {
movex =0;
}
upRect.set(0, 0, movex, bitmapHeight + SizeUtils.dp2px(6));
paint.setColor(ContextCompat.getColor(context, R.color.red));
canvas.drawRoundRect(upRect, SizeUtils.dp2px(5), SizeUtils.dp2px(5), paint);
if (movex == getWidth()) {
getParent().requestDisallowInterceptTouchEvent(false);
canvas.drawBitmap(outBitmap, movex -bitmapWidth <0 ?
SizeUtils.dp2px(3) :movex - (bitmapWidth + SizeUtils.dp2px(3)),
SizeUtils.dp2px(3), paint);
if (onSeekbarCompleListenner !=null) {
if (isNewdown) {
onSeekbarCompleListenner.comple();
isNewdown =false;
}
}
}else {
canvas.drawBitmap(inBitmap, movex -bitmapWidth <0 ? SizeUtils.dp2px(3) :movex -bitmapWidth, SizeUtils.dp2px(3), paint);
if (onSeekbarCompleListenner !=null) {
onSeekbarCompleListenner.uncomple();
}
}
}
private void initView() {
paint =new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(ContextCompat.getColor(context, R.color.red));
paint.setStrokeWidth(2);
bgRect =new RectF();
upRect =new RectF();
inBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.login_move);
outBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.login_ok);
bitmapWidth =inBitmap.getWidth();
bitmapHeight =inBitmap.getHeight();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isNewdown =true;
movex =bitmapWidth + SizeUtils.dp2px(3);
case MotionEvent.ACTION_MOVE:
movex = (int) event.getX();
invalidate();
break;
case MotionEvent.ACTION_UP:
if (movex < getWidth()) {
movex =0;
}
invalidate();
break;
default:
break;
}
return true;
}
public void setOnSeekbarCompleListenner(OnSeekbarCompleListenner onSeekbarCompleListenner) {
this.onSeekbarCompleListenner = onSeekbarCompleListenner;
}
public interface OnSeekbarCompleListenner {
void comple();
void uncomple();
}
}
```
里面用到的工具类:
```
public final class SizeUtils {
private SizeUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* Value of dp to value of px.
*
* @param dpValue The value of dp.
* @return value of px
*/
public static int dp2px(final float dpValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (dpValue * scale +0.5f);
}
/**
* Value of px to value of dp.
*
* @param pxValue The value of px.
* @return value of dp
*/
public static int px2dp(final float pxValue) {
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int) (pxValue / scale +0.5f);
}
/**
* Value of sp to value of px.
*
* @param spValue The value of sp.
* @return value of px
*/
public static int sp2px(final float spValue) {
final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale +0.5f);
}
/**
* Value of px to value of sp.
*
* @param pxValue The value of px.
* @return value of sp
*/
public static int px2sp(final float pxValue) {
final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale +0.5f);
}
/**
* Converts an unpacked complex data value holding a dimension to its final floating
* point value. The two parameters <var>unit</var> and <var>value
* are as in {@link TypedValue#TYPE_DIMENSION}.
*
* @param value The value to apply the unit to.
* @param unit The unit to convert from.
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
public static float applyDimension(final float value, final int unit) {
DisplayMetrics metrics = Utils.getApp().getResources().getDisplayMetrics();
switch (unit) {
case TypedValue.COMPLEX_UNIT_PX:
return value;
case TypedValue.COMPLEX_UNIT_DIP:
return value * metrics.density;
case TypedValue.COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case TypedValue.COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f /72);
case TypedValue.COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case TypedValue.COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f /25.4f);
}
return 0;
}
/**
* Force get the size of view.
* <p>e.g.
*
* SizeUtils.forceGetViewSize(view, new SizeUtils.onGetSizeListener() {
* Override
* public void onGetSize(final View view) {
* view.getWidth();
* }
* });
*
*
* @param view The view.
* @param listener The get size listener.
*/
public static void forceGetViewSize(final View view, final onGetSizeListener listener) {
view.post(new Runnable() {
@Override
public void run() {
if (listener !=null) {
listener.onGetSize(view);
}
}
});
}
/**
* Return the width of view.
*
* @param view The view.
* @return the width of view
*/
public static int getMeasuredWidth(final View view) {
return measureView(view)[0];
}
/**
* Return the height of view.
*
* @param view The view.
* @return the height of view
*/
public static int getMeasuredHeight(final View view) {
return measureView(view)[1];
}
/**
* Measure the view.
*
* @param view The view.
* @return arr[0]: view's width, arr[1]: view's height
*/
public static int[]measureView(final View view) {
ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp ==null) {
lp =new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
}
int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int lpHeight = lp.height;
int heightSpec;
if (lpHeight >0) {
heightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);
}else {
heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
}
view.measure(widthSpec, heightSpec);
return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()};
}
///////////////////////////////////////////////////////////////////////////
// interface
///////////////////////////////////////////////////////////////////////////
public interface onGetSizeListener {
void onGetSize(View view);
}
}
```
这就是整个的代码啦,代码不多,有需要的直接拷过去用就行了,替换一下图片,颜色,也可以做一下优化。