我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

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

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

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

用法:


 <com.leo.library.view.GestureContentView
   android:id="@+id/id_gesture_pwd"
   android:layout_gravity="center_horizontal"
   android:layout_marginTop="10dp"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:column="3"
   app:row="3"
   app:padding="50dp"
   app:normalDrawable="@drawable/gesture_node_normal"
   app:selectedDrawable="@drawable/gesture_node_pressed"
   app:erroDrawable="@drawable/gesture_node_wrong"
   app:normalStrokeColor="#000"
   app:erroStrokeColor="#ff0000"
   app:strokeWidth="4dp"
   />

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

attrs.xml:


<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="IndicatorView">
  <!--默认状态的drawable-->
  <attr name="normalDrawable" format="reference" />
  <!--被选中状态的drawable-->
  <attr name="selectedDrawable" format="reference" />
  <!--错误状态的drawabe-->
  <attr name="erroDrawable" format="reference" />
  <!--列数-->
  <attr name="column" format="integer" />
  <!--行数-->
  <attr name="row" format="integer" />
  <!--padding值,padding值越大点越小-->
  <attr name="padding" format="dimension" />
  <!--默认连接线颜色-->
  <attr name="normalStrokeColor" format="color" />
  <!--错误连接线颜色-->
  <attr name="erroStrokeColor" format="color" />
  <!--连接线size-->
  <attr name="strokeWidth" format="dimension" />
 </declare-styleable>
</resources>

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<Integer> 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、我们需要传入的默认显示图片:


 <!--默认状态的drawable-->
  <attr name="normalDrawable" format="reference" />

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


<!--被选中状态的drawable-->
  <attr name="selectedDrawable" format="reference" />

其它的一些属性:


<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="IndicatorView">
  <!--默认状态的drawable-->
  <attr name="normalDrawable" format="reference" />
  <!--被选中状态的drawable-->
  <attr name="selectedDrawable" format="reference" />
  <!--列数-->
  <attr name="column" format="integer" />
  <!--行数-->
  <attr name="row" format="integer" />
   </declare-styleable>
</resources>

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


<com.leo.library.view.IndicatorView
  android:id="@+id/id_indicator_view"
  android:layout_marginTop="20dp"
  android:layout_width="85dp"
  android:layout_height="85dp"
  android:layout_alignParentTop="true"
  android:layout_centerHorizontal="true"
  app:column="3"
  app:normalDrawable="@drawable/shape_white_indicator"
  app:padding="8dp"
  app:row="3"
  app:selectedDrawable="@drawable/shape_orange_indicator" />

注意:

中间的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,然后根据传入的宽、高计算出我们的点的大小。


<com.leo.library.view.IndicatorView
  android:id="@+id/id_indicator_view"
  android:layout_marginTop="20dp"
  android:layout_width="85dp"
  android:layout_height="85dp"

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


android:layout_width="wrap_content"
android:layout_height="wrap_content"

