亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android  View自定義鎖屏圖案

 更新時間:2018年06月04日 16:17:06   作者:星火燎原2016  
這篇文章主要為大家詳細(xì)介紹了Android View自定義鎖屏圖案,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

Android 自定義 View 技能是成為高級工程師所必備的,筆者覺得自定義 View 沒有什么捷徑可走,唯有經(jīng)常練習(xí)才能解決產(chǎn)品需求。筆者也好久沒有寫自定義 View 了,趕緊寫個控件找點感覺回來。

本文實現(xiàn)的是一個 鎖屏圖案的自定義控件。效果圖如下:

Github 地址:AndroidSample

LockView 介紹

自定義屬性:

引用方式:

(1) 在布局文件中引入

<com.xing.androidsample.view.LockView
  android:id="@+id/lock_view"
  app:rowCount="4"
  app:normalColor=""
  app:moveColor=""
  app:errorColor=""
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_margin="40dp" />

(2) 在代碼中設(shè)置正確的圖案,用于校驗是否匹配成功,并在回調(diào)中獲取結(jié)果

List<Integer> intList = new ArrayList<>();
  intList.add(3);
  intList.add(7);
  intList.add(4);
  intList.add(2);
  lockView.setStandard(intList);
  lockView.setOnDrawCompleteListener(new LockView.OnDrawCompleteListener() {
   @Override
   public void onComplete(boolean isSuccess) {
    Toast.makeText(CustomViewActivity.this, isSuccess ? "success" : "fail", Toast.LENGTH_SHORT).show();
   }
  });

實現(xiàn)思路

  1. 以默認(rèn)狀態(tài)繪制 rowCount * rowCount 個圓,外圓顏色需要在內(nèi)圓顏色上加上一定的透明度。
  2. 在 onTouchEvent() 方法中,判斷當(dāng)前觸摸點與各個圓的圓心距離是否小于圓的半徑,決定各個圓此時處于哪個狀態(tài)(normal,move,error),調(diào)用 invalidate() 重新繪制,更新顏色。
  3. ​將手指滑動觸摸過的圓的坐標(biāo)添加到一個 ArrayList 中,使用 Path 連接該集合中選中的圓,即可繪制出劃過的路徑線。

實現(xiàn)步驟

自定義屬性

在 res/values 目錄下新建 attrs.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="LockView">
  <attr name="normalColor" format="color|reference" /> <!--默認(rèn)圓顏色-->
  <attr name="moveColor" format="color|reference" />  <!--手指觸摸選中圓顏色--> 
  <attr name="errorColor" format="color|reference" />  <!--手指抬起錯誤圓顏色--> 
  <attr name="rowCount" format="integer" />    <!--每行每列圓的個數(shù)--> 
 </declare-styleable>
</resources>

獲取自定義屬性

public LockView(Context context) {
   this(context, null);
  }

  public LockView(Context context, @Nullable AttributeSet attrs) {
   super(context, attrs);
   readAttrs(context, attrs);
   init();
  }

 /**
 * 獲取自定義屬性
 */
  private void readAttrs(Context context, AttributeSet attrs) {
   TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LockView);
   normalColor = typedArray.getColor(R.styleable.LockView_normalColor, DEFAULT_NORMAL_COLOR);
   moveColor = typedArray.getColor(R.styleable.LockView_moveColor, DEFAULT_MOVE_COLOR);
   errorColor = typedArray.getColor(R.styleable.LockView_errorColor, DEFAULT_ERROR_COLOR);
   rowCount = typedArray.getInteger(R.styleable.LockView_rowCount, DEFAULT_ROW_COUNT);
   typedArray.recycle();
  }

 /**
 * 初始化
 */
  private void init() {
   stateSparseArray = new SparseIntArray(rowCount * rowCount);
   points = new PointF[rowCount * rowCount];

   innerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   innerCirclePaint.setStyle(Paint.Style.FILL);

   outerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   outerCirclePaint.setStyle(Paint.Style.FILL);

   linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   linePaint.setStyle(Paint.Style.STROKE);
   linePaint.setStrokeCap(Paint.Cap.ROUND);
   linePaint.setStrokeJoin(Paint.Join.ROUND);
   linePaint.setStrokeWidth(30);
   linePaint.setColor(moveColor);
  }

