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

首頁 > 系統 > Android > 正文

Android手勢密碼view學習筆記(二)

2019-12-12 03:32:52
字體:
來源:轉載
供稿:網友

我們還是接著我們上一篇博客中的內容往下講哈,上一節 Android手勢密碼view筆記(一)我們已經實現了我們的IndicatorView指示器view了:

下面我們來實現下我們的手勢密碼view:

實現思路:

1、我們照樣需要拿到用戶需要顯示的一些屬性(行、列、選中的圖片、未選中的圖片、錯誤顯示的圖片、連接線的寬度跟顏色......)。

2、我們需要根據手勢的變換然后需要判斷當前手指位置是不是在某個點中,在的話就把該點設置為選中狀態,然后每移動到兩個點(也就是一個線段)就記錄該兩個點。

3、最后把記錄的所有點(所有線段)畫在canvas上,并記錄每個點對應的num值(也就是我們設置的密碼)。

4、當手指抬起的時候,執行回調方法,把封裝的密碼集合傳給調用著。
好啦~ 既然右了思路,我們就來實現下:

首先是定義一個attrs.xml文件(為了方便,我就直接在上一篇博客中實現的indicatorview的attr里面繼續往下定義了):

<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="IndicatorView">  <!--默認狀態的drawable-->  <attr name="normalDrawable" format="reference" />  <!--被選中狀態的drawable-->  <attr name="selectedDrawable" format="reference" />  <!--列數-->  <attr name="column" format="integer" />  <!--行數-->  <attr name="row" format="integer" />  <!--錯誤狀態的drawabe-->  <attr name="erroDrawable" format="reference" />  <!--padding值,padding值越大點越小-->  <attr name="padding" format="dimension" />  <!--默認連接線顏色-->  <attr name="normalStrokeColor" format="color" />  <!--錯誤連接線顏色-->  <attr name="erroStrokeColor" format="color" />  <!--連接線size-->  <attr name="strokeWidth" format="dimension" /> </declare-styleable></resources>

然后就是第一個叫GestureContentView的view去繼承viewgroup,并重新三個構造方法:

public class GestureContentView extends ViewGroup { public void setGesturePwdCallBack(IGesturePwdCallBack gesturePwdCallBack) {  this.gesturePwdCallBack = gesturePwdCallBack; } public GestureContentView(Context context) {  this(context, null); } public GestureContentView(Context context, AttributeSet attrs) {  this(context, attrs, 0); } public GestureContentView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  }}

然后我們需要在帶三個參數的構造方法中獲取我們傳入的自定義屬性:

public GestureContentView(Context context, AttributeSet attrs, int defStyleAttr) {  super(context, attrs, defStyleAttr);  setWillNotDraw(false);  obtainStyledAttr(context, attrs, defStyleAttr);  initViews(); }

你會發現我這里調用下setWillNotDraw(false);方法,顧名思義,設置為false后onDraw方法才會被調用,當然你也可以重寫dispatchDraw方法(具體原因我就不扯了哈,自己網上查或者看view的源碼)。

private void obtainStyledAttr(Context context, AttributeSet attrs, int defStyleAttr) {  final TypedArray a = context.obtainStyledAttributes(    attrs, R.styleable.IndicatorView, defStyleAttr, 0);  mNormalDrawable = a.getDrawable(R.styleable.IndicatorView_normalDrawable);  mSelectedDrawable = a.getDrawable(R.styleable.IndicatorView_selectedDrawable);  mErroDrawable = a.getDrawable(R.styleable.IndicatorView_erroDrawable);  checkDrawable();  if (a.hasValue(R.styleable.IndicatorView_row)) {   mRow = a.getInt(R.styleable.IndicatorView_row, NUMBER_ROW);  }  if (a.hasValue(R.styleable.IndicatorView_column)) {   mColumn = a.getInt(R.styleable.IndicatorView_row, NUMBER_COLUMN);  }  if (a.hasValue(R.styleable.IndicatorView_padding)) {   DEFAULT_PADDING = a.getDimensionPixelSize(R.styleable.IndicatorView_padding, DEFAULT_PADDING);  }  strokeColor=a.getColor(R.styleable.IndicatorView_normalStrokeColor,DEFAULT_STROKE_COLOR);  erroStrokeColor=a.getColor(R.styleable.IndicatorView_erroStrokeColor,ERRO_STROKE_COLOR);  strokeWidth=a.getDimensionPixelSize(R.styleable.IndicatorView_strokeWidth,DEFAULT_STROKE_W); }

