Android仿支付宝支付密码框

  • 前言
  • 1. 设计思路
  • 2. 项目地址和implementation使用方法
  • 3. 涉及到的内容
  • 4. 设计流程
    • 4.1 Layout
    • 4.2 GridView和Adapter
    • 4.3 自定义密码框(自定义EditText)
    • 4.4 Dialog
  • 参考材料

前言

由于一些需求,需要完成一个类似于支付一样的密码框功能,这个时候我就决定是说不从网上去找开源项目,而是决定自己参考别人的做法自己来做一个这样类似的功能来实现。
目的是为了在写项目的同时学到知识,所以本文我会解析一下我具体代码的作用(自我分析),而不是简单介绍项目。

1. 设计思路

整体的内容很简单,就是一个Dialog,然后其中有一个支付宝输入框一样的密码输入内容。
我将整体内容分成三个模块分开来写,Dialog,Layout和专门负责数字按键的gridView。

其中,最上面的X按钮是关闭用,这个是固定的
然后中间的密码框是重写了editText后完成的,并不是像其他人用的那种6个EditText或者是说6个TextView这样。
下面的三个忘记密码是分成了三个不同的区域,其中也设置了点击事件。
最下面是用GridView来实现了按钮的排列不同的按钮。

2. 项目地址和implementation使用方法

项目地址:https://github.com/wodongx123/PayDialogUtil