計算圓的半徑

設(shè)定外圓半徑和相鄰兩圓之間間距相同,內(nèi)圓半徑是外圓半徑的一半,所以半徑計算方式為:

radius = Math.min(w, h) / (2 * rowCount + rowCount - 1) * 1.0f;

設(shè)置各圓坐標(biāo)

各圓坐標(biāo)使用一維數(shù)組保存,計算方式為:

// 各個圓設(shè)置坐標(biāo)點
for (int i = 0; i < rowCount * rowCount; i++) {
  points[i] = new PointF(0, 0);
  points[i].set((i % rowCount * 3 + 1) * radius, (i / rowCount * 3 + 1) * radius);
}

測量 View 寬高

根據(jù)測量模式設(shè)置控件的寬高,當(dāng)布局文件中設(shè)置的是 wrap_content ,默認(rèn)將控件寬高設(shè)置為 600dp

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  int width = getSize(widthMeasureSpec);
  int height = getSize(heightMeasureSpec);
  setMeasuredDimension(width, height);
 }

 private int getSize(int measureSpec) {
  int mode = MeasureSpec.getMode(measureSpec);
  int size = MeasureSpec.getSize(measureSpec);
  if (mode == MeasureSpec.EXACTLY) {
   return size;
  } else if (mode == MeasureSpec.AT_MOST) {
   return Math.min(size, dp2Px(600));
  }
  return dp2Px(600);
 }

onTouchEvent() 觸摸事件

在手指滑動過程中,根據(jù)當(dāng)前觸摸點坐標(biāo)是否落在圓的范圍內(nèi),更新該圓的狀態(tài),在重新繪制時,繪制成新的顏色。手指抬起時,將存放狀態(tài)的 list,選中圓的 list ,linePath 重置,并將結(jié)果回調(diào)出來。

 

private PointF touchPoint;

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    reset();
   case MotionEvent.ACTION_MOVE:
    if (touchPoint == null) {
     touchPoint = new PointF(event.getX(), event.getY());
    } else {
     touchPoint.set(event.getX(), event.getY());
    }
    for (int i = 0; i < rowCount * rowCount; i++) {
     // 是否觸摸在圓的范圍內(nèi)
     if (getDistance(touchPoint, points[i]) < radius) {
      stateSparseArray.put(i, STATE_MOVE);
      if (!selectedList.contains(points[i])) {
       selectedList.add(points[i]);
      }
      break;
     }
    }
    break;
   case MotionEvent.ACTION_UP:
    if (check()) { // 正確圖案
     if (listener != null) {
      listener.onComplete(true);
     }
     for (int i = 0; i < stateSparseArray.size(); i++) {
      int index = stateSparseArray.keyAt(i);
      stateSparseArray.put(index, STATE_MOVE);
     }
    } else {  // 錯誤圖案
     for (int i = 0; i < stateSparseArray.size(); i++) {
      int index = stateSparseArray.keyAt(i);
      stateSparseArray.put(index, STATE_ERROR);
     }
     linePaint.setColor(0xeeff0000);
     if (listener != null) {
      listener.onComplete(false);
     }
    }
    touchPoint = null;
    if (timer == null) {
     timer = new Timer();
    }
    timer.schedule(new TimerTask() {
     @Override
     public void run() {
      linePath.reset();
      linePaint.setColor(0xee0000ff);
      selectedList.clear();
      stateSparseArray.clear();
      postInvalidate();
     }
    }, 1000);
    break;
  }
  invalidate();
  return true;
 }

繪制各圓和各圓之間連接線段

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

 private void drawCircle(Canvas canvas) {
  // 依次從索引 0 到索引 8,根據(jù)不同狀態(tài)繪制圓點
  for (int index = 0; index < rowCount * rowCount; index++) {
   int state = stateSparseArray.get(index);
   switch (state) {
    case STATE_NORMAL:
     innerCirclePaint.setColor(normalColor);
     outerCirclePaint.setColor(normalColor & 0x66ffffff);
     break;
    case STATE_MOVE:
     innerCirclePaint.setColor(moveColor);
     outerCirclePaint.setColor(moveColor & 0x66ffffff);
     break;
    case STATE_ERROR:
     innerCirclePaint.setColor(errorColor);
     outerCirclePaint.setColor(errorColor & 0x66ffffff);
     break;
   }
   canvas.drawCircle(points[index].x, points[index].y, radius, outerCirclePaint);
   canvas.drawCircle(points[index].x, points[index].y, radius / 2f, innerCirclePaint);
  }
 }

