SeekBar 滑动验证效果

一、说明

  笔记主要是记录一些本人在开发当中的学习和使用笔记。笔记内容包含一些本人觉得重要的知识点、本人易犯的错误等。
  由于本人水平有限,其中出现的错误或者不合理的地方望各位读者多多包含,并指出其中不合理和错误的地方,以便我来修改正。谢谢!

二、笔记时间

  2019年06月27日:首次编辑
  2019年10月23日:第一次修改

三、简述

  本文主要讲述如何把 SeekBar 变成滑动验证的控件。

四、详情

  由于工作和生活的原因,很长一段时间没有写博客了,最近稍闲一点点,决定把这段时间工作中遇到的一些问题和解决方案记录下来。供自己和有需要的朋友借鉴。

  SeekBar 是我们工作当中经常使用到的控件,SeekBar 在画板中可以用他来调节画笔、画布的大小;可以用来调节图片颜色、显示下载进度;还可以制作滑动验证条等等。总的来说有很多场景我们都可以用SeekBar来实现。今天主要讲我的用 SeekBar 做滑动验证条的实现方式。

1、自定义 SeekBar 样式

  要实现美工小姐姐设计图的效果,以便与美工小姐姐产生共鸣,我们无法避免对SeekBar的样子进行自定义。下面介绍几个 SeekBar 的常用属性:

  • android:thumb //滑块样式
  • android:progressDrawable //进度条样式
  • android:max //拖动条的最大值
  • android:min //拖动条的最小值
  • android:progress //当前的进度值
  • android:thumbOffset //滑块偏移量
  • android:splitTrack //是否拆分绘制(滑块透明区域透明)
  • android:paddingStart //盒子内左边距
  • android:paddingEnd //盒子内右边距

  上面对几个重要属性进行了简短说明,下面一一说明怎么通过设置以上属性实现滑动验证效果。

1.1 android:thumb

  设置 thumb 可以实现自定义滑块样式。我们可以使用美工提供的图片,也可以用 shape 等来自定义滑块样式。我这里为了在不同机型上的通用性,是使用 shape 来自定义的样式,如下:

<!-- seekbar_thumb.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <size
        android:width="11dp"
        android:height="11dp" />
    <corners android:radius="3dp" />

    <gradient
        android:endColor="#FF44A3FF"
        android:startColor="#FF9CDCFF" />

</shape>

  样式效果是四角为圆角、颜色渐变、固定大小的正方形滑块。效果如下图:


thumb

1.2 android:progressDrawable

  设置 progressDrawable 可以实现自定义进度条样式。和 thumb 不一样的是,我们可以使用美工提供的图片,但是还是要用 layer-list 来进行组合,因为进度条有选中未选择等状态;当然也可以使用 shape 自定义。同样我这里也是使用 shape 自定义的,具体代码如下:

<!-- seekbar_progress.xml -->
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@android:id/background"
        android:height="11dp"
        android:gravity="center_vertical">
        <shape>
            <corners android:radius="3dp" />
            <solid android:color="@android:color/transparent" />
        </shape>
    </item>

    <item
        android:id="@android:id/secondaryProgress"
        android:height="11dp"
        android:gravity="center_vertical">
        <clip>
            <shape>
                <corners android:radius="3dp" />
                <solid android:color="#FF9CDCFF" />
            </shape>
        </clip>
    </item>

    <item
        android:id="@android:id/progress"
        android:height="11dp"
        android:gravity="center_vertical">
        <clip>
            <shape>
                <corners android:radius="3dp" />
                <solid android:color="#FF9CDCFF" />
            </shape>
        </clip>
    </item>
</layer-list>

  先简要说明一下我自定义的效果,我实现的效果是选中状态为:未选中(滑块右侧)透明,选中和过渡区域为固定色(滑块渐变的开始颜色)。效果如下图:


progressDrawable

  细心的同学回发现里面有三个 @android 的 ID,那个这几个 ID到底是做什么用的勒,下面对他们说明一下。

  • @android:id/background //这个不难理解,从命名上我们就可以看出来,他是设置进度条的背景
  • @android:id/secondaryProgress //这个咋一看难以从命名上直接看出来,其实他是第二进度选中样式,和 android:secondaryProgress 一起看就不难理解了。
  • @android:id/progress //这个自然就是第一进度选中样式了。

1.3 android:max、android:min

  这两个属性就不需要多讲了,相信大家都明白,他就是设置进度条的最大、最小进度值。

1.4 android:progress

  这个属性应该大家也都明白,就是设置当前选中的进度值。当然还有 android:secondaryProgress 这个属性,这个属性就是设置第二进度的角度值。

1.5 android:thumbOffset

  这个属性从命名可以看出是设置滑块的偏移量,但是设置不同的值是什么效果勒?下面我们来看看设置为10dp、0dp、-10dp 的效果:

  • android:thumbOffset="10dp" //向左偏移
  • android:thumbOffset="0dp" //向右偏移一个滑块宽度
  • android:thumbOffset="-10dp" //向右偏移
10dp

0dp

-10dp

1.6 android:splitTrack

  这个属性还是很有用的,尤其是自定义滑块的时候。我自定义的滑块是圆角,当我没设置这个属性的时候(默认 android:splitTrack="true"),发现滑动的时候圆角处不是透明的,而是有一个白色的角,这明显不是我们想要的效果。
  出现这种不透明的现象我们就可以设置 android:splitTrack="false" ,这样就是透明的了。


splitTrack="true"

splitTrack="false"

1.6 android:paddingStart、android:paddingEnd

  我们知道固定 SeekBar 宽高之后,滑块左右都默认会偏移半个滑块的宽度,以便滑动到最左和最右的时候能够完整的显示滑块。其实这个偏移位置我们是可以自己设定的,就是通过 paddingStart、paddingEnd 来设置两边的偏移。当然 paddingLeft、paddingRight 也可以达到一样的效果。

