国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android自定義View實現數字密碼鎖

2019-10-22 18:10:10
字體:
來源:轉載
供稿:網友

最近項目上用到一個密碼加鎖功能,需要一個數字密碼界面,就想著封裝成一個View來方便管理和使用。

廢話不多說,先上最終效果圖:

Android,View,數字密碼鎖

思路

整體可分為2個部分來實現,1.頂部是4個密碼位的填充;2.數字鍵盤部分。整體可以是一個縱向LinearLayout,4個密碼位用橫向LinearLayout即可,鍵盤由于是宮格形式,因此可用GridLayout來布局。由于密碼位和鍵盤數字都是以圓圈為背景,這里采用自定義一個圓形背景ImageView來使用。

實現

1.頁面布局

首先定義一個圓形背景的ImageView,由于最終實現的效果是點擊的時候要填充圓背景,非點擊狀態下是空心圓,因此可通過改變Paint的style來動態更改顯示:

/**  * 圓形背景ImageView(設置實心或空心)  */  public class CircleImageView extends ImageView{     private Paint mPaint;    private int mWidth;    private int mHeight;     public CircleImageView(Context context) {      this(context, null);    }     public CircleImageView(Context context, AttributeSet attrs) {      this(context, attrs, 0);    }     public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {      super(context, attrs, defStyleAttr);      initView(context);    }     public void initView(Context context){      mPaint = new Paint();      mPaint.setStyle(Paint.Style.STROKE);      mPaint.setColor(mPanelColor);      mPaint.setStrokeWidth(mStrokeWidth);      mPaint.setAntiAlias(true);    }     @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {      super.onSizeChanged(w, h, oldw, oldh);      mWidth = w;      mHeight = h;    }     @Override    public void draw(Canvas canvas) {      canvas.drawCircle(mWidth/2, mHeight/2, mWidth/2 - 6, mPaint);      super.draw(canvas);    }     /**    * 設置圓為實心狀態    */    public void setFillCircle(){      mPaint.setStyle(Paint.Style.FILL);      invalidate();    }     /**    * 設置圓為空心狀態    */    public void setStrokeCircle(){      mPaint.setStyle(Paint.Style.STROKE);      invalidate();    }  } 

可以看到,在onDraw中繪制了一個圓,默認為空心狀態,定義setFillCircle和setStrokeCircle這兩個方法以便外界可以方便地切換圓為實心或者空心。

圓形ImageView定義好了,開始添加密碼位,布局如下:

inputResultView = new LinearLayout(context); for(int i=0; i<4; i++){    CircleImageView mResultItem = new CircleImageView(context);    mResultIvList.add(mResultItem);    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mResultIvRadius, mResultIvRadius);    params.leftMargin = dip2px(context, 4);    params.rightMargin = dip2px(context, 4);    mResultItem.setPadding(dip2px(context, 2),dip2px(context, 2),dip2px(context, 2),dip2px(context, 2));    mResultItem.setLayoutParams(params);    inputResultView.addView(mResultItem); } LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.gravity = Gravity.CENTER_HORIZONTAL; params.bottomMargin = dip2px(context, 34); inputResultView.setLayoutParams(params); addView(inputResultView); 

接著添加數字鍵盤部分的布局:

