Android-来填写一个验证码吧!(二)

android

在开发中为了防止恶意破解、恶意提交等行为,所以我们在提交数据时,都会使用验证功能,去尽量区别人机。在Android应用中我们同样需要这一功能,去有效的避开恶意注册,恶意攻击。

上篇文章:Android-来填写一个验证码吧!(一)中,介绍了一种,数字英文随机生成的图片验证码,使用很简单,一行代码,调用一个工具类,就能完美的实现图片验证码的显示。

另外一种常见的,可以实现简单人机区别检测的方法,滑动验证条,效果如下:

滑动验证条

那么该如何实现呢?具体思路如下:
使用拓展自定义的SeekBar实现滑动条,根据触摸事件过滤错误操作,根据进度回调设置不同状态效果,再根据状态设置上不同的操作提示。

理清思路,接下来看下代码吧!

首先制作我们的拓展自定义SeekBar实现滑动条,然后根据dispatchTouchEvent进行事件的拦截,过滤非起始点的点击事件,避免SeekBar自带的点击跳转进度的效果,不然的话还叫什么滑动进度条。
这里的计算,是根据ValidationSeekBarLeft点坐标位为起始,根据thumb的大概大小计算出有效触摸范围,而其余的,通过return true;来进行拦截。

public class ValidationSeekBar extends AppCompatSeekBar {
    //验证条的宽度
    private int index = 0;
    //点击状态
    private boolean k = true;
    private int xDOWN;

    class measureRunnable
            implements Runnable {
        @Override
        public void run() {
            //重新测绘获取验证条宽度
            ValidationSeekBar.this.measure(0, 0);
            index = ValidationSeekBar.this.getLeft();
            Log.e("ValidationSeekBar", "index:" + index);
        }
    }

    public ValidationSeekBar(Context context) {
        super(context);
        this.post(new measureRunnable());
    }

    public ValidationSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.post(new measureRunnable());
    }

    public ValidationSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.post(new measureRunnable());
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();

        //过滤SeekBar的点击事件
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            xDOWN = (int) event.getX();
            k = true;
            if (xDOWN - index > 200) {
                k = false;
                return true;
            }
        }

        //过滤SeekBar的点击事件
        if (event.getAction() == MotionEvent.ACTION_UP) {
            k = true;
            if (xDOWN - index > 200) {
                k = false;
                return true;
            }
        }

        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            if (!k) {
                return true;
            }
        }
        return super.dispatchTouchEvent(event);
    }
}

以上,我的ValidationSeekBar就完成了。接下来去搭建我们的xml布局吧。以下是滑动条的布局:使用我们的ValidationSeekBar以及TextView组合出相关的提示效果。

 <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dp"
        android:layout_marginRight="25dp"
        android:layout_marginTop="30dp">

        <sakura.backdemo.ValidationSeekBar
            android:id="@+id/sb"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="0"
            android:progressDrawable="@drawable/seekbar_bg"
            android:thumb="@drawable/thumb"
            android:thumbOffset="-3px" />

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="请按住滑块,拖动到最右边"
            android:textColor="#888888"
            android:textSize="14dp" />

    </RelativeLayout>

来看一下整体的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@android:color/holo_red_light">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="设置登录密码"
            android:textColor="#fff"
            android:textSize="20sp" />
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="30dp"
        android:background="@drawable/bg"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:layout_marginRight="20dp"
                android:text="中国+86"
                android:textColor="#A2CD5A"
                android:textSize="16sp" />

            <View
                android:layout_width="0.1dp"
                android:layout_height="match_parent"
                android:background="#FF7F00" />

            <EditText
                android:id="@+id/et_forgetPass_PhoneNum"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:background="@null"
                android:digits="0123456789"
                android:hint="请填入您的手机号"
                android:inputType="number"
                android:maxLength="11"
                android:textSize="16sp" />
        </LinearLayout>
    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dp"
        android:layout_marginRight="25dp"
        android:layout_marginTop="30dp">

        <sakura.backdemo.ValidationSeekBar
            android:id="@+id/sb"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:max="100"
            android:progress="0"
            android:progressDrawable="@drawable/seekbar_bg"
            android:thumb="@drawable/thumb"
            android:thumbOffset="-3px" />

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="请按住滑块,拖动到最右边"
            android:textColor="#888888"
            android:textSize="14dp" />

    </RelativeLayout>

    <Button
        android:id="@+id/getCode"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="35dp"
        android:background="#ccc"
        android:text="获取验证码"
        android:textColor="#fff"
        android:textSize="18sp" />