Implementation使用方法(前提是你项目是使用AndroidX而不是Android Support的):

  1. 在Project的build.gradle中,添加
    maven { url ‘https://jitpack.io’ }
  2. 在需要引用的包的build.gradle中,添加
    implementation ‘com.github.wodongx123:PayDialogUtil:1.1’(相比1.0修复了bug)。
  3. 使用例:
    final PayDialog payDialog = new PayDialog(MainActivity.this);
    //为了方便,五个参数合成一个方法
    //从左到右依次是标题title,内容Content,左可点击文本,中可点击文本,右可点击文本
    //如果不需要其中的某项,就传""或者null,会自动隐藏
    //四个接口中fillContent代表实时已输入的密码(界面显示最多六位),后面三个对应着可点击文本的对应点击事件。
    payDialog.setText("支付", "请支付10元", "忘记密码", "忘记密码", "忘记密码").setListener(new PayDialog.DialogListener() {@Overridepublic void fillContent(String content) {if (content.length() == 6){payDialog.dismiss();Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();}}@Overridepublic void leftBtn() {Toast.makeText(MainActivity.this, "left button", Toast.LENGTH_SHORT).show();}@Overridepublic void centerBtn() {Toast.makeText(MainActivity.this, "center button", Toast.LENGTH_SHORT).show();}@Overridepublic void rightBtn() {Toast.makeText(MainActivity.this, "right button", Toast.LENGTH_SHORT).show();}});
    //本身是继承Dialog的,直接调用show就可以显示。
    payDialog.show();
    

3. 涉及到的内容

  • GridView基础。
  • 自定义View。
  • 自定义Dialog。
  • 基本绘图Paint和Canvas。
  • 补间动画。

4. 设计流程

4.1 Layout

  1. 创建一个layout(我取名叫layout_pay.xml),把整个的布局都在xml中画好,然后最下面是一个GridView,现在还没写上实际的按键。

  2. 其中我把除了Title以外的TextView都Visibility="gone"了,因为是打算到时候根据创建时传入的内容决定显示不显示。

  3. 然后创建一个PayLayout类继承LinearLayout,然后加载我们刚刚写好的layout_pay.xml。

    public class PayLayout extends LinearLayout {private Context mContext;private View mView;ImageView ivClose;//中间省略若干控件......GridView gvBtn;//下面三个是继承LinearLayout后必须添加的三个重载方法public PayLayout(Context context) {super(context);initLayout(context);}public PayLayout(Context context, @Nullable AttributeSet attrs) {super(context, attrs);initLayout(context);}public PayLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initLayout(context);}public void initLayout(Context context){mContext = context;//下面这一行重要mView = LayoutInflater.from(mContext).inflate(R.layout.layout_pay, this);//绑定控件{ivClose = mView.findViewById(R.id.iv_close);......gvBtn = mView.findViewById(R.id.gv_btn);}}
    }
    

    这里我简单说一下mView = LayoutInflater.from(mContext).inflate(R.layout.layout_pay, this)
    这一行,这个是自定义Layout中最重要的一个内容。
    LayoutInflater.inflate()这个方法就做三件事

    1. 加载一个xml布局作为子布局。
    2. 将这个布局放到父布局内部。
    3. 如果有return(像我们用mView来接收),return的是子布局的实例。

    .
      然后看我们传入的两个参数R.layout.layout_pay就是加载的子布局,this指的就是PayLayout(是继承自LinearLayout)。
      这个放入的关系,你可以想像成,PayLayout就是一张桌子,然后R.layout.layout_pay就是一个杯子,我们拿出了这个杯子,并且将他放在了桌子上,这就是infalte这个方法做的事了。

4.2 GridView和Adapter

  1. 接下来用GridView来加载我们的键盘,为此我们需要先定义键盘中每个按钮的样子,创建一个item的Layout(这里我取名item_button.xml)。

    画的很简单,一个重叠摆放的TextView和ImageView,ImageView在用于存放删除键图标的同时还负责撑开整个Layout的大小。

  2. GridView和ListView,RecyclerView一样都需要适配器Adapter来加载内部的多个子布局,这里创建一个ButtonAdapter,并且重写内部的几个方法。

    public class ButtonAdapter extends BaseAdapter {private static final String TAG = "ButtonAdapter";private Context mContext;private char[] mList;public ButtonAdapter(Context context, char[] list){mContext = context;mList = list;}@Overridepublic int getCount() {return mList.length;}@Overridepublic Object getItem(int position) {return mList[position];}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null) {convertView = LayoutInflater.from(mContext).inflate(R.layout.item_button, null);viewHolder = new ViewHolder();viewHolder.relativeLayout = convertView.findViewById(R.id.rl_item);viewHolder.textView = convertView.findViewById(R.id.tv_item);viewHolder.imageView = convertView.findViewById(R.id.iv_item);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}switch (mList[position]){case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '0':viewHolder.textView.setText(String.valueOf(mList[position]));break;case ' ':viewHolder.relativeLayout.setEnabled(false);break;case '/': ;viewHolder.relativeLayout.setBackgroundResource(R.drawable.selector_del);viewHolder.imageView.setImageResource(R.drawable.ic_paykeyborddelete);break;}viewHolder.relativeLayout.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mListener != null)  mListener.getContent(mList[position]);}});return convertView;}class ViewHolder{RelativeLayout relativeLayout;TextView textView;ImageView imageView;}}
    

      别的方法都不说,这里主要简单讲一下getView这个方法,这个方法是Adapter的核心

      我们都知道,如果用重复使用多个大体相同的布局的时候(比如说此处我们大家都是按钮但是只有按钮上的字不同),就会需要用到ListView或者RecyclerView这样的东西,那么,到底是怎么决定ListView中内部每个item的差距呢?答案就在这个getView方法中。

       getView()这个方法就做一件事:告诉父布局(这里是GridView),在一排的item中,第X(positon)个子布局长什么样。

       然后convertView这个参数就是当前该子布局的内容,但是由于每个item的getView会调用多次,所以要先判断是否为空,当为空的时候,就再用LayoutInflater.inflate()方法创建一个view,并且在getView 的最后return它。
       要注意的是,这里我们inflate方法第二个参数是为null,这就表示这个方法只加载了一个布局,但是并没有把它放到父布局内部,而这个放到父布局内部(此处是我们的GridView,这里你可以想象成桌子是GridView,杯子是item)会在return之后自动完成。

       接下来是setTag和getTag这个方法,这个方法一般用于区分不同的View,我们这里干脆就自定义一个内部类,再把控件都塞进去用于区分不同的item。

  3. Adapter写完了,接下来是按键的回馈,这里我们存储按钮主要的区分是通过一个char数组,那么要怎么区分不同的按钮呢,这里我自定义了一个接口interface供外部使用。

    public class ButtonAdapter extends BaseAdapter {private static final String TAG = "ButtonAdapter";private ButtonListener mListener;public void setmListener(ButtonListener mListener) {this.mListener = mListener;}.......//省略了刚刚出现过的代码@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {.......//省略了刚刚出现过的代码viewHolder.relativeLayout.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mListener != null)  mListener.getContent(mList[position]);}});return convertView;}class ViewHolder{RelativeLayout relativeLayout;TextView textView;ImageView imageView;}public interface ButtonListener{void getContent(char c);}}
    

    然后外部Layout是这么调用的。

    public class PayLayout extends LinearLayout {......//省略代码private void setGridView() {gvBtn.setNumColumns(3); //设成三列char[] list = new char[12];{list[0] = '1';list[1] = '2';list[2] = '3';list[3] = '4';list[4] = '5';list[5] = '6';list[6] = '7';list[7] = '8';list[8] = '9';list[9] = ' ';list[10] = '0';list[11] = '/';}ButtonAdapter buttonAdapter = new ButtonAdapter(mContext, list);buttonAdapter.setmListener(new ButtonAdapter.ButtonListener() {@Overridepublic void getContent(char c) {editPwd(c);}});gvBtn.setAdapter(buttonAdapter);}//点击按钮后内容的变化private void editPwd(char c) {switch (c){case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '0':etPassword.setText(etPassword.getText().toString() + c);break;case '/':int length = etPassword.getText().toString().length();if (length > 0)etPassword.setText(etPassword.getText().toString().substring(0, length-1));break;}}
    }
    
  4. 由于写接口这一招很好用,我就又故技重施的将Layout中的各种点击事件还有保存密码内容的edittext做了同样的操作用于将来Dialog写继承事件,代码差不多同上。

4.3 自定义密码框(自定义EditText)

我们的密码框不能直接使用EditText,因为输入完后显示的是实心圆,所以需要自己画一个界面来。

  1. 首先我们实际上的密码框外面的那一圈,我是用xml写的…并不是代码实现。

    <shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle" ><strokeandroid:color="@color/divideLine"android:width="1dp"/><cornersandroid:radius="5dp"/></shape>
    

    shape是图形,corners是弧度,stroke是边框,这些不多说了,然后在EditView中android:background引用。

  2. 创建一个类继承EditView,然后再xml中引用就完成。
    public class PwdView extends EditText {private static final String TAG = "PwdView";int width = getWidth();int height = getHeight();Paint linePaint;Paint circlePaint;public PwdView(Context context) {super(context);init();}public PwdView(Context context, AttributeSet attrs) {super(context, attrs);init();}public PwdView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {initPaints();}private void initPaints() {linePaint = new Paint();linePaint.setAntiAlias(true);linePaint.setColor(Color.parseColor("#dfdddf"));linePaint.setStyle(Paint.Style.FILL_AND_STROKE);linePaint.setStrokeWidth(4);circlePaint = new Paint();circlePaint.setAntiAlias(true);circlePaint.setColor(Color.parseColor("#000000"));circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);circlePaint.setStrokeWidth(1);}@Overrideprotected void onDraw(Canvas canvas) {width = getWidth();height = getHeight();for (int i=1; i<6; i++) //画五条竖线canvas.drawLine(width/6*i, 0, width/6*i, height, linePaint);int mid = width/6/2;for (int i=0; i<getText().toString().length(); i++){ //写了几个字符就画几个实心圆!canvas.drawCircle(width/6*i + mid, height/2, height/14+10, circlePaint);}}@Overridepublic boolean isEnabled() {return false;}
    }
    

这里我先讲一下onDraw这个方法,它一开始是这样的。

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);
}