然后獲取到了我們需要的東西后,我們需要知道每個點的大小跟自己的大小了(還是一樣的套路,不懂的看上一篇博客哈):

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  int widthMode = MeasureSpec.getMode(widthMeasureSpec);  int heightMode = MeasureSpec.getMode(heightMeasureSpec);  float width = MeasureSpec.getSize(widthMeasureSpec);  float height = MeasureSpec.getSize(heightMeasureSpec);  float result = Math.min(width, height);  height = getHeightValue(result, heightMode);  width = getWidthValue(result, widthMode);  setMeasuredDimension((int) width, (int) height); }
 private float getHeightValue(float height, int heightMode) {  if (heightMode == MeasureSpec.EXACTLY) {   mCellHeight = (height - (mColumn + 1) * DEFAULT_PADDING) / mColumn;  } else {   mCellHeight = Math.min(mNormalDrawable.getIntrinsicHeight(), mSelectedDrawable.getIntrinsicHeight());   height = mCellHeight * mColumn + (mColumn + 1) * DEFAULT_PADDING;  }  return height; } private float getWidthValue(float width, int widthMode) {  if (widthMode == MeasureSpec.EXACTLY) {   mCellWidth = (width - (mRow + 1) * DEFAULT_PADDING) / mRow;  } else {   mCellWidth = Math.min(mNormalDrawable.getIntrinsicWidth(), mSelectedDrawable.getIntrinsicWidth());   width = mCellWidth * mRow + (mRow + 1) * DEFAULT_PADDING;  }  return width; }

好了,view的大小跟點的大小我們都知道了,然后我們需要根據我們傳入的行數跟列數添加我們的點了:

@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {  super.onSizeChanged(w, h, oldw, oldh);  if (!isInitialed && getChildCount() == 0) {   isInitialed = true;   points = new ArrayList<>();   addChildViews();  } }

首先在onSizeChanged方法中去添加我們的點(也就是子view)因為onSizeChanged會被調很多次,然后避免重復添加子view,我們做了一個判斷,第一次并且child=0的時候再去添加子view:

 private void addChildViews() {  for (int i = 0; i < mRow; i++) {   for (int j = 0; j < mColumn; j++) {    GesturePoint point = new GesturePoint();    ImageView image = new ImageView(getContext());    point.setImageView(image);    int left = (int) ((j + 1) * DEFAULT_PADDING + j * mCellWidth);    int top = (int) ((i + 1) * DEFAULT_PADDING + i * mCellHeight);    int right = (int) (left + mCellWidth);    int bottom = (int) (top + mCellHeight);    point.setLeftX(left);    point.setRightX(right);    point.setTopY(top);    point.setBottomY(bottom);    point.setCenterX((int) (left + mCellWidth / 2));    point.setCenterY((int) (top + mCellHeight / 2));    point.setNormalDrawable(mNormalDrawable);    point.setErroDrawable(mErroDrawable);    point.setSelectedDrawable(mSelectedDrawable);    point.setState(PointState.POINT_STATE_NORMAL);    point.setNum(Integer.parseInt(String.valueOf(mRow * i + j)));    point.setPointX(i);    point.setPointY(j);    this.addView(image, (int) mCellWidth, (int) mCellHeight);    points.add(point);   }  } }

添加的個數=行數*列數,然后把創建的image添加進當前viewgroup中:

for (int i = 0; i < mRow; i++) {   for (int j = 0; j < mColumn; j++) {    GesturePoint point = new GesturePoint();    ImageView image = new ImageView(getContext());  ......   this.addView(image, (int) mCellWidth, (int) mCellHeight); }}

并且把所有的點信息存放在了一個叫points.add(point);的集合中,集合中存放的是GesturePoint對象:

