欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2017年03月01日 09:28:33   作者:vv_小蟲  
這篇文章主要為大家詳細介紹了Android手勢密碼view的第二篇學習筆記,具有一定的參考價值,感興趣的小伙伴們可以參考一下

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

下面我們來實現(xiàn)下我們的手勢密碼view:

實現(xiàn)思路:

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

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

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

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

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

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="IndicatorView">
  <!--默認狀態(tài)的drawable-->
  <attr name="normalDrawable" format="reference" />
  <!--被選中狀態(tài)的drawable-->
  <attr name="selectedDrawable" format="reference" />
  <!--列數(shù)-->
  <attr name="column" format="integer" />
  <!--行數(shù)-->
  <attr name="row" format="integer" />
  <!--錯誤狀態(tài)的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,并重新三個構(gòu)造方法:

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);
  }
}

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

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

你會發(fā)現(xiàn)我這里調(diào)用下setWillNotDraw(false);方法,顧名思義,設(shè)置為false后onDraw方法才會被調(diào)用,當然你也可以重寫dispatchDraw方法(具體原因我就不扯了哈,自己網(wǎng)上查或者看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的大小跟點的大小我們都知道了,然后我們需要根據(jù)我們傳入的行數(shù)跟列數(shù)添加我們的點了:

@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會被調(diào)很多次,然后避免重復(fù)添加子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);
   }
  }
 }

添加的個數(shù)=行數(shù)*列數(shù),然后把創(chuàng)建的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;
 //點對應(yīng)的行值
 private int pointX;
 //點對應(yīng)的列值
 private int pointY;
 //點對應(yīng)的imageview
 private ImageView imageView;
 //當前點的狀態(tài):選中、未選中、錯誤
 private PointState state;
 //當前點對應(yīng)的密碼數(shù)值
 private int num;
 //未選中點的drawale
 private Drawable normalDrawable;
 private Drawable erroDrawable;
 private Drawable selectedDrawable;
 }

既然我們已經(jīng)添加了很多個點,然后我們要做的就是根據(jù)行列排列我們點的位置了(重寫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方法,然后通過手指滑動位置做出相應(yīng)的改變了:

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

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

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

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

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  //是否允許用戶繪制
  if (!isDrawEnable) return super.onTouchEvent(event);
  //畫筆顏色設(shè)置為繪制顏色
  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();
   //根據(jù)當前手指位置找出對應(yīng)的點
   currPoint = getPointByPosition(preX, preY);
   //如果當前手指在某個點中的時候,把該點標記為選中狀態(tài)
   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());
   //沒有在點的范圍內(nèi)的話并且currpoint也為空的時候(在畫板外移動手指)直接返回
   if (point == null && currPoint == null) {
    return super.onTouchEvent(event);
   } else {
    //當按下時候的點為空,然后手指移動到了某一點的時候,把該點賦給currpoint
    if (currPoint == null) {
     currPoint = point;
     //修改該點的狀態(tài)
     currPoint.setState(PointState.POINT_STATE_SELECTED);
     //添加該點的值
     pwds.add(currPoint.getNum());
    }
   }
   //當移動的不在點范圍內(nèi)、一直在同一個點中移動、選中了某個點后再次選中的時候不讓選中(也就是不讓出現(xiàn)重復(fù)密碼)
   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 {
    //修改該點的狀態(tài)為選中
    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);
    //如果存在點的話,把中間點對應(yīng)的線段存入集合總
    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) {
   //手指抬起的時候回調(diào)
   if (gesturePwdCallBack != null) {
    List<Integer> datas=new ArrayList<>(pwds.size());
    datas.addAll(pwds);
    gesturePwdCallBack.callBack(datas);
   }
  }
  return true;

 }

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

 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();
  /**
   * 根據(jù)等差數(shù)列公式an=a1+(n-1)*d,我們知道an跟a1,n=(an的列或者行值-a1的列或者行值+1),
   * 算出d,如果d為整數(shù)那么為等差數(shù)列,如果an的列或者行值-a1的列或者行值>1的話,就證明存在
   * 中間值。
   * 1、算出的d是否為整數(shù)
   * 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);
   //利用等差數(shù)列公式算出中間點(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());
      //修改該點的狀態(tài)為選中狀態(tài)
      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;
 }


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

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

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

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

相關(guān)文章

最新評論