这个super.onDraw(canvas)这个方法是做什么的呢,我们回忆一下平时EditText是怎么用的,点击EditText,我们往键盘上敲字符,敲一个然后editText就显示一个,而这个onDraw就是负责显示用的。

可以来做一个测试(请自行测试):把super.onDraw()这一行删掉,再往editText内敲字符,你就会发现EditText中什么都不会显示。

那么我们现在不需要它显示原本的打一个字符敲一个字符,而是显示分割线和实心圆,那么就要先删掉super.onDraw(),再根据我们的需求来自行绘画。

有关怎么使用Paint和Canvas进行自定义画View,详细参考这篇,我也是看这篇学的:https://blog.csdn.net/moira33/article/details/79111343(无图)
https://www.jianshu.com/p/76603b122fb4(有图)

这里简单做一个介绍,想要进行画图,就需要纸和笔,而在Android中,Paint就是笔,而Canvas就是纸,但是这个纸是无限大的,而我们只能看到控件显示的纸的那一部分(你可以理解成外面的风景是无限大的,而我们只能透过窗户来看到其中一部分,而这个窗户的大小就是控件的大小)。

我们主要是需要用笔来画分割线,和我们分割线的实心圆而怎么画我也是这个五行代码就搞定了,请自行理解,涉及到一点点数学。

