热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android手势密码view学习笔记(一)

这篇文章主要为大家详细介绍了Android手势密码view的学习笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

刚接触Android的时候看到别人写的手势密码view,然后当时就在想,我什么时候才能写出如此高端的东西?? 没关系,不要怕哈,说出这样话的人不是你技术不咋地而是你不愿意花时间去研究它,其实也没有那么难哦(世上无难事,只怕有心人!),下面我们就一步一步实现一个手势密码view。

想必都看过手势密码view,但我们还是看看我们今天要实现的效果吧:

上面是一个手势view的提示view,下面是一个手势view。

用法:

 

app打头的是自定义的一些属性,

attrs.xml:

<&#63;xml version="1.0" encoding="utf-8"&#63;>

 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
 

MainActivity.java:

public class MainActivity extends AppCompatActivity implements IGesturePwdCallBack {
 private GestureContentView mGestureView;
 private IndicatorView indicatorView;
 private TextView tvIndicator;

 private int count=0;
 private String pwd;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mGestureView= (GestureContentView) findViewById(R.id.id_gesture_pwd);
  indicatorView= (IndicatorView) findViewById(R.id.id_indicator_view);
  tvIndicator= (TextView) findViewById(R.id.id_indicator);
  mGestureView.setGesturePwdCallBack(this);
 }

 @Override
 public void callBack(List pwds) {
  StringBuffer sbPwd=new StringBuffer();
  for (Integer pwd:pwds) {
   sbPwd.append(pwd);
  }
  tvIndicator.setText(sbPwd.toString());
  if(pwds!=null&&pwds.size()>0){
   indicatorView.setPwds(pwds);
  }
  if(count++==0){
   pwd=sbPwd.toString();
   Toast.makeText(this,"请再次绘制手势密码",Toast.LENGTH_SHORT).show();
   mGestureView.changePwdState(PointState.POINT_STATE_NORMAL,0);
  } else{
   count=0;
   if(pwd.equals(sbPwd.toString())){
    Toast.makeText(this,"密码设置成功",Toast.LENGTH_SHORT).show();
   }else{
    Toast.makeText(this,"两次密码不一致,请重新绘制",Toast.LENGTH_SHORT).show();
    indicatorView.startAnimation(AnimationUtils.loadAnimation(this,R.anim.anim_shake));
    count=0;
    mGestureView.changePwdState(PointState.POINT_STATE_ERRO,0);
    new Handler().postDelayed(new Runnable() {
     @Override
     public void run() {
      mGestureView.changePwdState(PointState.POINT_STATE_NORMAL,0);
     }
    },1000);
   }
  }
 }
}

看不懂也没关系啊,我们先明确下我们要完成的目标,然后一步一步实现:

先实现下我们的指示器view,因为实现了指示器view也就相当于实现了一半的手势密码view了:

实现思路:

1、我们需要知道指示器有多少行、多少列、默认显示什么、选中后显示什么?
2、然后根据传入的密码把对应的点显示成选中状态,没有选中的点为默认状态。

好了,知道我们的思路,首先自定义一个view叫IndicatorView继承view,然后重写三个构造方法:

public class IndicatorView extends View {
 public IndicatorView(Context context) {
  this(context, null);
 }

 public IndicatorView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

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

定义自定义属性(在res/values下创建attrs.xml文件):

1、我们需要传入的默认显示图片:

 
  

2、我们需要拿到传入的选中时图片:


  

其它的一些属性:

<&#63;xml version="1.0" encoding="utf-8"&#63;>

 
  
  
  
  
  
  
  
  
   

定义完属性后,此时我们xml中就可以引用自定义view了:


注意:

中间的drawable文件可以在github项目中找到,链接我会在文章最后给出。

有了自定义属性,然后我们在带三个参数的构造方法中获取我们在布局文件传入的自定义属性:

private static final int NUMBER_ROW = 3;
 private static final int NUMBER_COLUMN = 3;
 private int DEFAULT_PADDING = dp2px(10);
 private final int DEFAULT_SIZE = dp2px(40);

 private Bitmap mNormalBitmap;
 private Bitmap mSelectedBitmap;
 private int mRow = NUMBER_ROW;
 private int mColumn = NUMBER_COLUMN;

 public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  final TypedArray a = context.obtainStyledAttributes(
    attrs, R.styleable.IndicatorView, defStyleAttr, 0);
  mNormalBitmap = drawableToBitmap(a.getDrawable(R.styleable.IndicatorView_normalDrawable));
  mSelectedBitmap = drawableToBitmap(a.getDrawable(R.styleable.IndicatorView_selectedDrawable));
  if (a.hasValue(R.styleable.IndicatorView_row)) {
   mRow = a.getInt(R.styleable.IndicatorView_row, NUMBER_ROW);
  }
  if (a.hasValue(R.styleable.IndicatorView_column)) {
   mColumn = a.getInt(R.styleable.IndicatorView_row, NUMBER_COLUMN);
  }
  if (a.hasValue(R.styleable.IndicatorView_padding)) {
   DEFAULT_PADDING = a.getDimensionPixelSize(R.styleable.IndicatorView_padding, DEFAULT_PADDING);
  }
 }