public class GesturePoint { //點的左邊距值 private int leftX; //點的top值 private int topY; //點的右邊距值 private int rightX; private int bottomY; //點的中間值x軸 private int centerX; private int centerY; //點對應的行值 private int pointX; //點對應的列值 private int pointY; //點對應的imageview private ImageView imageView; //當前點的狀態:選中、未選中、錯誤 private PointState state; //當前點對應的密碼數值 private int num; //未選中點的drawale private Drawable normalDrawable; private Drawable erroDrawable; private Drawable selectedDrawable; }

既然我們已經添加了很多個點,然后我們要做的就是根據行列排列我們點的位置了(重寫onLayout方法擺放子view(點)的位置):

@Override protected void onLayout(boolean changed, int l, int t, int r, int b) {  if (points != null && points.size() > 0) {   for (GesturePoint point : points) {    point.layout();   }  } }
public void layout() {  if (this.imageView != null) {   this.imageView.layout(leftX, topY, rightX, bottomY);  } }

然后我們添加下我們的view并運行代碼:

<FrameLayout  android:layout_width="match_parent"  android:layout_height="0dp"  android:layout_weight="1"  android:layout_marginTop="10dp"  >  <com.leo.library.view.GestureContentView   android:id="@+id/id_gesture_pwd"   android:layout_gravity="center_horizontal"   android:layout_marginTop="10dp"   android:layout_width="match_parent"   android:layout_height="match_parent"   app:column="3"   app:row="3"   app:padding="50dp"   app:normalDrawable="@drawable/gesture_node_normal"   app:selectedDrawable="@drawable/gesture_node_pressed"   app:erroDrawable="@drawable/gesture_node_wrong"   app:normalStrokeColor="#000"   app:erroStrokeColor="#ff0000"   app:strokeWidth="4dp"   /> </FrameLayout>

效果圖:

寫到這里,離我們的目標越來越近了,接下來要做的就是重寫onTouchEvent方法,然后通過手指滑動位置做出相應的改變了:

1、我們需要根據手指的位置查看是否在某個點內,判斷該點是不是被選中,沒選中就改變其狀態為選中狀態。

2、手指每連接兩個點的時候,我們需要判斷兩個點中間是否有點,比如:我們手指連接了(0,0) 跟(2,2)這兩個點,那么我們需要判斷中間是否有點(1、1)存在。然后需要把線段(0,0)~(1、1)和線段(1、1)~(2、2)保存在集合中,然后下一次再畫線段的時候起點就為(2、2)點了。

3、沒選中一個點我們就把該點對應的num值(密碼)存入集合中。

4、當執行ACTION_UP事件(也就是手指抬起的時候),回調方法,把存儲的集合數據傳給調用者。

