当前位置: 首页 > news >正文

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。

相关文章:

  • webpack的使用
  • 最详细的Log4j使用教程
  • Javascript 对象 - 数学对象
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 都是用 DllImport?有没有考虑过自己写一个 extern 方法?
  • python实现将json数据以json格式写入txt文件
  • 【笔记】Nginx热更新相关知识
  • 简单读!spring-mvc源码之穿越http请求
  • C++与Rust操作裸指针的比较
  • 团队项目的NABCD的分析
  • .net core 依赖注入的基本用发
  • win10驱动下获取cpu信息
  • 闭包--闭包之tab栏切换(四)
  • 单据类报表的制作
  • Spring Boot(1)
  • 自己简单写的 事件订阅机制
  • 【comparator, comparable】小总结
  • extract-text-webpack-plugin用法
  • Java|序列化异常StreamCorruptedException的解决方法
  • JavaScript HTML DOM
  • Java多态
  • PAT A1092
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • 初探 Vue 生命周期和钩子函数
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 聊聊directory traversal attack
  • 如何在GitHub上创建个人博客
  • 深入浅出Node.js
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • MyCAT水平分库
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • (3)STL算法之搜索
  • (arch)linux 转换文件编码格式
  • (BFS)hdoj2377-Bus Pass
  • (C语言)fgets与fputs函数详解
  • (C语言)球球大作战
  • (二十四)Flask之flask-session组件
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET HttpWebRequest、WebClient、HttpClient
  • .Net中的集合
  • 。Net下Windows服务程序开发疑惑
  • /ThinkPHP/Library/Think/Storage/Driver/File.class.php  LINE: 48
  • @autowired注解作用_Spring Boot进阶教程——注解大全(建议收藏!)
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @media screen 针对不同移动设备
  • [100天算法】-目标和(day 79)
  • [2023-年度总结]凡是过往,皆为序章
  • [Angularjs]asp.net mvc+angularjs+web api单页应用之CRUD操作
  • [AutoSar]BSW_OS 02 Autosar OS_STACK
  • [C++]Leetcode17电话号码的字母组合
  • [CUDA手搓]从零开始用C++ CUDA搭建一个卷积神经网络(LeNet),了解神经网络各个层背后算法原理