好吧這一段時間確實沒有好好的敲代碼了,深深的罪惡感啊,準(zhǔn)備這周搞一下在guihub上看到的一個密碼應(yīng)用PassWord,準(zhǔn)備把這個仿寫完成的,主要為了熟悉一些現(xiàn)在熱門但在現(xiàn)在公司項目上用不到的東東.
知識點 |
- Android手勢解鎖 |
- Realm數(shù)據(jù)庫的使用 |
- MVP架構(gòu)的搭建 |
- 一些5.0的控件使用 |
- 自定義控件 |
1.Android手勢解鎖
昨天看了一下鴻洋之前寫的手勢解鎖控件的實現(xiàn),膜拜一下,感覺自己的代碼量太少需要大大加強啊.好了廢話不多說了,直接看實現(xiàn),這里主要就是自定義view和畫圖兩個方面的東東.
整個解鎖的界面構(gòu)成包括兩個部分,解鎖的圓,以及手指劃過時的線兩個部分,圓的構(gòu)成包括兩個部分外圈的空心圓以及內(nèi)圈的實心圓,然后圓的狀態(tài)包括三個部分正常未按下,按下,手指抬起主要是顏色的變化.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
/**
* 手勢解鎖中單個圓的view
* Created by apple on 16/6/20.
*/
public class GestureLockView extends View {
private static final String TAG = "GestureLockView";
//觸摸點的三種狀態(tài)
enum Mode {
STATUS_NO_FINGER, STATUS_FINGER_ON, STATUS_FINGER_UP;
}
//初始狀態(tài)
private Mode mCurrentStatus = Mode.STATUS_NO_FINGER;
//圓的寬高
private int mWidth;
private int mHeight;
//圓的半徑
private int mRadius;
//畫筆的寬度
private int mStrokeWidth = 2;
//圓的中心坐標(biāo)點
private int mCenterX, mCenterY;
private Paint mPaint;
/**
* 箭頭(小三角最長邊的一半長度 = mArrawRate * mWidth / 2 )
*/
private float mArrowRate = 0.333f;
private int mArrowDegree = -1;
private Path mArrowPath;
/**
* 內(nèi)圓的半徑 = mInnerCircleRadiusRate * mRadus
*/
private float mInnerCircleRadiusRate = 0.3F;
/**
* 四個顏色仅乓,可由用戶自定義夸楣,初始化時由GestureLockViewGroup傳入
*/
private int mColorNoFingerInner;
private int mColorNoFingerOutter;
private int mColorFingerOn;
private int mColorFingerUp;
public GestureLockView(Context context) {
super(context);
}
public GestureLockView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GestureLockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public GestureLockView(Context context, int colorNoFingerInner, int colorNoFingerOutter, int colorFingerOn, int colorFingerUp) {
super(context);
mColorFingerOn = colorFingerOn;
mColorFingerUp = colorFingerUp;
mColorNoFingerInner = colorNoFingerInner;
mColorNoFingerOutter = colorNoFingerOutter;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//path 一個路徑類
mArrowPath = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
//圓的邊框為正方形,取兩個最小的
mWidth = mWidth < mHeight ? mWidth : mHeight;
//獲取圓的半徑和圓點坐標(biāo)
mRadius = mCenterX = mCenterY = mWidth / 2;
//減去畫筆的寬度
mRadius -= mStrokeWidth;
//默認的三角位置為圓的正上方向上的剪頭,然后根據(jù)兩個GestureLockView來進行角度的旋轉(zhuǎn)
//確定位置以及確定圓的path路徑
float mArrowLength = mWidth / 2 * mArrowRate;
mArrowPath.moveTo(mWidth / 2, mStrokeWidth + 2);//移動到線的起始點
mArrowPath.lineTo(mWidth / 2 - mArrowLength, mStrokeWidth + mArrowLength + 2);//從原點到這個點劃線
mArrowPath.lineTo(mWidth / 2 + mArrowLength, mStrokeWidth + mArrowLength + 2);
mArrowPath.close();
mArrowPath.setFillType(Path.FillType.WINDING);//設(shè)置填充類型
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//根據(jù)不同的狀態(tài)畫圓
switch (mCurrentStatus) {
case STATUS_FINGER_ON:
//按下時畫圓
//繪制外圓
mPaint.setColor(mColorFingerOn);
mPaint.setStyle(Paint.Style.STROKE);//只有邊線
mPaint.setStrokeWidth(2);
canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
//繪制內(nèi)圓
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
break;
case STATUS_FINGER_UP:
//手指抬起時
mPaint.setColor(mColorFingerUp);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
drawArrow(canvas);
break;
case STATUS_NO_FINGER:
//沒有按下的狀態(tài)
//繪制外圓
mPaint.setStyle(Paint.Style.FILL);//完全填充
mPaint.setColor(mColorNoFingerOutter);
canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
//繪制內(nèi)圓
mPaint.setColor(mColorNoFingerInner);
canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
break;
}
}
/**
* 繪制剪頭
* canvas的save和restore的作用
* 這兩個是成對出現(xiàn)的 save 可以保存當(dāng)前canvas上以有的狀態(tài)
* 一般我們需要是對某個特定元素進行旋轉(zhuǎn)等操作,而這個操作又不想對其他的元素有影響,那么就可以使用這個
* restore就可以取出之前保存的狀態(tài)
*/
private void drawArrow(Canvas canvas){
if(mArrowDegree != -1){
mPaint.setStyle(Paint.Style.FILL);
canvas.save();
canvas.rotate(mArrowDegree,mCenterX,mCenterY);//畫布旋轉(zhuǎn)
canvas.drawPath(mArrowPath,mPaint);//繪制路徑,這里其實繪制兩條線,不過因為畫筆模式為完全填充,所以就構(gòu)建成了一個三角形
canvas.restore();
}
}
/**
* 設(shè)置當(dāng)前的模式并進行重新繪制
*/
public void setMode(Mode mode){
mCurrentStatus = mode;
invalidate();
}
/**
* 設(shè)置剪頭的角度
* @param degree
*/
public void setmArrowDegree(int degree){
mArrowDegree = degree;
}
public int getmArrowDegree(){
return mArrowDegree;
}
}
然后是整體的布局,這里使用了自定義熟悉來設(shè)置一些參數(shù),包括上面的顏色以及每行的個數(shù),這里每行的個數(shù)和列是相互對應(yīng)的,就是3*3,然后還有重試次數(shù).自定義屬性
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import java.util.ArrayList;
import java.util.List;
/**
* 抄的洪洋的手勢解鎖,這里有一個問題就是圖像最開始的一個坐標(biāo)點是從0開始的
* Created by apple on 16/6/20.
*/
public class GestureLockViewGroup extends RelativeLayout {
private static final String TAG = "GestureLockViewGroup";
//所有的點view
private GestureLockView[] mGestureLockViews;
private int mCount = 4;
//答案
private int[] mAnswer = {1, 2,3, 6, 7};
//被選中的圖像id
private List<Integer> mChoose = new ArrayList<>();
private Paint mPaint;
/**
* 每個GestureLockView中間的間距 設(shè)置為:mGestureLockViewWidth * 25%
*/
private int mMarginBetweenLockView = 30;
/**
* GestureLockView的邊長 4 * mWidth / ( 5 * mCount + 1 )
*/
private int mGestureLockViewWidth;
/**
* GestureLockView無手指觸摸的狀態(tài)下內(nèi)圓的顏色
*/
private int mNoFingerInnerCircleColor = 0xFF939090;
/**
* GestureLockView無手指觸摸的狀態(tài)下外圓的顏色
*/
private int mNoFingerOuterCircleColor = 0xFFE0DBDB;
/**
* GestureLockView手指觸摸的狀態(tài)下內(nèi)圓和外圓的顏色
*/
private int mFingerOnColor = 0xFF378FC9;
/**
* GestureLockView手指抬起的狀態(tài)下內(nèi)圓和外圓的顏色
*/
private int mFingerUpColor = 0xFFFF0000;
/**
* 寬度
*/
private int mWidth;
/**
* 高度
*/
private int mHeight;
private Path mPath;
/**
* 指引線的開始位置x
*/
private int mLastPathX;
/**
* 指引線的開始位置y
*/
private int mLastPathY;
/**
* 指引下的結(jié)束位置
*/
private Point mTmpTarget = new Point();
/**
* 最大嘗試次數(shù)
*/
private int mTryTimes = 4;
/**
* 回調(diào)接口
*/
private OnGestureLockViewListener mOnGestureLockViewListener;
public GestureLockViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GestureLockViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//獲取自定義屬性的值
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.GestureLockViewGroup, defStyleAttr, 0);
int count = typedArray.getIndexCount();
for (int a = 0; a < count; a++) {
int index = typedArray.getIndex(a);
switch (index) {
case R.styleable.GestureLockViewGroup_color_finger_on:
mFingerOnColor = typedArray.getColor(a, mFingerOnColor);
break;
case R.styleable.GestureLockViewGroup_color_finger_up:
mFingerUpColor = typedArray.getColor(a, mFingerUpColor);
break;
case R.styleable.GestureLockViewGroup_color_no_finger_inner_circle:
mNoFingerInnerCircleColor = typedArray.getColor(a, mNoFingerInnerCircleColor);
break;
case R.styleable.GestureLockViewGroup_color_no_finger_outer_circle:
mNoFingerOuterCircleColor = typedArray.getColor(a, mNoFingerOuterCircleColor);
break;
case R.styleable.GestureLockViewGroup_count:
mCount = typedArray.getInteger(a, count);
break;
case R.styleable.GestureLockViewGroup_tryTimes:
mTryTimes = typedArray.getInteger(a, mTryTimes);
break;
}
}
typedArray.recycle();
//初始化畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPath = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
mHeight = mWidth = mWidth < mHeight ? mWidth : mHeight;
//測量布局每個GestureLockView
if (mGestureLockViews == null) {
mGestureLockViews = new GestureLockView[mCount * mCount];
// 這里鴻神說是根據(jù)屏幕寬度進行百分比的布局..計算方法沒看懂
// 計算每個GestureLockView的寬度
mGestureLockViewWidth = (int) (4 * mWidth * 1.0f / (5 * mCount + 1));
//計算每個GestureLockView的間距
mMarginBetweenLockView = (int) (mGestureLockViewWidth * 0.25);
// 設(shè)置畫筆的寬度為GestureLockView的內(nèi)圓直徑稍微小點(不喜歡的話,隨便設(shè))
mPaint.setStrokeWidth(mGestureLockViewWidth * 0.29f);
for (int i = 0; i < mGestureLockViews.length; i++) {
mGestureLockViews[i] = new GestureLockView(getContext(), mNoFingerInnerCircleColor, mNoFingerOuterCircleColor, mFingerOnColor, mFingerUpColor);
mGestureLockViews[i].setId(i + 1);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mGestureLockViewWidth, mGestureLockViewWidth);
// 不是每行的第一個涉兽,則設(shè)置位置為前一個的右邊
if (i % mCount != 0) {
params.addRule(RelativeLayout.RIGHT_OF, mGestureLockViews[i - 1].getId());
}
// 從第二行開始枷畏,設(shè)置為上一行同一位置View的下面
if (i > mCount - 1) {
params.addRule(RelativeLayout.BELOW, mGestureLockViews[i - mCount].getId());
}
//設(shè)置右下左上的邊距
int rightMargin = mMarginBetweenLockView;
int bootomMargin = mMarginBetweenLockView;
int leftMargin = 0;
int topMargin = 0;
//第一行有topMargin 第一列有l(wèi)eftMargin
if (i >= 0 && i < mCount) {
topMargin = mMarginBetweenLockView;
}
if (i % mCount == 0) {
leftMargin = mMarginBetweenLockView;
}
params.setMargins(leftMargin, topMargin, rightMargin, bootomMargin);
mGestureLockViews[i].setMode(GestureLockView.Mode.STATUS_NO_FINGER);
addView(mGestureLockViews[i], params);
}
}
}
//這里就是處理手指滑動
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
//手指按下的時候現(xiàn)把所有的重置
reSet();
break;
case MotionEvent.ACTION_UP:
//手指抬起的時候要進行剪頭角度的測量和繪制,以及判斷結(jié)果的正確與否
mPaint.setColor(mFingerUpColor);
mPaint.setAlpha(50);
this.mTryTimes--;
if (mOnGestureLockViewListener != null && mChoose.size() > 0) {
mOnGestureLockViewListener.onGestureEvent(checkAnswer());
if (mTryTimes <= 0) {
mOnGestureLockViewListener.onUnmatchedExceedBoundary();
}
}
//將終點設(shè)置為起點
mTmpTarget.x = mLastPathX;
mTmpTarget.y = mLastPathY;
changeItemMode();
for (int i = 0; i + 1 < mChoose.size(); i++) {
int startId = mChoose.get(i);
int nextId = mChoose.get(i + 1);
GestureLockView startView = (GestureLockView) findViewById(startId);
GestureLockView nextView = (GestureLockView) findViewById(nextId);
int dx = nextView.getLeft() - startView.getLeft();
int dy = nextView.getTop() - startView.getTop();
int degrees = (int) (Math.toDegrees(Math.atan2(dy, dx)) + 90);//角度的計算方法
startView.setmArrowDegree(degrees);
}
break;
case MotionEvent.ACTION_MOVE:
mPaint.setColor(mFingerOnColor);
mPaint.setAlpha(50);
GestureLockView gestureLockView = getChildIdByPos(x, y);
if (gestureLockView != null) {
int id = gestureLockView.getId();
if (!mChoose.contains(id)) {
mChoose.add(id);
gestureLockView.setMode(GestureLockView.Mode.STATUS_FINGER_ON);
if (mOnGestureLockViewListener != null) {
mOnGestureLockViewListener.onBlockSelected(id);
}
//設(shè)置劃線的起點
mLastPathX = gestureLockView.getLeft() / 2 + gestureLockView.getRight() / 2;
mLastPathY = gestureLockView.getTop() / 2 + gestureLockView.getBottom() / 2;
if (mChoose.size() == 1) {
mPath.moveTo(mLastPathX, mLastPathY);
} else {
mPath.lineTo(mLastPathX, mLastPathY);
}
}
}
mTmpTarget.x = x;
mTmpTarget.y = y;
break;
}
invalidate();
return true;
}
/**
* 恢復(fù)初始設(shè)置
*/
private void reSet() {
mChoose.clear();
mPath.reset();
for (GestureLockView gestureLockView : mGestureLockViews) {
gestureLockView.setMode(GestureLockView.Mode.STATUS_NO_FINGER);
gestureLockView.setmArrowDegree(-1);
}
}
//檢測答案
private boolean checkAnswer() {
if (mAnswer.length != mChoose.size()) {
Log.e(TAG,mAnswer.length+"length");
return false;
}
for (int i = 0; i < mAnswer.length; i++) {
Log.e(TAG,mChoose.get(i)+"point"+mAnswer[i]+"answer");
if (mAnswer[i] != mChoose.get(i)) {
return false;
}
}
return true;
}
/**
* 判斷當(dāng)前點是否在當(dāng)前view中
*
* @param gestureLockView
* @param x
* @param y
* @return
*/
private boolean checkPositionInChild(GestureLockView gestureLockView, int x, int y) {
int padding = (int) (mGestureLockViewWidth * 0.15);
if (x > gestureLockView.getLeft() + padding && x < gestureLockView.getRight() - padding &&
y > gestureLockView.getTop() + padding && y < gestureLockView.getBottom() - padding
) {
return true;
}
return false;
}
/**
* 根據(jù)坐標(biāo)點獲取子view
*
* @param x
* @param y
* @return
*/
private GestureLockView getChildIdByPos(int x, int y) {
for (GestureLockView gestureLockView : mGestureLockViews) {
if (checkPositionInChild(gestureLockView, x, y)) {
return gestureLockView;
}
}
return null;
}
private void changeItemMode() {
for (GestureLockView gestureLockView : mGestureLockViews) {
if (mChoose.contains(gestureLockView.getId())) {
gestureLockView.setMode(GestureLockView.Mode.STATUS_FINGER_UP);
}
}
}
public void setListener(OnGestureLockViewListener listener) {
this.mOnGestureLockViewListener = listener;
}
public void setAnswer(int[] answer) {
this.mAnswer = answer;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPath != null) {
canvas.drawPath(mPath, mPaint);
}
if (mChoose.size() > 0) {
if (mLastPathX != 0 && mLastPathY != 0) {
canvas.drawLine(mLastPathX, mLastPathY, mTmpTarget.x, mTmpTarget.y, mPaint);
}
}
}
public interface OnGestureLockViewListener {
/**
* 單獨選中元素的Id
*
* @param cId
*/
public void onBlockSelected(int cId);
/**
* 是否匹配
*
* @param matched
*/
public void onGestureEvent(boolean matched);
/**
* 超過嘗試次數(shù)
*/
public void onUnmatchedExceedBoundary();
}
}
自定義的屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="color_no_finger_inner_circle" format="color"/>
<attr name="color_no_finger_outer_circle" format="color"/>
<attr name="color_finger_on" format="color"/>
<attr name="color_finger_up" format="color"/>
<attr name="count" format="integer"/>
<attr name="tryTimes" format="integer"/>
<declare-styleable name="GestureLockViewGroup">
<attr name="color_no_finger_inner_circle"/>
<attr name="color_no_finger_outer_circle" />
<attr name="color_finger_on" />
<attr name="color_finger_up" />
<attr name="count" />
<attr name="tryTimes" />
</declare-styleable>
</resources>
布局調(diào)用,自定義的顏色沒有加
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:attar="http://schemas.android.com/apk/armglobe.gesture"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="armglobe.gesture.MainActivity">
<armglobe.gesture.GestureLockViewGroup
android:id="@+id/getsturegroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F2F2F7"
android:gravity="center_vertical"
attar:count="4"
attar:tryTimes="5" />
</RelativeLayout>
Activity代碼
public class MainActivity extends AppCompatActivity {
GestureLockViewGroup gestureLockViewGroup;
GestureLockViewGroup.OnGestureLockViewListener lockViewListener = new GestureLockViewGroup.OnGestureLockViewListener() {
@Override
public void onBlockSelected(int cId) {
}
@Override
public void onGestureEvent(boolean matched) {
if(matched){
Toast.makeText(MainActivity.this,"正確",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(MainActivity.this,"錯誤",Toast.LENGTH_SHORT).show();
}
}
@Override
public void onUnmatchedExceedBoundary() {
Toast.makeText(MainActivity.this,"已經(jīng)超出最大重試次數(shù)",Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gestureLockViewGroup = (GestureLockViewGroup) findViewById(R.id.getsturegroup);
gestureLockViewGroup.setListener(lockViewListener);
}
}
好了手勢解鎖的代碼就這些,下面可以開始看realm數(shù)據(jù)庫的使用了,之前項目都是用的sql語句,反正寫的要吐血,這次看看專門為移動的數(shù)據(jù)庫會怎么樣