 @Override public boolean onTouchEvent(MotionEvent event) {  //是否允許用戶繪制  if (!isDrawEnable) return super.onTouchEvent(event);  //畫筆顏色設置為繪制顏色  linePaint.setColor(strokeColor);  int action = event.getAction();  //當手指按下的時候  if (MotionEvent.ACTION_DOWN == action) {   //清除畫板   changeState(PointState.POINT_STATE_NORMAL);   preX = (int) event.getX();   preY = (int) event.getY();   //根據當前手指位置找出對應的點   currPoint = getPointByPosition(preX, preY);   //如果當前手指在某個點中的時候,把該點標記為選中狀態   if (currPoint != null) {    currPoint.setState(PointState.POINT_STATE_SELECTED);    //把當前選中的點添加進集合中    pwds.add(currPoint.getNum());   }   //當手指移動的時候  } else if (MotionEvent.ACTION_MOVE == action) {   //,清空畫板,然后畫出前面存儲的線段   clearScreenAndDrawLine();   //獲取當前移動的位置是否在某個點中   GesturePoint point = getPointByPosition((int) event.getX(), (int) event.getY());   //沒有在點的范圍內的話并且currpoint也為空的時候(在畫板外移動手指)直接返回   if (point == null && currPoint == null) {    return super.onTouchEvent(event);   } else {    //當按下時候的點為空,然后手指移動到了某一點的時候,把該點賦給currpoint    if (currPoint == null) {     currPoint = point;     //修改該點的狀態     currPoint.setState(PointState.POINT_STATE_SELECTED);     //添加該點的值     pwds.add(currPoint.getNum());    }   }   //當移動的不在點范圍內、一直在同一個點中移動、選中了某個點后再次選中的時候不讓選中(也就是不讓出現重復密碼)   if (point == null || currPoint.getNum() == point.getNum() || point.getState() == PointState.POINT_STATE_SELECTED) {    lineCanvas.drawLine(currPoint.getCenterX(), currPoint.getCenterY(), event.getX(), event.getY(), linePaint);   } else {    //修改該點的狀態為選中    point.setState(PointState.POINT_STATE_SELECTED);    //連接currpoint跟當前point    lineCanvas.drawLine(currPoint.getCenterX(), currPoint.getCenterY(), point.getCenterX(), point.getCenterY(), linePaint);    //判斷兩個點中是否存在點    List<Pair<GesturePoint, GesturePoint>> betweenPoints = getBetweenPoints(currPoint, point);    //如果存在點的話,把中間點對應的線段存入集合總    if (betweenPoints != null && betweenPoints.size() > 0) {     pointPairs.addAll(betweenPoints);     currPoint = point;     pwds.add(point.getNum());    } else {     pointPairs.add(new Pair(currPoint, point));     pwds.add(point.getNum());     currPoint = point;    }   }   invalidate();  } else if (MotionEvent.ACTION_UP == action) {   //手指抬起的時候回調   if (gesturePwdCallBack != null) {    List<Integer> datas=new ArrayList<>(pwds.size());    datas.addAll(pwds);    gesturePwdCallBack.callBack(datas);   }  }  return true; }

重點解釋下getBetweenPoints方法(聲明一下哈:本人數學比較差,有好方法的童鞋虐過哈。記得評論告訴我一下,拜謝啦~~ 自己覺得自己的方法比較蠢,嘻嘻~!!):

 private List<Pair<GesturePoint, GesturePoint>> getBetweenPoints(GesturePoint currPoint, GesturePoint point) {  //定義一個集合裝傳入的點  List<GesturePoint> points1 = new ArrayList<>();  points1.add(currPoint);  points1.add(point);  //排序兩個點  Collections.sort(points1, new Comparator<GesturePoint>() {   @Override   public int compare(GesturePoint o1, GesturePoint o2) {    return o1.getNum() - o2.getNum();   }  });  GesturePoint maxPoint = points1.get(1);  GesturePoint minPoint = points1.get(0);  points1.clear();  /**   * 根據等差數列公式an=a1+(n-1)*d,我們知道an跟a1,n=(an的列或者行值-a1的列或者行值+1),   * 算出d,如果d為整數那么為等差數列,如果an的列或者行值-a1的列或者行值>1的話,就證明存在   * 中間值。   * 1、算出的d是否為整數   * 2、an的行值-a1的行值>1或者an的列值-a1的列值>1   *   * 兩個條件成立的話就證明有中間點   */  if (((maxPoint.getNum() - minPoint.getNum()) % Math.max(maxPoint.getPointX(), maxPoint.getPointY()) == 0)    &&    ((maxPoint.getPointX() - minPoint.getPointX()) > 1 ||      maxPoint.getPointY() - minPoint.getPointY() > 1    )) {   //算出等差d   int duration = (maxPoint.getNum() - minPoint.getNum()) / Math.max(maxPoint.getPointX(), maxPoint.getPointY());   //算出中間有多少個點   int count = maxPoint.getPointX() - minPoint.getPointX() - 1;   count = Math.max(count, maxPoint.getPointY() - minPoint.getPointY() - 1);   //利用等差數列公式算出中間點(an=a1+(n-1)*d)   for (int i = 0; i < count; i++) {    int num = minPoint.getNum() + (i + 1) * duration;    for (GesturePoint p : this.points) {     //在此判斷算出的中間點是否存在并且沒有被選中     if (p.getNum() == num && p.getState() != PointState.POINT_STATE_SELECTED) {      //把選中的點添加進集合      pwds.add(p.getNum());      //修改該點的狀態為選中狀態      p.setState(PointState.POINT_STATE_SELECTED);      points1.add(p);     }    }   }  }  //利用算出的中間點來算出中間線段  List<Pair<GesturePoint, GesturePoint>> pairs = new ArrayList<>();  for (int i = 0; i < points1.size(); i++) {   GesturePoint p = points1.get(i);   if (i == 0) {    pairs.add(new Pair(minPoint, p));   } else if (pairs.size() > 0) {    pairs.add(new Pair(pairs.get(0).second, p));   }   if (i == points1.size() - 1) {    pairs.add(new Pair(p, maxPoint));   }  }  //返回中間線段  return pairs; }

好啦!!代碼就解析到這里了哈,看不懂的童鞋自己去拖代碼然后跑跑就知道了。

整個做下來除了某幾個地方有點難度外,其它的地方也都是很簡單的東西呢?以前看起來很高大上的東西是不是現在覺得soeasy了呢?? 哈哈~~~

最后給出項目github鏈接:
https://github.com/913453448/GestureContentView

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 棋牌| 岱山县| 彰武县| 西吉县| 关岭| 济南市| 孟津县| 栾城县| 鹤壁市| 贵南县| 梨树县| 精河县| 巴青县| 梁河县| 信宜市| 长宁县| 象山县| 中卫市| 长岛县| 房产| 汶川县| 南阳市| 井陉县| 泗洪县| 浏阳市| 新津县| 大名县| 阿巴嘎旗| 枣阳市| 临澧县| 大荔县| 吐鲁番市| 兴仁县| 揭阳市| 松滋市| 鄂伦春自治旗| 宁明县| 满城县| 车险| 偏关县| 乌什县|