</LinearLayout>

这样整个界面的布局就出来了,但是还不够,我们还希望为我们的ValidationSeekBar增加更多的状态反馈效果。那么我们需要制作一些progressDrawablethumb

材料一:默认状态下的progressDrawable

<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--seekBar背景-->
    <item android:id="@android:id/background">
        <!--形状-->
        <shape android:shape="rectangle">
            <!--大小-->
            <size android:height="42dp" />
            <!--圆角-->
            <corners android:radius="2dp" />
            <!--背景-->
            <solid android:color="#E7EAE9" />
            <!--边框-->
            <stroke
                android:width="1dp"
                android:color="#C3C5C4" />
        </shape>
    </item>
    <!--seekBar的进度条-->
    <item android:id="@android:id/progress">
        <clip>
            <shape xmlns:android="http://schemas.android.com/apk/res/android"
                android:innerRadius="120dp"
                android:shape="rectangle"
                android:thickness="1dp"
                android:useLevel="false">
                <solid android:color="#281991fa" />
                <stroke
                    android:width="1dp"
                    android:color="#1991FA" />
                <corners
                    android:bottomLeftRadius="2dp"
                    android:bottomRightRadius="2dp"
                    android:topLeftRadius="2dp"
                    android:topRightRadius="2dp" />
            </shape>
        </clip>
    </item>
</layer-list>

材料二:错误状态下的progressDrawable

<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--seekBar背景-->
    <item android:id="@android:id/background">
        <!--形状-->
        <shape android:shape="rectangle">
            <!--大小-->
            <size android:height="42dp" />
            <!--圆角-->
            <corners android:radius="2dp" />
            <!--背景-->
            <solid android:color="#E7EAE9" />
            <!--边框-->
            <stroke
                android:width="1dp"
                android:color="#C3C5C4" />
        </shape>
    </item>
    <!--seekBar的进度条-->
    <item android:id="@android:id/progress">
        <clip>
            <shape xmlns:android="http://schemas.android.com/apk/res/android"
                android:innerRadius="120dp"
                android:shape="rectangle"
                android:thickness="1dp"
                android:useLevel="false">
                <solid android:color="#28f57a7a" />
                <stroke
                    android:width="1dp"
                    android:color="#F57A7A" />
                <corners
                    android:bottomLeftRadius="2dp"
                    android:bottomRightRadius="2dp"
                    android:topLeftRadius="2dp"
                    android:topRightRadius="2dp" />
            </shape>
        </clip>
    </item>
</layer-list>

材料三:完成状态下的progressDrawable

<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!--seekBar背景-->
    <item android:id="@android:id/background">
        <!--形状-->
        <shape android:shape="rectangle">
            <!--大小-->
            <size android:height="42dp" />
            <!--圆角-->
            <corners android:radius="2dp" />
            <!--背景-->
            <solid android:color="#E7EAE9" />
            <!--边框-->
            <stroke
                android:width="1dp"
                android:color="#C3C5C4" />
        </shape>
    </item>
    <!--seekBar的进度条-->
    <item android:id="@android:id/progress">
        <clip>
            <shape xmlns:android="http://schemas.android.com/apk/res/android"
                android:innerRadius="120dp"
                android:shape="rectangle"
                android:thickness="1dp"
                android:useLevel="false">
                <solid android:color="#2829946b" />
                <stroke
                    android:width="1dp"
                    android:color="#29946b" />
                <corners
                    android:bottomLeftRadius="2dp"
                    android:bottomRightRadius="2dp"
                    android:topLeftRadius="2dp"
                    android:topRightRadius="2dp" />
            </shape>
        </clip>
    </item>
</layer-list>

