android滑动拼图验证码控件


滑动拼图验证码一般我们在网站上可以经常见到,本篇文章给大家介绍该效果在android端的是如何实现

首先看一下效果图:


* 新建自定义view DragImageView主要方法:

* 设置图片、滑块资源,设置图片、滑块之间的比例大小

* 滑块滑到正确位置,拼图成功调用ok()方法

* 滑块滑动位置错误,拼图失败调用fail()方法

* 重新滑动,reset()方法

* 拼图成功后,图片一道闪亮效果

* 整个类代码如下:

package com.yolanda.code.library.widget;

import android.animation.ValueAnimator;

import android.content.Context;

import android.graphics.Bitmap;

import android.os.Handler;

import android.util.AttributeSet;

import android.view.View;

import android.view.ViewGroup;

import android.view.animation.AlphaAnimation;

import android.view.animation.Animation;

import android.view.animation.LinearInterpolator;

import android.view.animation.TranslateAnimation;

import android.widget.FrameLayout;

import android.widget.ImageView;

import android.widget.SeekBar;

import android.widget.TextView;

import com.yolanda.code.library.R;

import com.yolanda.code.library.view.DiyStyleTextView;

/**

* @author Created by Yolanda on 2018/10/31.

* @description 滑动拼图验证码

*/

public final class DragImageView extends FrameLayout implements SeekBar.OnSeekBarChangeListener {

    private final int showTipsTime = 1500;

    private final int animeTime = 333;

    private final int flashTime = 800;

    private ImageView ivCover;

    private ImageView ivBlock;

    private SeekBar sb;

    private TextView tvTips2;

    private DiyStyleTextView tvTips;

    private View vFlash, flContent;

    private Handler handler = new Handler();

    private Bitmap cover, block, completeCover;

    private boolean isNormal;

    public DragImageView(Context context) {

        super(context);

        init();

    }

    public DragImageView(Context context, AttributeSet attrs) {

        super(context, attrs);

        init();

    }

