溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發(fā)布時間:2020-08-23 09:07:44 來源:腳本之家 閱讀:226 作者:IT_ZJYANG 欄目:移動開發(fā)

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

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

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

思路

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

實現

1.頁面布局

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

/** 
 * 圓形背景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); 
   } 
 
   /** 
   * 設置圓為實心狀態(tài) 
   */ 
   public void setFillCircle(){ 
     mPaint.setStyle(Paint.Style.FILL); 
     invalidate(); 
   } 
 
   /** 
   * 設置圓為空心狀態(tài) 
   */ 
   public void setStrokeCircle(){ 
     mPaint.setStyle(Paint.Style.STROKE); 
     invalidate(); 
   } 
 } 

可以看到,在onDraw中繪制了一個圓,默認為空心狀態(tài),定義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。

由于點擊數字按下的時候填充,松開的時候為空心狀態(tài),所以可以在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位,如果沒超過,就繼續(xù)追加。如果等于4,就說明輸入完成,此時的mPassWord的內容就是最終的密碼,可以用一個接口將其回調出去方便Activity中獲取輸入的密碼:

/** 
 * 監(jiān)聽輸入完畢的接口 
 */ 
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個密碼位左右擺動并且手機震動效果,震動結束之后,當前存儲的密碼位重置為初始狀態(tài),如下:

/** 
 * 輸入錯誤的狀態(tài)顯示(包括震動,密碼位左右搖擺效果,重置密碼位) 
 */ 
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); 
  } 
 
  /** 
   * 輸入錯誤的狀態(tài)顯示(包括震動,密碼位左右搖擺效果,重置密碼位) 
   */ 
  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); 
  } 
 
  /** 
   * 監(jiān)聽輸入完畢的接口 
   */ 
  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); 
    } 
 
    /** 
     * 設置圓為實心狀態(tài) 
     */ 
    public void setFillCircle(){ 
      mPaint.setStyle(Paint.Style.FILL); 
      invalidate(); 
    } 
 
    /** 
     * 設置圓為空心狀態(tài) 
     */ 
    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> 

在代碼中監(jiān)聽輸入的密碼結果:

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構造方法中初始化了圓圓和數字的顏色風格,以及空心圓的邊界粗細大小,可根據需求自行更改。

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

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI