自定义View — 滑动验证码

人间烟火气,最抚凡人心。 — 网络

写在前面

2020大半年没有更新,2021年初我又来了,回顾一下2020年,年初立下的Flag,也算是完成了几个,1.多吃蔬菜水果;2.戒烟少喝酒;3.坚持读书;4.体重回到了大学毕业时的水平。还有没完成的Flag,1.当工作任务很多的时候,没有抱着学习的态度去工作,而是为了完成任务而工作,没有收获;2.没有坚持运动;3.偶尔还是不能调整好心态。当然还有其他的收获,1.驾照到手;2.买新房,开始还房贷的生活;3.向心仪的女孩子摊牌成功;4.报自考专升本。

现在进入正题,之前做过一个项目,有注册账号的功能,但是注册之前要通过滑块验证码,先来说说设计原理,云端会返回给我两张同高不同宽的图片,一张是有滑动块缺口的全部图片,一张是滑动块图片,我只需要横向滑动将横坐标传回给云端进行验证即可。这个设计思路清晰明了,下面来说一下我的实现思路,首先想到的就是自定义View,往自定义View中添加两个ImageView用来显示图片内容,其中显示滑动块图片的ImageView可以通过触摸事件进行横向移动,起始位置为最左边,通过调整显示滑动块图片的ImageView的左边距达到横向移动的效果。

具体实现

创建SliderView继承自FrameLayout,创建两个ImageView,通过计算手指移动的距离调整横向移动。

public class SliderView extends FrameLayout {

    // 背景图(固定的)
    private ImageView mInvariableView;
    // 滑动块(可移动)
    private ImageView mVariableView;

    // 触摸抬起事件监听器
    private OnTouchUpListener mOnTouchUpListener;

    // 滑动块的左边距
    private int mVariableLeftMargin;
    // 滑动块可以滑动的范围
    private int mVariableDistance;
    // 记下手指按下的位置
    private int mDownX;

    public SliderView(@NonNull Context context) {
        this(context, null);
    }

    public SliderView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SliderView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public SliderView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    /**
     * 初始化
     * @param context
     */
    private void init(Context context) {
        // 创建背景图控件
        mInvariableView = new ImageView(context);
        mInvariableView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        addView(mInvariableView);

        // 创建滑动块控件
        mVariableView = new ImageView(context);
        mVariableView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        addView(mVariableView);
    }

    /**
     * 移动滑动块
     * 原理:通过设置滑动块的左边距达到移动的效果
     * @param margin 左边距
     */
    private void move(int margin) {
        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mVariableView.getLayoutParams();
        marginLayoutParams.leftMargin = margin;
        mVariableView.setLayoutParams(marginLayoutParams);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 手下按下时,记录当前位置
                mDownX = (int) event.getX();
                // 同时记录滑动块的左边距
                mVariableLeftMargin = ((MarginLayoutParams) mVariableView.getLayoutParams()).leftMargin;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算手指移动的距离
                int distance = (int) (event.getX() - mDownX);
                // 如果左边距不为0,说明在此次ACTON_DOWN事件产生之前,滑动块已经不在初始位置,已经滑动过一次或多次,
                // 要将已产生的左边距加上,避免丢失上一次滑动的距离,导致滑动块从初始位置移动
                if (mVariableLeftMargin != 0) distance = mVariableLeftMargin + distance;
                // 避免手指移动的距离过大,滑动块超出背景图的范围,保证滑动块一直在背景图内移动
                if (distance < 0) distance = 0;
                else if (distance > mVariableDistance) distance = mVariableDistance;
                // 开始移动滑动块
                move(distance);
                break;
            case MotionEvent.ACTION_UP:
                if (mOnTouchUpListener == null)
                    throw new NullPointerException("You have to set the OnTouchUpListener!");
                mOnTouchUpListener.onTouchUp(mVariableView.getLeft());
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 重置滑动块位置
     */
    public void reset() {
        move(0);
    }

    /**
     * 设置图片
     * @param invariableBitmap 固定的背景图
     * @param variableBitmap 滑动块图片
     */
    public void setSliderBitmap(Bitmap invariableBitmap, Bitmap variableBitmap) {
        mInvariableView.setImageBitmap(invariableBitmap);
        mVariableView.setImageBitmap(variableBitmap);

        // 计算滑动块可以移动的范围
        mVariableDistance = invariableBitmap.getWidth() - variableBitmap.getWidth();
    }

    /**
     * 设置触摸抬起事件监听器
     * @param listener 触摸抬起事件监听器
     */
    public void setOnTouchUpListener(OnTouchUpListener listener) {
        mOnTouchUpListener = listener;
    }

    public interface OnTouchUpListener {
        void onTouchUp(int x);
    }
}

如何使用

下面通过一个Demo演示如何使用该自定义View。

1.创建布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <com.demo.slider.SliderView
            android:id="@+id/view_slider"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
2.创建Activity

新建Activity,重写onCreate函数,调用setContentView指定布局,初始化View并SliderView设置监听器和滑动块图片。

public class MainActivity extends AppCompatActivity implements SliderView.OnTouchUpListener {

    private SliderView mSliderView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
    }

    private void initView() {
        // 这里涉及到内部机密,就用两个本地图片代替了,放在drawable资源目录下
        Bitmap invariableBitmap = DrawableUtil.drawableToBitmap(getResources().getDrawable(R.drawable.invariable));
        Bitmap variableBitmap = DrawableUtil.drawableToBitmap(getResources().getDrawable(R.drawable.variable));

        mSliderView = findViewById(R.id.view_slider);
        mSliderView.setOnTouchUpListener(this);
        mSliderView.setSliderBitmap(invariableBitmap, variableBitmap);
    }

    @Override
    public void onTouchUp(int x) {
        // 在这个回调里将横坐标传回给云端,进行验证
        Log.d(MainActivity.class.getSimpleName(), "onTouchUp : x = " + x);
    }
}

运行效果如下:

初始位置.png
匹配位置.png

最后

开篇既然说了关于个人,那就再说说吧,毕竟要有始有终,我对2020年整体是满意的,2021年希望能把2020年没有完成的Flag完成,赚钱多多,成长多多。2020因为疫情注定不平凡,2021年因为疫情或许不能回家过年,希望疫情早日过去,大家都能够正常生活。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容