好了,现在我们已经拿到了我们想要的东西了,接下来我们需要知道我的view要多大,相比小伙伴都知道接下来要干什么了吧?对~! 我们需要重写下onMeasure方法,然后指定我们view的大小:

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 }

那么我们该以一个什么样的规则指定我们的view的大小呢?

1、当用户自己指定了view的大小的话,我们就用用户传入的size,然后根据传入的宽、高计算出我们的点的大小。


2、如果用户没有指定view的大小,宽高都设置为wrap_content的话,我们需要根据用户传入的选中图片跟没选中图片的大小计算view的大小:

android:layout_
android:layout_

好了,既然知道咋测量我们的view后,我们接下来就实现出来:

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  float width = MeasureSpec.getSize(widthMeasureSpec);
  float height = MeasureSpec.getSize(heightMeasureSpec);
  float result=Math.min(width,height);
  height = getHeightValue(result, heightMode);
  width = getWidthValue(result, widthMode);
  }
 private float getHeightValue(float height, int heightMode) {
  //当size为确定的大小的话
  //每个点的高度等于(控件的高度-(行数+1)*padding值)/行数
  if (heightMode == MeasureSpec.EXACTLY) {
   mCellHeight = (height - (mRow + 1) * DEFAULT_PADDING) / mRow;
  } else {
   //高度不确定的话,我们就取选中的图片跟未选中图片中的高度的最小值
   mCellHeight = Math.min(mNormalBitmap.getHeight(), mSelectedBitmap.getHeight());
   //此时控件的高度=点的高度*行数+(行数+1)*默认padding值
   height = mCellHeight * mRow + (mRow + 1) * DEFAULT_PADDING;
  }
  return height;
 }

宽度计算方式也是一样的话,只是行数换成了列数:

 private float getWidthValue(float width, int widthMode) {
  if (widthMode == MeasureSpec.EXACTLY) {
   mCellWidth = (width - (mColumn + 1) * DEFAULT_PADDING) / mColumn;
  } else {
   mCellWidth = Math.min(mNormalBitmap.getWidth(), mSelectedBitmap.getWidth());
   width = mCellWidth * mColumn + (mColumn + 1) * DEFAULT_PADDING;
  }
  return width;
 }

好了,现在是知道了点的高度跟宽度,然后控件的宽高自然也就知道了,但是如果我们传入的选中的图片跟未选择的图片大小不一样咋办呢?没关系,接下来我们重新修改下图片的size:

@Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  .....
  height = getHeightValue(result, heightMode);
  width = getWidthValue(result, widthMode);
  setMeasuredDimension((int) width, (int) height);
  //重新修改图片的size
  resizeBitmap(mCellWidth, mCellHeight);
 }
 private void resizeBitmap(float width, float height) {
  if (width > 0 && height > 0) {
   if (mNormalBitmap.getWidth() != width || mNormalBitmap.getHeight() !=height) {
    if (mNormalBitmap.getWidth() > 0 && mNormalBitmap.getHeight() > 0) {
     mNormalBitmap = Bitmap.createScaledBitmap(mNormalBitmap, (int) width, (int) height, false);
    }
   }
   if (mSelectedBitmap.getWidth()!=width || mSelectedBitmap.getHeight() !=height) {
    if (mSelectedBitmap.getWidth() > 0 && mSelectedBitmap.getHeight() > 0) {
     mSelectedBitmap = Bitmap.createScaledBitmap(mSelectedBitmap, (int) width, (int) height, false);
    }
   }
  }
 }

好了,图片也拿到了,控件的宽高跟点的宽高都知道,所以接下来我们该进入我们的核心代码了(重写onDraw方法,画出我们的点):

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  //遍历行数
  for (int i = 0; i 

嗯嗯!!然后我们暴露一个方法,让外界传入需要现实的密码集合:

 public void setPwds(List pwds) {
  if(pwds!=null)this.pwds=pwds;
  if (Looper.myLooper() == Looper.getMainLooper()) {
   invalidate();
  } else {
   postInvalidate();
  }
 }

好啦~~ 我们的指示器view就做完啦~~~ 是不是很简单呢? 指示器view做完后,再想想手势密码view,是不是就只是差根据手势改变,然后画出我们的line呢?

现附上项目的github链接:
https://github.com/913453448/GestureContentView/

下一节我们将一起实现一下手势密码view。

Android手势密码view笔记(二)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 【MicroServices】【Arduino】装修甲醛检测,ArduinoDart甲醛、PM2.5、温湿度、光照传感器等,数据记录于SD卡,Python数据显示,UI5前台,微服务后台……
    这篇文章介绍了一个基于Arduino的装修甲醛检测项目,使用了ArduinoDart甲醛、PM2.5、温湿度、光照传感器等硬件,并将数据记录于SD卡,使用Python进行数据显示,使用UI5进行前台设计,使用微服务进行后台开发。该项目还在不断更新中,有兴趣的可以关注作者的博客和GitHub。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
author-avatar
你是我的小二郎
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有