好了,既然知道咋测量我们的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 < mRow; i++) {
   //遍历列数
   for (int j = 0; j < mColumn; j++) {
    float left = (j + 1) * DEFAULT_PADDING + j * mCellWidth;
    float top = (i + 1) * DEFAULT_PADDING + i * mCellHeight;
    //每个点代表的密码值=点对应的行数值*列数+对应的列数
    //比如3*3的表格,然后第二排的第一个=1*3+0=3
    int num=i * mColumn + j;
    //此点是不是在传入的密码集合中?
    if (pwds!=null&&pwds.contains(num)) {
     //这个点在传入的密码集合中的话就画一个选中的bitmap
     canvas.drawBitmap(mSelectedBitmap, left, top, null);
    } else {
     canvas.drawBitmap(mNormalBitmap, left, top, null);
    }
   }
  }
 }

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


 public void setPwds(List<Integer> 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笔记(二)

您可能感兴趣的文章:Android手势密码的实现Android自定义控件实现手势密码Android自定义UI手势密码终结版Android仿支付宝手势密码解锁功能Android手势密码实现实例代码Android九宫格手势密码代码设计Android自定义UI手势密码改进版源码下载Android自定义UI手势密码简单版Android手势密码view学习笔记(二)Android自定义View手势密码


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

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

我们还是接着我们上一篇博客中的内容往下讲哈,上一节 Android手势密码view笔记(一)我们已经实现了我们的IndicatorView指示器view了:下面我们来实现下我们的手势密码view:实现思路: 1、我们照样需要拿到用户需要显示
2022-06-06

android学习笔记之View的滑动

Android开发中我们常常需要View滑动实现一些绚丽的效果来优化用户体验,下面这篇文章主要给大家介绍了关于android学习笔记之View滑动的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
2023-01-03

Android学习笔记——Menu介绍(一)

背景: Android3.0(API level 11)开始,Android设备不再需要专门的菜单键。 随着这种变化,Android app应该取消对传统6项菜单的依赖。取而代之的是提供anction bar来提供基本的用户功能。
2022-06-06

第一行代码—Android第二版学习笔记

Android 第一行代码学习笔记第一章 概述1.1安卓系统架构1.2Android四大组件1.3项目结构1.4app目录结构1.5 项目运行原理1.6 res目录详解1.7日志工具的使用第二章 活动2.1 创建基本活动2.2 使用Inte
2022-06-06

如何在Android中利用view实现一个手势密码功能

这篇文章将为大家详细讲解有关如何在Android中利用view实现一个手势密码功能,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。用法:
2023-05-31

移动终端学习笔记之Android(一)Android第一堂课

移动终端 我们可以简单认为智能手机、平板、可穿戴式智能设备都是移动终端。当然大部分人最熟悉的就是手机。所以笔者在后续学习当中所针对的都是手机应用开发。 从1993年,IBM推出了首台智能手机IBM Simon。1996年,微软开发Windo
2022-06-06

Android学习笔记(一)环境安装及第一个hello world

开发环境 安装JDK和JRE 下载安装文件并安装: jdk-8u11-windows-i586.exe jre-8u11-windows-i586.exe 使用google提供的adt-bundle,直接解压到本地即可使用,使用版本如下:
2022-06-06

Android学习笔记--Activity中使用Intent传值示例代码

Intent,又称为意图,是一种运行时绑定机制,它能在程序运行的过程中链接两个不同的组件(Activity、Service、BroadcastReceiver)。通过Intent,程序可以向Android表达某种请求或意愿,Android会
2022-06-06

Android学习笔记--通过Application传递数据代码示例

在整个Android程序中,有时需要保存某些全局的数据(如:用户信息),方便在程序的任何地方调用。在Activity之间数据传递中有一种比较使用的方式,就是全局对象,使用过J2EE的都应该知道JavaWeb的四个作用域,其中Applicat
2022-06-06

Android开发中怎么实现一个手势密码功能

本篇文章为大家展示了Android开发中怎么实现一个手势密码功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1.如果使用GestureOverlayView,在xml配置文件中使用Android.
2023-05-31

Android学习笔记--使用剪切板在Activity中传值示例代码

在Activity之间传递数据还可以利用一些技巧,不管windows还是Linux操作系统,都会支持一种叫剪切板的技术,也就是某一个程序将一些数据复制到剪切板上,然后其他的任何程序都可以从剪切板中获取数据,在Android系统中也存在此技术
2022-06-06

MySQL入门阶段这一篇就够了-学习笔记(手敲1.5万字)

前言 虽然在大一下学期,就已经接触到了MySQL,但是那个时候只是会用MySQL进行增删改查,在大三上学期,尝试投简历寻找实习时,对方公司对于程序员的MySQL水平有很高的要求,所以我开始系统化的学习MySQL。顺便将整理出的笔记逐步写入博
2023-08-18

如何在Android应用中实现一个手势密码功能

如何在Android应用中实现一个手势密码功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。实现思路:1. 正上方的提示区域,用一个类(LockIndicato
2023-05-31

Android通过"记住密码"功能学习数据存储类SharedPreferences详解及实例

SharedPreferences是Android中存储简单数据的一个工具类。可以想象它是一个小小的Cookie,它通过用键值对的方式把简单数据类型(boolean、int、float、long和String)存储在应用程序的私有目录下(d
2022-06-06

Android studio 学习2:实现密码登录界面和验证码登录界面的切换(在同一xml页面)

Android studio :实现密码登录界面和验证码登录界面的切换(不用新建xml页面) 一、了解Xml设置隐藏属性 android:visibility=" " Visible:正常显示 Invisible:保留位置 Gone:完全隐
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录