4.4 Dialog

  1. 最后来写Dialog,首先需要创建一个xml(这里取名dialog_pay.xml)文件来用上我们刚刚写的PayLayout,代码是最简单的了

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><com.example.paydialogutil.PayLayoutandroid:id="@+id/layout_pay"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
    
  2. 接下来,写一个类继承Dialog类,重写内部方法和完成初始化。

    public class PayDialog extends Dialog {private Context mContext;  //环境private View mView;   //视图PayLayout mLayout;   // 布局private Window mWindow;private DialogListener mListener;public PayDialog(@NonNull Context context) {super(context, R.style.dialog_pay_theme);initDialog(context);}private void initDialog(Context context) {mContext = context;mView = LayoutInflater.from(mContext).inflate(R.layout.dialog_pay, null);//设置好viewmLayout = mView.findViewById(R.id.layout_pay);mWindow = getWindow();mWindow.setContentView(mView);//将view投到window(也就是Dialog上)WindowManager.LayoutParams lp = getWindow().getAttributes();lp.width = WindowManager.LayoutParams.MATCH_PARENT;lp.height = WindowManager.LayoutParams.WRAP_CONTENT;lp.gravity = Gravity.BOTTOM;mWindow.setAttributes(lp);mWindow.setWindowAnimations(R.style.dialog_anim); //设置窗口的进入退出动画(补间动画)}}
    

    这里几个要点,我一个一个分析。

  •   mView = LayoutInflater.from(mContext).inflate(R.layout.dialog_pay, null):这里加载了我们刚刚写的xml文件,然后呢这个代码也是第三次出现了,加载我们的子布局,但是这里要注意的是,Dialog本身,不是一个布局,所以第二个参数暂时先填null。
      Dialog是通过其内部的一个Window类来显示布局内容,所以获取Window的实例,再将加载的布局mView放上去。(Dialog放上Layout,Layout放上控件,GridView放上item,差不多就这种感觉)。

  •   super(context, R.style.dialog_pay_theme):这里简单介绍一下Dialog的构造函数,简单来说,Dialog有一个默认的Style类型,当你的构造函数没有传自定义的Style的时候,他就会用上这个默认的Style,而如果一旦内部调用了默认的style,那么你的对话框就会带上一个自带的padding值,无法铺满屏幕。
      当然还有一种解决办法就是调用mWindow.getDecorView().setPadding(),只是这里我没有用。

    <resources xmlns:tools="http://schemas.android.com/tools"><!--主题样式--><style name="dialog_pay_theme" parent="@style/Theme.AppCompat.Dialog"><!--边框--><item name="android:windowFrame">@null</item><item name="android:windowNoTitle">true</item><item name="android:windowBackground">@android:color/transparent</item><item name="android:backgroundDimAmount">0.4</item><!--弹框背景灰度--></style></resources>
    
  • setWindowAnimation()在Dialog中,无论是补间动画还是属性动画都难以实现理想的对话框从下弹出的效果,前者动画还在跳转的时候你就已经可以点按钮了(控件实际位置不变),后者不好设置参数(嫌麻烦)。这里有关Window可以专门的通过style中设置相关的动画达到效果,动画的xml文件就是补间动画的写法。

    <resources xmlns:tools="http://schemas.android.com/tools"><style name="dialog_anim" parent="android:Animation"><item name="android:windowEnterAnimation">@anim/anim_dialog_in</item><item name="android:windowExitAnimation">@anim/anim_dialog_out</item></style></resources>
    
  1. 最后写上对外的操作接口,就大功告成,就是setText和setListener的方法,代码不难。

  2. 最后来看看具体的调用,三行代码加四个接口方法就能完成调用!

    final PayDialog payDialog = new PayDialog(MainActivity.this);
    payDialog.setText("支付", "请支付10元", "忘记密码", "忘记密码", "忘记密码").setListener(new PayDialog.DialogListener() {@Overridepublic void fillContent(String content) {if (content.length() == 6){payDialog.dismiss();Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();}}@Overridepublic void leftBtn() {Toast.makeText(MainActivity.this, "left button", Toast.LENGTH_SHORT).show();}@Overridepublic void centerBtn() {Toast.makeText(MainActivity.this, "center button", Toast.LENGTH_SHORT).show();}@Overridepublic void rightBtn() {Toast.makeText(MainActivity.this, "right button", Toast.LENGTH_SHORT).show();}});
    payDialog.show();
    

参考材料

Android仿支付宝密码输入框(自定义数字键盘) - 落雨敏 - CSDN博客
https://blog.csdn.net/lin857/article/details/84111930
LayoutInflater.from(this).inflate()方法参数解析 - Sacredness的博客 - CSDN博客
https://blog.csdn.net/Sacredness/article/details/88684527
Android自定义Dialog的Match_parent无效问题解决 - 一座小楼的专栏 - CSDN博客
https://blog.csdn.net/czhpxl007/article/details/50519056?utm_source=blogxgwz1
Android 自定义View之绘图 - CSDN/简书(我觉得两文的作者为同一人)
https://blog.csdn.net/moira33/article/details/79111343(无图有目录)
https://www.jianshu.com/p/76603b122fb4(有图无目录)

Android 仿支付宝支付密码输入框的具体实现相关推荐

  1. android绑定支付宝支付宝支付宝支付密码错误,Android 仿支付宝支付密码输入框

    看到网友:http://www.jointforce.com/jfperiodical/article/3527?ref=myread 做的仿支付宝支付密码输入框,感觉挺适合初学自定义view的同仁练 ...

  2. Android 仿支付宝支付密码输入框

    看到网友:http://www.jointforce.com/jfperiodical/article/3527?ref=myread 做的仿支付宝支付密码输入框,感觉挺适合初学自定义view的同仁练 ...

  3. 仿支付宝支付密码输入框

    仿支付宝支付密码输入框 前段时间看到小伙伴们在做一个密码输入框,刚好拿来复习下Quartz 2D,不废话,直接上图: 主要思路如下: - UITextField上面覆盖一个UIView - 设置UIV ...

  4. android 保险密码效果,本文实例为大家分享了android仿支付宝密码输入框展示的具体代码,供大家参考,具体内容如下这个没什么好分析的,就是一些基本的绘制什么线,矩形什么的,看代码更...

    本文实例为大家分享了android仿支付宝密码输入框展示的具体代码,供大家参考,具体内容如下 这个没什么好分析的,就是一些基本的绘制什么线,矩形什么的,看代码更具体 布局文件: android:lay ...

  5. android支付宝支付微信支付封装,Android仿支付宝微信支付密码界面弹窗封装dialog...

    一,功能效果 二,实现过程 1,先写xml文件:dialog_keyboard.xml 注意事项 (1),密码部分用的是一个线性布局中6个TextView,并设置android:inputType=& ...

  6. Android 仿支付宝蚂蚁森林动画效果

    Android 动画可以归纳为以下几种: 视图动画(View 动画) 帧动画(Frame 动画.Drawable 动画) 属性动画 触摸反馈动画(Ripple Effect) 揭露动画(Reveal ...

  7. html输入支付密码样式,基于JS实现类似支付宝支付密码输入框

    基于JS实现类似支付宝支付密码输入框 2019-01-06 编程之家 https://www.jb51.cc 编程之家收集整理的这篇文章主要介绍了基于JS实现类似支付宝支付密码输入框,编程之家小编觉得 ...

  8. 仿支付宝支付成功控件

    仿支付宝支付控件(PathMeasure的应用) 本节介绍的是支付宝支付成功的特效实例,废话不多说,先上图再说: 思路 1.首先对于该效果的实现,我考虑的通过动画的绘制过程,从而截取每一段动画,让控件 ...

  9. android 余额宝收益列表,Android 仿支付宝中的余额宝收益进度条

    一. 看效果 二.上代码 package com.framework.widget; import android.app.Activity; import android.content.Conte ...

最新文章

  1. 博士后小姐姐把“二次元老婆生成器”升级了:这一次可以指定画风
  2. 吴恩达后,其钦点的百度研究院院长林元庆也离职筹备AI创业
  3. Unity3d 下websocket的使用
  4. Docker与虚拟机
  5. BackTrack5 安装中文输入法
  6. HP-UNIX操作系统root账号被锁定的两种解决方法
  7. 数组做参数_C语言进阶之路:函数—数组参数!
  8. 【1】TCP三次握手的第三次的 ack包丢失会怎样?
  9. Docker中搭建FastDFS文件系统(多图)
  10. 登陆代码 寻找更好的
  11. ultraiso软碟通dmg转iso图文教程
  12. FIT2CLOUD飞致云旗下多云管理平台完成华为FusionCompute兼容性测试
  13. (Java笔记)静态代理
  14. 弘辽科技:拼多多运营做好竞品分析。
  15. 【基于Pytorch的手写汉字识别】
  16. 【超级不爽】腾讯居然在我不知情未授权的情况下偷偷的转载我的博客文章
  17. NoClassDefFoundError: com/fasterxml/jackson/core/util/DefaultIndenter
  18. reg类型变量综合电路_Verilog中 reg和wire 用法和区别以及always和assign的区别
  19. Windows无法ping通百度IP问题
  20. C语言中用零作为终止标记,求出一批非零整数中的偶数.奇数的平均值,用零作为终止标记的程序...

热门文章

  1. Android中的SurfaceView详解
  2. Dreamweaver下载链接
  3. 定义全局变量的几种方式
  4. 苹果笔记本适合学python吗_MacBook Pro适合深度学习吗?
  5. 如何更好的提出问题,找解决方案
  6. 第三十二讲项目6-输出星号棱形(6)
  7. 永久60服务器消息,魔兽怀旧服:永久60级一夜之间玩家骤减,你会如何拯救这个服务器...
  8. QQ聊天记录的保存位置
  9. 《西方现代思想讲义》读书笔记
  10. 计算机国考整理知识点,通过计算机国考二级必须要知道的几个要点