材料四:各状态下的thumb

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 按下状态-->
    <item android:drawable="@drawable/dw" android:state_pressed="true" />

    <!-- 有焦点状态 -->
    <item android:drawable="@drawable/de" android:state_focused="true" />

    <!-- 普通状态 -->
    <item android:drawable="@drawable/de" />
</selector>

到这里,我们的界面以及材料都已经准备好了,接下里我们去Activity中看一下操作流程吧

public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {

    private TextView tv;
    private ValidationSeekBar seekBar;
    Handler handler = new Handler();
    private Button getCode;


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

        getCode = (Button) findViewById(R.id.getCode);
        tv = (TextView) findViewById(R.id.tv);
        seekBar = (ValidationSeekBar) findViewById(R.id.sb);
        seekBar.setOnSeekBarChangeListener(this);

    }

    /**
     * seekBar进度变化时回调
     *
     * @param seekBar
     * @param progress
     * @param fromUser
     */
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (seekBar.getProgress() == seekBar.getMax()) {
        } else {
            tv.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * seekBar开始触摸时回调
     *
     * @param seekBar
     */
    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    /**
     * seekBar停止触摸时回调
     *
     * @param seekBar
     */
    @Override
    public void onStopTrackingTouch(final SeekBar seekBar) {

        //验证错误时的滑动条样式
        if (seekBar.getProgress() != seekBar.getMax()) {
            seekBar.setProgressDrawable(getResources().getDrawable(R.drawable.seekbar_error_bg));
            seekBar.setThumb(getResources().getDrawable(R.drawable.er));
            seekBar.setThumbOffset(0);
            int progress = seekBar.getProgress();
            seekBar.setProgress(0);
            seekBar.setProgress(progress);
            //错误状态延时警示
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    //错误回退动画效果
                    runFloat(seekBar.getProgress());
                }
            }, 400);
        }

        //设置验证成功的滑动条样式
        if (seekBar.getProgress() == seekBar.getMax()) {
            seekBar.setThumb(getResources().getDrawable(R.drawable.gd));
            seekBar.setProgressDrawable(getResources().getDrawable(R.drawable.seekbar_good_bg));
            seekBar.setProgress(0);
            seekBar.setThumbOffset(-3);
            seekBar.setProgress(100);
            //成功时的提示信息
            tv.setVisibility(View.VISIBLE);
            tv.setTextColor(Color.rgb(0x29, 0x94, 0x6b));
            tv.setText("完成验证");
            //按钮状态
            getCode.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
        }

    }

    private void runFloat(int Progress) {
        //使用ValueAnimator进行进度的偏移。
        ValueAnimator valueAnimator = ValueAnimator.ofInt(Progress, 0);
        //动画时间,可根据需要调整
        valueAnimator.setDuration(200);
        //动画更新监听
        valueAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        //设置进度
                        seekBar.setProgress(Integer.parseInt(valueAnimator
                                .getAnimatedValue().toString()));
                    }
                });
        //动画状态监听
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {
                //重置默认的滑动条样式
                seekBar.setThumb(getResources().getDrawable(R.drawable.thumb));
                seekBar.setThumbOffset(-1);
                seekBar.setProgressDrawable(getResources().getDrawable(R.drawable.seekbar_bg));
                //重置提示信息
                tv.setVisibility(View.VISIBLE);
                tv.setTextColor(Color.GRAY);
                tv.setText("请按住滑块,拖动到最右边");
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        //开启valueAnimator
        valueAnimator.start();
    }

}

至此,我们的整个滑动验证码的制作流程就结束了,以上代码基本实现文章开头效果图以及常规的滑动验证码的需求逻辑,整体代码逻辑还是比较清晰了然的,使用起来也很方便。

当然一定还有更多方法和更优化的逻辑,还请大家提出,共同完善,
有了需求才有了功能,有了想法才有了创作,你的反馈会是使我进步的最大动力。

觉得还不够方便?还想要什么功能?告诉我!欢迎反馈,欢迎Star。

我的其他文章推荐:
Android-来填写一个验证码吧!(一)
Android-返回桌面?退出程序?
一句话搞定高仿ios底部弹出提示框。
一句话搞定的Android底部导航栏,一键绑定片段,ViewPager。

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

推荐阅读更多精彩内容