完整 View 代碼:

/**
 * Created by star.tao on 2018/5/30.
 * email: xing-java@foxmail.com
 * github: https://github.com/xing16
 */

public class LockView extends View {

 private static final int DEFAULT_NORMAL_COLOR = 0xee776666;
 private static final int DEFAULT_MOVE_COLOR = 0xee0000ff;
 private static final int DEFAULT_ERROR_COLOR = 0xeeff0000;
 private static final int DEFAULT_ROW_COUNT = 3;

 private static final int STATE_NORMAL = 0;
 private static final int STATE_MOVE = 1;
 private static final int STATE_ERROR = 2;


 private int normalColor; // 無滑動默認(rèn)顏色
 private int moveColor; // 滑動選中顏色
 private int errorColor; // 錯誤顏色

 private float radius; // 外圓半徑

 private int rowCount;

 private PointF[] points; // 一維數(shù)組記錄所有圓點的坐標(biāo)點

 private Paint innerCirclePaint; // 內(nèi)圓畫筆

 private Paint outerCirclePaint; // 外圓畫筆

 private SparseIntArray stateSparseArray;

 private List<PointF> selectedList = new ArrayList<>();

 private List<Integer> standardPointsIndexList = new ArrayList<>();

 private Path linePath = new Path(); // 手指移動的路徑

 private Paint linePaint;

 private Timer timer;

 public LockView(Context context) {
  this(context, null);
 }

 public LockView(Context context, @Nullable AttributeSet attrs) {
  super(context, attrs);
  readAttrs(context, attrs);
  init();
 }