1.7 滑动验证最终配置

  虽然上面介绍了那么多属性,不过这里实现滑动验证效果,我并没有全部使用,最终的配置如下:

  <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="50dp"
            android:layout_height="11dp"
            android:paddingStart="6dp"
            android:paddingEnd="6dp"
            android:progressDrawable="@drawable/seekbar_progress"
            android:splitTrack="false"
            android:thumb="@drawable/seekbar_thumb" />

  以上配置的效果如下图:


最终显示效果

2、滑动验证功能实现

  第一大点主要是自定义滑动条的样式,也就是实现我们美工小姐姐的奇思异想。剩下的滑动验证功能就需要我们这些攻城狮一句一句代码敲出来了!下面我介绍一下我的实现吧。

2.1 滑动停止回到初始位置

  这并不是什么复杂的功能,我们快速过。只需要给 SeekBar 设置 setOnSeekBarChangeListener 监听,然后实现监听,在监听中的 onStopTrackingTouch 方法里出现设置 progres 为 0 就好了。还是贴一下代码,如下:

    SeekBar seekBar = findViewById(R.id.seekbar);
    seekBar.setOnSeekBarChangeListener(this);

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        seekBar.setProgress(0);
    }

2.1 屏蔽滑动条滑块外的点击事件

  原本以为做完以上工作,滑动验证就算完成了。可偏偏又出现了另外一个问题,说好的滑动验证,居然点击滑块的其它位置滑块也会设置过去。不过这是 SeekBar 的基本功能,我们不能说啥,现在我们看看怎么屏蔽滑块外的点击事件。我自定义了 SeekBar ,对触摸事件进行了处理,代码如下:

public class VerificationSeekBar extends AppCompatSeekBar {
    private boolean isInterception = true;

    public VerificationSeekBar(Context context) {
        super(context);
    }

    public VerificationSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerificationSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            isInterception = true;
            if (event.getX() > getThumb().getMinimumWidth()) {
                isInterception = false;
                return true;
            }
        }

        if (event.getAction() == MotionEvent.ACTION_MOVE && !isInterception) {
            return true;
        }

        return super.dispatchTouchEvent(event);
    }
}

  上面代码的逻辑也很简单,就是判断点击位置是否在滑块外,在滑块外就屏蔽啥也不做,但是消费掉滑动事件。
  现在替换 SeekBar 为我们自定义的 VerificationSeekBar ,就能够达到滑动验证的效果了。

2.2 解决点击滑动条尾部直接验证成功的问题

  我在做画板的时候,利用该方式来做滑动清屏,结果发现了一个隐藏bug。我们程序员往往最怕这种隐藏bug了,但是怕归怕,问题我们还是要解决了。当然了,这个bug并不是很难解。
  下面说一下这个具体的bug:在白板当中,我设置的是滑动到90%以上,松手即触发清屏。结果当我们点击滑动条末尾位置时,同样触发了清屏。说好的滑动清屏,可是实际使用结果却可以点击清屏,这很明显和我们的设计方案不符合。因此我做了以下小修改,来完善该功能。
  我把该问题记录下来,一个是给自己做笔记,另外一个我相信滑动验证也会有类似的问题,希望读者能够直接解决好问题。以下就是我的解决方案。

2.2.1 把 isInterception 的值提供出来

  把 isInterception 的值提供出来其实就是在 VerificationSeekBar 中添加一个获取该值的方法,具体如下:

// VerificationSeekBar.java 
/**
     * 获取是否滑动清屏
     * 
     * @return true:滑动验证    false:非滑动验证
     */
    public boolean isInterception() {
        return isInterception;
    }

2.2.2 判断是否是滑动验证

  这个就需要我们在实现SeekBar的监听事件时,利用 isInterception 方法来判断是否触发验证成功。具体如下:

    VerificationSeekBar mySeekBar;
    mySeekBar = findViewById(R.id.seekbar);
    mySeekBar.setOnSeekBarChangeListener(this);

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
      if (mySeekBar.isInterception()) {
            succeed();  //滑动验证成功
        } else {
            failed();  //滑动验证失败
        }
        seekBar.setProgress(0);
    }

2.2.3 其它解决方案

  我以上的解决方案并不是唯一的解决方案。我再提供两种解决思路,因为项目时间紧张我就没一一验证了。读者可以验证一下,把验证结果加到评论区,让所有读者共勉。

  • 第一种解决方案和我上面提供的解决方案一样,只是我们可以把 setOnSeekBarChangeListener 监听的逻辑在 VerificationSeekBar 中实现,并向外提供一个 succeed、failed 接口,来保证封装的完整性。本人推荐该方法。

  • 第二中解决方案,我们可以利用 onStartTrackingTouch ,通过开始点击的值(位置)来判断是否是滑动验证,非点击滑动条末尾,但是要增加变量,不建议使用。用我画板90%触发清屏为例,我们可以修改为如下:

    int startProgress = 0;

    SeekBar seekBar = findViewById(R.id.seekbar);
    seekBar.setOnSeekBarChangeListener(this);

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        startProgress = seekBar.getProgress();
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        if (startProgress < 90 && seekBar.getProgress() >= 90) {
            succeed();  //滑动验证成功
        } else {
            failed();  //滑动验证失败
        }
        seekBar.setProgress(0);
    }

注:第二中方案要着重验证,从滑动条中间拖到满足条件,是否会被判断为 succeed?若满足验证成功,建议和第一种犯案一样封装一下;若验证失败,建议把 <90 的条件改为,<= getThumb().getMinimumWidth()

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