    public DragImageView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        init();

    }

    private void init() {

        View.inflate(getContext(), R.layout.drag_view, this);

        flContent = findViewById(R.id.drag_fl_content);

        ivCover = findViewById(R.id.drag_iv_cover);

        ivBlock = findViewById(R.id.drag_iv_block);

        tvTips = findViewById(R.id.drag_tv_tips);

        tvTips2 = findViewById(R.id.drag_tv_tips2);

        vFlash = findViewById(R.id.drag_v_flash);

        tvTips.setColorRegex("拼图|成功|失败|正确|[\\d\\.%]+", 0xfff75151);

        sb = findViewById(R.id.drag_sb);

        sb.setMax(getContext().getResources().getDisplayMetrics().widthPixels);

        sb.setOnSeekBarChangeListener(this);

        reset();

    }

    /**

    * 设置资源

    *

    * @param cover        拼图

    * @param block        滑块

    * @param completeCover 完成的拼图

    * @param block_y      滑块Y值比例

    */

    public void setUp(Bitmap cover, Bitmap block, Bitmap completeCover, float block_y) {

        this.cover = cover;

        this.block = block;

        this.completeCover = completeCover;

        ivCover.setImageBitmap(completeCover);

        ivBlock.setImageBitmap(block);

        setLocation(1f * cover.getWidth() / cover.getHeight(), 1f * block.getHeight() / cover.getHeight(), block_y);

    }

    /**

    * 设置比例大小

    *

    * @param cover_wph  图片bili

    * @param block_size 滑块大小占高比

    * @param block_y    滑块位置占高比

    */

    private void setLocation(final float cover_wph, final float block_size, final float block_y) {

        post(new Runnable() {

            @Override

            public void run() {

                final int w = flContent.getMeasuredWidth();

                int h = (int) (w / cover_wph);

                ViewGroup.LayoutParams l = flContent.getLayoutParams();

                l.width = w;

                l.height = h;

                flContent.setLayoutParams(l);

                ViewGroup.MarginLayoutParams l2 = (MarginLayoutParams) ivBlock.getLayoutParams();

                l2.height = (int) (h * block_size);

                l2.width = l2.height * block.getWidth() / block.getHeight();

                l2.topMargin = (int) (h * block_y);

                ivBlock.setLayoutParams(l2);

            }

        });

    }

    public void ok() {

        ivCover.setImageBitmap(completeCover);

        blockHideAnime();

        int penset = (int) (99 - (timeUse > 1 ? timeUse - 1 : 0) / 0.1f);

        if (penset < 1) penset = 1;

        tvTips.setText(String.format("拼图成功: 耗时%.1f秒,打败了%d%%的用户!", timeUse, penset));

        tipsShowAnime(true);

        flashShowAnime();

        sb.setEnabled(false);

    }

    public void fail() {

        twinkleImage(ivBlock);

        tvTips.setText("拼图失败: 请重新拖曳滑块到正确的位置!");

        tipsShowAnime(true);

        handler.postDelayed(resetRun, showTipsTime);

        sb.setEnabled(false);

    }

    public void reset() {

        final int position = sb.getProgress();

        if (position != 0) {

            ValueAnimator animator = ValueAnimator.ofFloat(1f, 0);

            animator.setDuration(animeTime).start();

            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    float f = (Float) animation.getAnimatedValue();

                    sb.setProgress((int) (position * f));

                }

            });

        }

        tipsShowAnime(false);

        tips2ShowAnime(true);

        sb.setEnabled(true);

        ivBlock.setVisibility(GONE);

        vFlash.setVisibility(GONE);

        ivCover.setImageBitmap(completeCover);

        isNormal = true;

    }

    //===================seekbar监听===================

    private long timeTemp;

    private float timeUse;

    @Override

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

        int cw = ivCover.getMeasuredWidth();

        int bw = ivBlock.getMeasuredWidth();

        ViewGroup.MarginLayoutParams l = (MarginLayoutParams) ivBlock.getLayoutParams();

        l.leftMargin = (cw - bw) * progress / seekBar.getMax();

        ivBlock.setLayoutParams(l);

    }

    @Override

    public void onStartTrackingTouch(SeekBar seekBar) {

        ivBlock.setVisibility(VISIBLE);

        ivCover.setImageBitmap(cover);

        tips2ShowAnime(false);

        timeTemp = System.currentTimeMillis();

        isNormal = false;

    }

    @Override

    public void onStopTrackingTouch(SeekBar seekBar) {

        timeUse = (System.currentTimeMillis() - timeTemp) / 1000.f;

        if (dragListenner != null)

            dragListenner.onDrag(seekBar.getProgress() * 1f / seekBar.getMax());

    }

    //===================seekbar监听===================

    //闪烁滑块

    private void twinkleImage(final View view) {

        ValueAnimator animator = ValueAnimator.ofFloat(0, 1.0F);

        animator.setTarget(view);

        animator.setInterpolator(new LinearInterpolator());

        animator.setDuration(showTipsTime).start();

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override

            public void onAnimationUpdate(ValueAnimator animation) {

                float f = (Float) animation.getAnimatedValue();

                int time = (int) (showTipsTime * f);

                if (time < 125)

                    view.setVisibility(INVISIBLE);

                else if (time < 250)

                    view.setVisibility(VISIBLE);

                else if (time < 375)

                    view.setVisibility(INVISIBLE);

                else

                    view.setVisibility(VISIBLE);

            }

        });

    }

    //提示文本显示隐藏

    private void tipsShowAnime(boolean isShow) {

        if ((tvTips.getVisibility() == VISIBLE) == isShow)

            return;

        TranslateAnimation translateAnimation = new TranslateAnimation(

                Animation.RELATIVE_TO_SELF, 0f,

                Animation.RELATIVE_TO_SELF, 0f,

                Animation.RELATIVE_TO_SELF, isShow ? 1f : 0f,

                Animation.RELATIVE_TO_SELF, isShow ? 0f : 1f);

        translateAnimation.setDuration(animeTime);

        //translateAnimation.setInterpolator(new LinearInterpolator());

        tvTips.setAnimation(translateAnimation);

        tvTips.setVisibility(isShow ? VISIBLE : GONE);

    }

    //提示文本显示隐藏

    private void tips2ShowAnime(boolean isShow) {

        if ((tvTips2.getVisibility() == VISIBLE) == isShow)

            return;

        AlphaAnimation translateAnimation = new AlphaAnimation(isShow ? 0 : 1, isShow ? 1 : 0);

        translateAnimation.setDuration(animeTime);

        //translateAnimation.setInterpolator(new LinearInterpolator());

        tvTips2.setAnimation(translateAnimation);

        tvTips2.setVisibility(isShow ? VISIBLE : GONE);

    }

    //成功完成拼图滑块消失

    private void blockHideAnime() {

        AlphaAnimation translateAnimation = new AlphaAnimation(1, 0);

        translateAnimation.setDuration(animeTime);

        //translateAnimation.setInterpolator(new LinearInterpolator());

        ivBlock.setAnimation(translateAnimation);

        ivBlock.setVisibility(GONE);

    }

    //失败震动动画

    private void failAnime() {

    }

    //成功高亮动画

    private void flashShowAnime() {

        TranslateAnimation translateAnimation = new TranslateAnimation(

                Animation.RELATIVE_TO_SELF, 1f,

                Animation.RELATIVE_TO_SELF, -1f,

                Animation.RELATIVE_TO_SELF, 0f,

                Animation.RELATIVE_TO_SELF, 0f);

        translateAnimation.setDuration(flashTime);

        //translateAnimation.setInterpolator(new LinearInterpolator());

        vFlash.setAnimation(translateAnimation);

        vFlash.setVisibility(VISIBLE);

        translateAnimation.setAnimationListener(new Animation.AnimationListener() {

            @Override

            public void onAnimationStart(Animation animation) {

            }

            @Override

            public void onAnimationEnd(Animation animation) {

                vFlash.setVisibility(GONE);

            }

            @Override

            public void onAnimationRepeat(Animation animation) {

            }

        });

    }

    //失败延时重置控件

    private Runnable resetRun = new Runnable() {

        @Override

        public void run() {

            tipsShowAnime(false);

            tips2ShowAnime(true);

            sb.setEnabled(true);

            final int position = sb.getProgress();

            ValueAnimator animator = ValueAnimator.ofFloat(1f, 0);

            animator.setDuration(animeTime).start();

            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override

                public void onAnimationUpdate(ValueAnimator animation) {

                    float f = (Float) animation.getAnimatedValue();

                    sb.setProgress((int) (position * f));

                }

            });

            isNormal = true;

        }

    };

    //监听

    private DragListenner dragListenner;

    public interface DragListenner {

        void onDrag(float position);

    }

    public void setDragListenner(DragListenner dragListenner) {

        this.dragListenner = dragListenner;

    }

}

* 完整项目请前往: 项目github地址

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

推荐阅读更多精彩内容