 private void readAttrs(Context context, AttributeSet attrs) {
  TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LockView);
  normalColor = typedArray.getColor(R.styleable.LockView_normalColor, DEFAULT_NORMAL_COLOR);
  moveColor = typedArray.getColor(R.styleable.LockView_moveColor, DEFAULT_MOVE_COLOR);
  errorColor = typedArray.getColor(R.styleable.LockView_errorColor, DEFAULT_ERROR_COLOR);
  rowCount = typedArray.getInteger(R.styleable.LockView_rowCount, DEFAULT_ROW_COUNT);
  typedArray.recycle();
 }

 private void init() {
  stateSparseArray = new SparseIntArray(rowCount * rowCount);
  points = new PointF[rowCount * rowCount];

  innerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  innerCirclePaint.setStyle(Paint.Style.FILL);

  outerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  outerCirclePaint.setStyle(Paint.Style.FILL);


  linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  linePaint.setStyle(Paint.Style.STROKE);
  linePaint.setStrokeCap(Paint.Cap.ROUND);
  linePaint.setStrokeJoin(Paint.Join.ROUND);
  linePaint.setStrokeWidth(30);
  linePaint.setColor(moveColor);

 }


 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  // 外圓半徑 = 相鄰?fù)鈭A之間間距 = 2倍內(nèi)圓半徑
  radius = Math.min(w, h) / (2 * rowCount + rowCount - 1) * 1.0f;
  // 各個圓設(shè)置坐標(biāo)點
  for (int i = 0; i < rowCount * rowCount; i++) {
   points[i] = new PointF(0, 0);
   points[i].set((i % rowCount * 3 + 1) * radius, (i / rowCount * 3 + 1) * radius);
  }
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  int width = getSize(widthMeasureSpec);
  int height = getSize(heightMeasureSpec);
  setMeasuredDimension(width, height);
 }

 private int getSize(int measureSpec) {
  int mode = MeasureSpec.getMode(measureSpec);
  int size = MeasureSpec.getSize(measureSpec);
  if (mode == MeasureSpec.EXACTLY) {
   return size;
  } else if (mode == MeasureSpec.AT_MOST) {
   return Math.min(size, dp2Px(600));
  }
  return dp2Px(600);
 }


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

 private void drawCircle(Canvas canvas) {
  // 依次從索引 0 到索引 8,根據(jù)不同狀態(tài)繪制圓點
  for (int index = 0; index < rowCount * rowCount; index++) {
   int state = stateSparseArray.get(index);
   switch (state) {
    case STATE_NORMAL:
     innerCirclePaint.setColor(normalColor);
     outerCirclePaint.setColor(normalColor & 0x66ffffff);
     break;
    case STATE_MOVE:
     innerCirclePaint.setColor(moveColor);
     outerCirclePaint.setColor(moveColor & 0x66ffffff);
     break;
    case STATE_ERROR:
     innerCirclePaint.setColor(errorColor);
     outerCirclePaint.setColor(errorColor & 0x66ffffff);
     break;
   }
   canvas.drawCircle(points[index].x, points[index].y, radius, outerCirclePaint);
   canvas.drawCircle(points[index].x, points[index].y, radius / 2f, innerCirclePaint);
  }
 }

 /**
  * 繪制選中點之間相連的路徑
  *
  * @param canvas
  */
 private void drawLinePath(Canvas canvas) {
  // 重置linePath
  linePath.reset();
  // 選中點個數(shù)大于 0 時,才繪制連接線段
  if (selectedList.size() > 0) {
   // 起點移動到按下點位置
   linePath.moveTo(selectedList.get(0).x, selectedList.get(0).y);
   for (int i = 1; i < selectedList.size(); i++) {
    linePath.lineTo(selectedList.get(i).x, selectedList.get(i).y);
   }
   // 手指抬起時,touchPoint設(shè)置為null,使得已經(jīng)繪制游離的路徑,消失掉,
   if (touchPoint != null) {
    linePath.lineTo(touchPoint.x, touchPoint.y);
   }
   canvas.drawPath(linePath, linePaint);
  }
 }

 private PointF touchPoint;

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
   case MotionEvent.ACTION_DOWN:
    reset();
   case MotionEvent.ACTION_MOVE:
    if (touchPoint == null) {
     touchPoint = new PointF(event.getX(), event.getY());
    } else {
     touchPoint.set(event.getX(), event.getY());
    }
    for (int i = 0; i < rowCount * rowCount; i++) {
     // 是否觸摸在圓的范圍內(nèi)
     if (getDistance(touchPoint, points[i]) < radius) {
      stateSparseArray.put(i, STATE_MOVE);
      if (!selectedList.contains(points[i])) {
       selectedList.add(points[i]);
      }
      break;
     }
    }
    break;
   case MotionEvent.ACTION_UP:
    if (check()) { // 正確圖案
     if (listener != null) {
      listener.onComplete(true);
     }
     for (int i = 0; i < stateSparseArray.size(); i++) {
      int index = stateSparseArray.keyAt(i);
      stateSparseArray.put(index, STATE_MOVE);
     }
    } else {  // 錯誤圖案
     for (int i = 0; i < stateSparseArray.size(); i++) {
      int index = stateSparseArray.keyAt(i);
      stateSparseArray.put(index, STATE_ERROR);
     }
     linePaint.setColor(0xeeff0000);
     if (listener != null) {
      listener.onComplete(false);
     }
    }
    touchPoint = null;
    if (timer == null) {
     timer = new Timer();
    }
    timer.schedule(new TimerTask() {
     @Override
     public void run() {
      linePath.reset();
      linePaint.setColor(0xee0000ff);
      selectedList.clear();
      stateSparseArray.clear();
      postInvalidate();
     }
    }, 1000);
    break;
  }
  invalidate();
  return true;
 }

 /**
  * 清除繪制圖案的條件,當(dāng)觸發(fā) invalidate() 時將清空圖案
  */
 private void reset() {
  touchPoint = null;
  linePath.reset();
  linePaint.setColor(0xee0000ff);
  selectedList.clear();
  stateSparseArray.clear();
 }


 public void onStop() {
  timer.cancel();
 }

 private boolean check() {
  if (selectedList.size() != standardPointsIndexList.size()) {
   return false;
  }
  for (int i = 0; i < standardPointsIndexList.size(); i++) {
   Integer index = standardPointsIndexList.get(i);
   if (points[index] != selectedList.get(i)) {
    return false;
   }
  }
  return true;
 }

 public void setStandard(List<Integer> pointsList) {
  if (pointsList == null) {
   throw new IllegalArgumentException("standard points index can't null");
  }
  if (pointsList.size() > rowCount * rowCount) {
   throw new IllegalArgumentException("standard points index list can't large to rowcount * columncount");
  }
  standardPointsIndexList = pointsList;
 }

 private OnDrawCompleteListener listener;

 public void setOnDrawCompleteListener(OnDrawCompleteListener listener) {
  this.listener = listener;
 }


 public interface OnDrawCompleteListener {
  void onComplete(boolean isSuccess);
 }


 private float getDistance(PointF centerPoint, PointF downPoint) {
  return (float) Math.sqrt(Math.pow(centerPoint.x - downPoint.x, 2) + Math.pow(centerPoint.y - downPoint.y, 2));

 }

 private int dp2Px(int dpValue) {
  return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
 }

}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Kotlin?this關(guān)鍵字的使用實例詳解

    Kotlin?this關(guān)鍵字的使用實例詳解

    這篇文章主要介紹了Kotlin?this關(guān)鍵字的使用實例,在Kotlin中,this關(guān)鍵字允許我們引用一個類的實例,該類的函數(shù)恰好正在運行。此外,還有其他方式可以使this表達(dá)式派上用場
    2023-02-02
  • Android BroadcastReceiver常見監(jiān)聽整理

    Android BroadcastReceiver常見監(jiān)聽整理

    這篇文章主要介紹了Android BroadcastReceiver常見監(jiān)聽整理的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • Android應(yīng)用實現(xiàn)點擊按鈕震動

    Android應(yīng)用實現(xiàn)點擊按鈕震動

    這篇文章主要為大家詳細(xì)介紹了Android應(yīng)用實現(xiàn)點擊按鈕震動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Android 生命周期架構(gòu)組件使用方法

    Android 生命周期架構(gòu)組件使用方法

    這篇文章主要介紹了 Android 生命周期架構(gòu)組件的使用方法,需要的朋友可以參考下
    2018-02-02
  • Android實現(xiàn)消水果游戲代碼分享

    Android實現(xiàn)消水果游戲代碼分享

    本文給大家分享實現(xiàn)開心消水果游戲的核心代碼,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的朋友一起看看吧
    2016-11-11
  • 仿網(wǎng)易新聞客戶端頭條ViewPager嵌套實例

    仿網(wǎng)易新聞客戶端頭條ViewPager嵌套實例

    正確使用requestDisallowInterceptTouchEvent(boolean flag)方法,下面為大家介紹下外層ViewPager布局的實例,感興趣的朋友可以參考下哈
    2013-06-06
  • Emoji表情在Android JNI中的兼容性問題詳解

    Emoji表情在Android JNI中的兼容性問題詳解

    這篇文章主要給大家介紹了關(guān)于Emoji表情在Android JNI中的兼容性問題,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Android JNI具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Android開發(fā)實現(xiàn)刪除聯(lián)系人通話記錄的方法

    Android開發(fā)實現(xiàn)刪除聯(lián)系人通話記錄的方法

    這篇文章主要介紹了Android開發(fā)實現(xiàn)刪除聯(lián)系人通話記錄的方法,較為詳細(xì)的分析了Android刪除通話記錄的原理、步驟與相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2016-10-10
  • 利用Android畫圓弧canvas.drawArc()實例詳解

    利用Android畫圓弧canvas.drawArc()實例詳解

    這篇文章主要給大家介紹了關(guān)于利用Android畫圓弧canvas.drawArc()的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的理解和學(xué)習(xí)具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Android自定義View實現(xiàn)字母導(dǎo)航欄

    Android自定義View實現(xiàn)字母導(dǎo)航欄

    通常手機通訊錄都會有索引欄,這篇文章主要介紹了Android自定義View實現(xiàn)字母導(dǎo)航欄,現(xiàn)在分享給大家。
    2016-10-10

最新評論