Android自定义View实现数字密码锁

最近项目上用到一个密码加锁功能,需要一个数字密码界面,就想着封装成一个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构造方法中初始化了圆圆和数字的颜色风格,以及空心圆的边界粗细大小,可根据需求自行更改。

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

(0)

相关推荐

  • Android利用手势完成屏幕密码锁功能

    本文实例为大家分享了Android画笔屏幕锁小程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 1.如果使用GestureOverlayView,在xml配置文件中使用Android.gesture.GestureOverlayView,否则会报ClassNotFoundException 2.关于判断MEDIA_MOUNTED,API的解释:getExternalStorageState() returns MEDIA_MOUNTED if the media ispresent and

  • Android自定义View实现数字密码锁

    最近项目上用到一个密码加锁功能,需要一个数字密码界面,就想着封装成一个View来方便管理和使用. 废话不多说,先上最终效果图: 思路 整体可分为2个部分来实现,1.顶部是4个密码位的填充:2.数字键盘部分.整体可以是一个纵向LinearLayout,4个密码位用横向LinearLayout即可,键盘由于是宫格形式,因此可用GridLayout来布局.由于密码位和键盘数字都是以圆圈为背景,这里采用自定义一个圆形背景ImageView来使用. 实现 1.页面布局 首先定义一个圆形背景的ImageVi

  • Android自定义View实现数字雨效果的全过程

    目录 效果图 实现步骤 总结 效果图 在安卓中多种类型的动画,有帧动画.补间动画.属性动画,除此之外,使用自定义的View结合数学公式,就可以绘制出复杂的界面或者动画.这篇文章记录的是仿照黑客帝国的数字雨,来看看效果吧. 实现步骤 准备工作,常量的配置信息 // 文字的颜色值 final int DEFAULT_TEXT_COLOR = Color.argb(255, 0, 255, 70); // 文字大小 final int TEXT_SIZE = 24; // 普通画笔 Paint mPa

  • android自定义view实现数字进度条

    之前看到过一个数字进度条,一直想写,今天就把这个实现下,想起来也是很简单的,先看下实现的效果: 思路: 绘制2根线 绘制进度条的文字,不断的改变起点和终点,然后没多少时间去更新UI就ok,在这就不画图了,看代码就看的明白,不要想的很复杂! package com.tuya; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import a

  • Android自定义View实现带数字的进度条实例代码

    第一步.效果展示 图1.蓝色的进度条 图2.红色的进度条 图3.多条颜色不同的进度条 图4.多条颜色不同的进度条 第二步.自定义ProgressBar实现带数字的进度条 0.项目结构 如上图所示:library项目为自定义的带数字的进度条NumberProgressBar的具体实现,demo项目为示例项目以工程依赖的方式引用library项目,然后使用自定义的带数字的进度条NumberProgressBar来做展示 如上图所示:自定义的带数字的进度条的library项目的结构图 如上图所示:de

  • Android自定义View实现圆环带数字百分比进度条

    分享一个自己制作的Android自定义View.是一个圆环形状的反映真实进度的进度条,百分比的进度文字跟随已完成进度的圆弧转动.以下是效果图: 这个自定义View可以根据需要设定圆环的宽度和百分比文字的大小. 先说一下思路:这个View一共分为三部分:第一部分也就是灰色的圆环部分,代表未完成的进度:第二部分是蓝色的圆弧部分,代表已经完成的进度:第三部分是红色的百分比的数字百分比文本,显示当前确切的完成进度. 下面是View的编写思路: ①:定义三个画笔,分别画灰色圆环,蓝色圆弧,红色文字: ②:

  • Android自定义View模仿即刻点赞数字切换效果实例

    目录 即刻点赞展示 自己如何实现这种数字切换呢? 效果展示 源码 总结 即刻点赞展示 点赞的数字增加和减少并不是整个替换,而是差异化替换.再加上动画效果就看的很舒服. 自己如何实现这种数字切换呢? 下面用一张图来展示我的思路: 现在只需要根据这张图,写出对应的动画即可. 分为2种场景: 数字+1: 差异化的数字从3号区域由渐变动画(透明度 0- 255) + 偏移动画 (3号区域绘制文字的基线,2号区域绘制文字的基线),将数字移动到2号位置处 差异化的数字从2号区域由渐变动画(透明度 255-

  • Android 自定义View 密码框实例代码

    暴露您view中所有影响可见外观的属性或者行为. •通过XML添加和设置样式 •通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 效果图展示: 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="borde

  • Android自定义View实现等级滑动条的实例

     Android自定义View实现等级滑动条的实例 实现效果图: 思路: 首先绘制直线,然后等分直线绘制点: 绘制点的时候把X值存到集合中. 然后绘制背景图片,以及图片上的数字. 点击事件down的时候,换小图片为大图片.move的时候跟随手指移动. up的时候根据此时的X计算最近的集合中的点,然后自动吸附回去. 1,自定义属性 <?xml version="1.0" encoding="utf-8"?> <resources> <de

  • Android自定义View仿支付宝输入六位密码功能

    跟选择银行卡界面类似,也是用一个PopupWindow,不过输入密码界面是一个自定义view,当输入六位密码完成后用回调在Activity中获取到输入的密码并以Toast显示密码.效果图如下: 自定义view布局效果图及代码如下: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/

  • Android自定义view制作绚丽的验证码

    废话不多说了,先给大家展示下自定义view效果图,如果大家觉得还不错的话,请继续往下阅读. 怎么样,这种验证码是不是很常见呢,下面我们就自己动手实现这种效果,自己动手,丰衣足食,哈哈~ 一. 自定义view的步骤 自定义view一直被认为android进阶通向高手的必经之路,其实自定义view好简单,自定义view真正难的是如何绘制出高难度的图形,这需要有好的数学功底(后悔没有好好学数学了~),因为绘制图形经常要计算坐标点及类似的几何变换等等.自定义view通常只需要以下几个步骤: 写一个类继承

随机推荐