GridLayout numContainer = new GridLayout(context); numContainer.setColumnCount(3); for(int i=0; i<numArr.length; i++){   RelativeLayout numItem = new RelativeLayout(context);   numItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom);   RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius);   gridItemParams.addRule(CENTER_IN_PARENT);   final TextView numTv = new TextView(context);   numTv.setText(numArr[i]);   numTv.setTextColor(mPanelColor);   numTv.setTextSize(30);   numTv.setGravity(Gravity.CENTER);   numTv.setLayoutParams(gridItemParams);   final CircleImageView numBgIv = new CircleImageView(context);   numBgIv.setLayoutParams(gridItemParams);   numItem.addView(numBgIv);   numItem.addView(numTv);   numContainer.addView(numItem);   if(i == 9){     numItem.setVisibility(INVISIBLE);   } }  //刪除按鈕 RelativeLayout deleteItem = new RelativeLayout(context); deleteItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom); RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius); gridItemParams.addRule(CENTER_IN_PARENT); //假如刪除按鈕是設置自定義圖片資源的話,可用注釋這段 //ImageView deleteIv = new ImageView(context); //deleteIv.setImageResource(R.drawable.icn_delete_pw); //deleteIv.setLayoutParams(gridItemParams); //deleteItem.addView(deleteIv); TextView deleteTv = new TextView(context); deleteTv.setText("Delete"); deleteTv.setTextColor(mPanelColor); deleteTv.setTextSize(dip2px(context, 8)); deleteTv.setLayoutParams(gridItemParams); deleteTv.setGravity(Gravity.CENTER); deleteItem.addView(deleteTv); numContainer.addView(deleteItem);  LinearLayout.LayoutParams gridParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); gridParams.gravity = Gravity.CENTER_HORIZONTAL; numContainer.setLayoutParams(gridParams); addView(numContainer); 

數字鍵盤這里用一個數組存數字內容,遍歷添加,注意此處由于第10個的子View的時候是空白的,所以當遍歷到第10個元素的時候,可以將其隱藏。遍歷完后再單獨添加刪除按鈕。

2.輸入邏輯

頁面布局完成了,接下來就是密碼輸入的邏輯部分,最終的效果是每點擊一次數字,密碼位就填充一個,每點擊刪除按鈕一次,密碼位就回退一個,輸入4個數字之后,即完成輸入,獲取結果,并重置密碼位。這里用一個StringBuilder變量來記錄當前已輸入的密碼,每次添加就append進去,每次刪除就調用deleteCharAt。

由于點擊數字按下的時候填充,松開的時候為空心狀態,所以可以在ACTION_DOWN和ACTION_UP事件中分別操作:

numTv.setOnTouchListener(new OnTouchListener() {     @Override     public boolean onTouch(View v, MotionEvent event) {       switch (event.getAction()){         case MotionEvent.ACTION_DOWN:           numBgIv.setFillCircle();           numTv.setTextColor(Color.WHITE);           if(mPassWord.length() < 4){             mPassWord.append(numTv.getText());             mResultIvList.get(mPassWord.length()-1).setFillCircle();             if(mInputListener!=null && mPassWord.length() == 4){               //已完整輸入4個             }           }           break;         case MotionEvent.ACTION_UP:           numBgIv.setStrokeCircle();           numTv.setTextColor(mPanelColor);           break;       }       return true;     }  }); 

每次點擊的時候,判斷當前已輸入的密碼位是否已經超過4位,如果沒超過,就繼續追加。如果等于4,就說明輸入完成,此時的mPassWord的內容就是最終的密碼,可以用一個接口將其回調出去方便Activity中獲取輸入的密碼:

/**  * 監聽輸入完畢的接口  */ private InputListener mInputListener;  public void setInputListener(InputListener mInputListener) {   his.mInputListener = mInputListener; }  public interface InputListener{   void inputFinish(String result); } 

然后在上面的ACTION_DOWN中輸入數字等于4的時候,回調該接口:

if(mInputListener!=null && mPassWord.length() == 4){    mInputListener.inputFinish(mPassWord.toString()); } 

另外,刪除的操作單獨封裝為一個方法:

/**  * 刪除  */ public void delete(){   if(mPassWord.length() == 0){     return;   }   mResultIvList.get(mPassWord.length()-1).setStrokeCircle();   mPassWord.deleteCharAt(mPassWord.length()-1); } 

注意點:當前無輸入密碼時,直接return不作任何操作,假如已有輸入數字,就刪除最尾部的那個數字。

最后,還要考慮一種情況,即用戶輸入密碼錯誤時的一些反饋,參照平時的習慣,一般是4個密碼位左右擺動并且手機震動效果,震動結束之后,當前存儲的密碼位重置為初始狀態,如下:

/**  * 輸入錯誤的狀態顯示(包括震動,密碼位左右搖擺效果,重置密碼位)  */ public void showErrorStatus(){   mVibrator.vibrate(new long[]{100,100,100,100},-1);   List<Animator> animators = new ArrayList<>();   ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(inputResultView, "translationX", -50.0f,50.0f,-50.0f,0.0f);   translationXAnim.setDuration(400);   animators.add(translationXAnim);   AnimatorSet btnSexAnimatorSet = new AnimatorSet();   btnSexAnimatorSet.playTogether(animators);   btnSexAnimatorSet.start();   btnSexAnimatorSet.addListener(new Animator.AnimatorListener() {     @Override     public void onAnimationStart(Animator animation) {      }      @Override     public void onAnimationEnd(Animator animation) {       resetResult();     }      @Override     public void onAnimationCancel(Animator animation) {      }      @Override     public void onAnimationRepeat(Animator animation) {      }   }); } 

可以看到,在onAnimationEnd中調用了resetResult,即動畫結束時重置密碼,resetResult方法如下:

/**  * 重置密碼輸入  */ public void resetResult(){   for(int i=0; i<mResultIvList.size(); i++){     mResultIvList.get(i).setStrokeCircle();   }   mPassWord.delete(0, 4); } 

遍歷所有密碼位View設置為空心,并且刪除當前mPassWord變量存儲的所有內容。

完整代碼

完整的自定義數字密碼鎖代碼如下:

 

package com.example.zjyang.viewtest.view;  import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Service; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Vibrator; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.GridLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView;  import java.util.ArrayList; import java.util.List;  import static android.widget.RelativeLayout.CENTER_HORIZONTAL; import static android.widget.RelativeLayout.CENTER_IN_PARENT;  /**  * Created by IT_ZJYANG on 2018/1/22.  * 數字解鎖鍵盤View  */  public class NumLockPanel extends LinearLayout {    private String[] numArr = new String[]{"1","2","3","4","5","6","7","8","9", "", "0"};    private int mPaddingLeftRight;   private int mPaddingTopBottom;   //4個密碼位ImageView   private ArrayList<CircleImageView> mResultIvList;    private LinearLayout inputResultView;   //存儲當前輸入內容   private StringBuilder mPassWord;   //振動效果   private Vibrator mVibrator;   //整個鍵盤的顏色   private int mPanelColor;   //4個密碼位的寬度   private int mResultIvRadius;   //數字鍵盤的每個圓的寬度   private int mNumRadius;   //每個圓的邊界寬度   private int mStrokeWidth;    public NumLockPanel(Context context) {     this(context, null);   }    public NumLockPanel(Context context, AttributeSet attrs) {     this(context, attrs, 0);   }    public NumLockPanel(Context context, AttributeSet attrs, int defStyleAttr) {     super(context, attrs, defStyleAttr);     mPaddingLeftRight = dip2px(context, 21);     mPaddingTopBottom = dip2px(context, 10);     mPanelColor = Color.BLACK; //顏色代碼可采用Color.parse("#000000");     mResultIvRadius = dip2px(context, 20);     mNumRadius = dip2px(context, 66);     mStrokeWidth = dip2px(context, 2);     mVibrator = (Vibrator)context.getSystemService(Service.VIBRATOR_SERVICE);     mResultIvList = new ArrayList<>();     mPassWord = new StringBuilder();     setOrientation(VERTICAL);     setGravity(CENTER_HORIZONTAL);     initView(context);   }    public void initView(Context context){     //4個結果號碼     inputResultView = new LinearLayout(context);     for(int i=0; i<4; i++){       CircleImageView mResultItem = new CircleImageView(context);       mResultIvList.add(mResultItem);       LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mResultIvRadius, mResultIvRadius);       params.leftMargin = dip2px(context, 4);       params.rightMargin = dip2px(context, 4);       mResultItem.setPadding(dip2px(context, 2),dip2px(context, 2),dip2px(context, 2),dip2px(context, 2));       mResultItem.setLayoutParams(params);       inputResultView.addView(mResultItem);     }     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);     params.gravity = Gravity.CENTER_HORIZONTAL;     params.bottomMargin = dip2px(context, 34);     inputResultView.setLayoutParams(params);     addView(inputResultView);      //數字鍵盤     GridLayout numContainer = new GridLayout(context);     numContainer.setColumnCount(3);     for(int i=0; i<numArr.length; i++){       RelativeLayout numItem = new RelativeLayout(context);       numItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom);       RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius);       gridItemParams.addRule(CENTER_IN_PARENT);       final TextView numTv = new TextView(context);       numTv.setText(numArr[i]);       numTv.setTextColor(mPanelColor);       numTv.setTextSize(30);       numTv.setGravity(Gravity.CENTER);       numTv.setLayoutParams(gridItemParams);       final CircleImageView numBgIv = new CircleImageView(context);       numBgIv.setLayoutParams(gridItemParams);       numTv.setOnTouchListener(new OnTouchListener() {         @Override         public boolean onTouch(View v, MotionEvent event) {           switch (event.getAction()){             case MotionEvent.ACTION_DOWN:               numBgIv.setFillCircle();               numTv.setTextColor(Color.WHITE);               if(mPassWord.length() < 4){                 mPassWord.append(numTv.getText());                 mResultIvList.get(mPassWord.length()-1).setFillCircle();                 if(mInputListener!=null && mPassWord.length() == 4){                   mInputListener.inputFinish(mPassWord.toString());                 }               }               break;             case MotionEvent.ACTION_UP:               numBgIv.setStrokeCircle();               numTv.setTextColor(mPanelColor);               break;           }           return true;         }       });        numItem.addView(numBgIv);       numItem.addView(numTv);       numContainer.addView(numItem);       if(i == 9){         numItem.setVisibility(INVISIBLE);       }     }      //刪除按鈕     RelativeLayout deleteItem = new RelativeLayout(context);     deleteItem.setPadding(mPaddingLeftRight,mPaddingTopBottom,mPaddingLeftRight,mPaddingTopBottom);     RelativeLayout.LayoutParams gridItemParams = new RelativeLayout.LayoutParams(mNumRadius, mNumRadius);     gridItemParams.addRule(CENTER_IN_PARENT);     //假如刪除按鈕是設置自定義圖片資源的話,可用注釋這段     //ImageView deleteIv = new ImageView(context);     //deleteIv.setImageResource(R.drawable.icn_delete_pw);     //deleteIv.setLayoutParams(gridItemParams);     //deleteItem.addView(deleteIv);     TextView deleteTv = new TextView(context);     deleteTv.setText("Delete");     deleteTv.setTextColor(mPanelColor);     deleteTv.setTextSize(dip2px(context, 8));     deleteTv.setLayoutParams(gridItemParams);     deleteTv.setGravity(Gravity.CENTER);     deleteItem.addView(deleteTv);     numContainer.addView(deleteItem);     deleteTv.setOnClickListener(new OnClickListener() {       @Override       public void onClick(View v) {         delete();       }     });      LinearLayout.LayoutParams gridParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);     gridParams.gravity = Gravity.CENTER_HORIZONTAL;     numContainer.setLayoutParams(gridParams);     addView(numContainer);   }    /**    * 輸入錯誤的狀態顯示(包括震動,密碼位左右搖擺效果,重置密碼位)    */   public void showErrorStatus(){     mVibrator.vibrate(new long[]{100,100,100,100},-1);     List<Animator> animators = new ArrayList<>();     ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(inputResultView, "translationX", -50.0f,50.0f,-50.0f,0.0f);     translationXAnim.setDuration(400);     animators.add(translationXAnim);     AnimatorSet btnSexAnimatorSet = new AnimatorSet();     btnSexAnimatorSet.playTogether(animators);     btnSexAnimatorSet.start();     btnSexAnimatorSet.addListener(new Animator.AnimatorListener() {       @Override       public void onAnimationStart(Animator animation) {        }        @Override       public void onAnimationEnd(Animator animation) {         resetResult();       }        @Override       public void onAnimationCancel(Animator animation) {        }        @Override       public void onAnimationRepeat(Animator animation) {        }     });   }    /**    * 刪除    */   public void delete(){     if(mPassWord.length() == 0){       return;     }     mResultIvList.get(mPassWord.length()-1).setStrokeCircle();     mPassWord.deleteCharAt(mPassWord.length()-1);   }    /**    * 重置密碼輸入    */   public void resetResult(){     for(int i=0; i<mResultIvList.size(); i++){       mResultIvList.get(i).setStrokeCircle();     }     mPassWord.delete(0, 4);   }    /**    * 監聽輸入完畢的接口    */   private InputListener mInputListener;    public void setInputListener(InputListener mInputListener) {     this.mInputListener = mInputListener;   }    public interface InputListener{     void inputFinish(String result);   }     /**    * dip/dp轉像素    *    * @param dipValue    *      dip或 dp大小    * @return 像素值    */   public static int dip2px(Context context, float dipValue) {     DisplayMetrics metrics = context.getResources().getDisplayMetrics();     return (int) (dipValue * (metrics.density) + 0.5f);   }    /**    * 圓形背景ImageView(設置實心或空心)    */   public class CircleImageView extends ImageView{      private Paint mPaint;     private int mWidth;     private int mHeight;      public CircleImageView(Context context) {       this(context, null);     }      public CircleImageView(Context context, AttributeSet attrs) {       this(context, attrs, 0);     }      public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {       super(context, attrs, defStyleAttr);       initView(context);     }      public void initView(Context context){       mPaint = new Paint();       mPaint.setStyle(Paint.Style.STROKE);       mPaint.setColor(mPanelColor);       mPaint.setStrokeWidth(mStrokeWidth);       mPaint.setAntiAlias(true);     }      @Override     protected void onSizeChanged(int w, int h, int oldw, int oldh) {       super.onSizeChanged(w, h, oldw, oldh);       mWidth = w;       mHeight = h;     }      @Override     public void draw(Canvas canvas) {       canvas.drawCircle(mWidth/2, mHeight/2, mWidth/2 - 6, mPaint);       super.draw(canvas);     }      /**      * 設置圓為實心狀態      */     public void setFillCircle(){       mPaint.setStyle(Paint.Style.FILL);       invalidate();     }      /**      * 設置圓為空心狀態      */     public void setStrokeCircle(){       mPaint.setStyle(Paint.Style.STROKE);       invalidate();     }   } } 

使用

在Activity的布局文件中:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   xmlns:tools="http://schemas.android.com/tools"   android:id="@+id/activity_main"   android:layout_width="match_parent"   android:layout_height="match_parent"   android:background="#ffffff"   tools:context="com.example.zjyang.viewtest.MainActivity">    <com.example.zjyang.viewtest.view.NumLockPanel     android:id="@+id/num_lock"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_marginTop="30dp">    </com.example.zjyang.viewtest.view.NumLockPanel> </RelativeLayout> 

在代碼中監聽輸入的密碼結果:

public class MainActivity extends AppCompatActivity {      private NumLockPanel mNumLockPanel;    @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);      mNumLockPanel = (NumLockPanel) findViewById(R.id.num_lock);     mNumLockPanel.setInputListener(new NumLockPanel.InputListener() {       @Override       public void inputFinish(String result) {         //此處result即為輸入結果         Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();         //錯誤效果示例         mNumLockPanel.showErrorStatus();       }     });   } } 

最后,在自定義View構造方法中初始化了圓圓和數字的顏色風格,以及空心圓的邊界粗細大小,可根據需求自行更改。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 阿拉尔市| 宁陵县| 河西区| 南溪县| 汝阳县| 庄河市| 静安区| 洛南县| 商都县| 增城市| 台州市| 阳江市| 新蔡县| 新竹市| 南华县| 龙胜| 陆河县| 象山县| 峨边| 沛县| 大港区| 咸丰县| 陇西县| 明溪县| 韶关市| 龙游县| 清流县| 富裕县| 荆门市| 交口县| 铜山县| 广西| 平利县| 舟曲县| 固镇县| 武隆县| 普洱| 西藏| 那曲县| 遂川县| 长泰县|