好吧这一段时间确实没有好好的敲代码了,深深的罪恶感啊,准备这周搞一下在guihub上看到的一个密码应用PassWord,准备把这个仿写完成的,主要为了熟悉一些现在热门但在现在公司项目上用不到的东东.
知识点 |
- Android手势解锁 |
- Realm数据库的使用 |
- MVP架构的搭建 |
- 一些5.0的控件使用 |
- 自定义控件 |
1.Android手势解锁
昨天看了一下鸿洋之前写的手势解锁控件的实现,膜拜一下,感觉自己的代码量太少需要大大加强啊.好了废话不多说了,直接看实现,这里主要就是自定义view和画图两个方面的东东.
整个解锁的界面构成包括两个部分,解锁的圆,以及手指划过时的线两个部分,圆的构成包括两个部分外圈的空心圆以及内圈的实心圆,然后圆的状态包括三个部分正常未按下,按下,手指抬起主要是颜色的变化.
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";
//触摸点的三种状态
enum Mode {
STATUS_NO_FINGER, STATUS_FINGER_ON, STATUS_FINGER_UP;
}
//初始状态
private Mode mCurrentStatus = Mode.STATUS_NO_FINGER;
//圆的宽高
private int mWidth;
private int mHeight;
//圆的半径
private int mRadius;
//画笔的宽度
private int mStrokeWidth = 2;
//圆的中心坐标点
private int mCenterX, mCenterY;
private Paint mPaint;
/**
* 箭头(小三角最长边的一半长度 = mArrawRate * mWidth / 2 )
*/
private float mArrowRate = 0.333f;
private int mArrowDegree = -1;
private Path mArrowPath;
/**
* 内圆的半径 = 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;
//获取圆的半径和圆点坐标
mRadius = mCenterX = mCenterY = mWidth / 2;
//减去画笔的宽度
mRadius -= mStrokeWidth;
//默认的三角位置为圆的正上方向上的剪头,然后根据两个GestureLockView来进行角度的旋转
//确定位置以及确定圆的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);//设置填充类型
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//根据不同的状态画圆
switch (mCurrentStatus) {
case STATUS_FINGER_ON:
//按下时画圆
//绘制外圆
mPaint.setColor(mColorFingerOn);
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);
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:
//没有按下的状态
//绘制外圆
mPaint.setStyle(Paint.Style.FILL);//完全填充
mPaint.setColor(mColorNoFingerOutter);
canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
//绘制内圆
mPaint.setColor(mColorNoFingerInner);
canvas.drawCircle(mCenterX, mCenterY, mRadius * mInnerCircleRadiusRate, mPaint);
break;
}
}
/**
* 绘制剪头
* canvas的save和restore的作用
* 这两个是成对出现的 save 可以保存当前canvas上以有的状态
* 一般我们需要是对某个特定元素进行旋转等操作,而这个操作又不想对其他的元素有影响,那么就可以使用这个
* restore就可以取出之前保存的状态
*/
private void drawArrow(Canvas canvas){
if(mArrowDegree != -1){
mPaint.setStyle(Paint.Style.FILL);
canvas.save();
canvas.rotate(mArrowDegree,mCenterX,mCenterY);//画布旋转
canvas.drawPath(mArrowPath,mPaint);//绘制路径,这里其实绘制两条线,不过因为画笔模式为完全填充,所以就构建成了一个三角形
canvas.restore();
}
}
/**
* 设置当前的模式并进行重新绘制
*/
public void setMode(Mode mode){
mCurrentStatus = mode;
invalidate();
}
/**
* 设置剪头的角度
* @param degree
*/
public void setmArrowDegree(int degree){
mArrowDegree = degree;
}
public int getmArrowDegree(){
return mArrowDegree;
}
}
然后是整体的布局,这里使用了自定义熟悉来设置一些参数,包括上面的颜色以及每行的个数,这里每行的个数和列是相互对应的,就是3*3,然后还有重试次数.自定义属性
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;
/**
* 抄的洪洋的手势解锁,这里有一个问题就是图像最开始的一个坐标点是从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中间的间距 设置为:mGestureLockViewWidth * 25%
*/
private int mMarginBetweenLockView = 30;
/**
* GestureLockView的边长 4 * mWidth / ( 5 * mCount + 1 )
*/
private int mGestureLockViewWidth;
/**
* GestureLockView无手指触摸的状态下内圆的颜色
*/
private int mNoFingerInnerCircleColor = 0xFF939090;
/**
* GestureLockView无手指触摸的状态下外圆的颜色
*/
private int mNoFingerOuterCircleColor = 0xFFE0DBDB;
/**
* GestureLockView手指触摸的状态下内圆和外圆的颜色
*/
private int mFingerOnColor = 0xFF378FC9;
/**
* GestureLockView手指抬起的状态下内圆和外圆的颜色
*/
private int mFingerUpColor = 0xFFFF0000;
/**
* 宽度
*/
private int mWidth;
/**
* 高度
*/
private int mHeight;
private Path mPath;
/**
* 指引线的开始位置x
*/
private int mLastPathX;
/**
* 指引线的开始位置y
*/
private int mLastPathY;
/**
* 指引下的结束位置
*/
private Point mTmpTarget = new Point();
/**
* 最大尝试次数
*/
private int mTryTimes = 4;
/**
* 回调接口
*/
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];
// 这里鸿神说是根据屏幕宽度进行百分比的布局..计算方法没看懂
// 计算每个GestureLockView的宽度
mGestureLockViewWidth = (int) (4 * mWidth * 1.0f / (5 * mCount + 1));
//计算每个GestureLockView的间距
mMarginBetweenLockView = (int) (mGestureLockViewWidth * 0.25);
// 设置画笔的宽度为GestureLockView的内圆直径稍微小点(不喜欢的话,随便设)
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);
// 不是每行的第一个,则设置位置为前一个的右边
if (i % mCount != 0) {
params.addRule(RelativeLayout.RIGHT_OF, mGestureLockViews[i - 1].getId());
}
// 从第二行开始,设置为上一行同一位置View的下面
if (i > mCount - 1) {
params.addRule(RelativeLayout.BELOW, mGestureLockViews[i - mCount].getId());
}
//设置右下左上的边距
int rightMargin = mMarginBetweenLockView;
int bootomMargin = mMarginBetweenLockView;
int leftMargin = 0;
int topMargin = 0;
//第一行有topMargin 第一列有leftMargin
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:
//手指按下的时候现把所有的重置
reSet();
break;
case MotionEvent.ACTION_UP:
//手指抬起的时候要进行剪头角度的测量和绘制,以及判断结果的正确与否
mPaint.setColor(mFingerUpColor);
mPaint.setAlpha(50);
this.mTryTimes--;
if (mOnGestureLockViewListener != null && mChoose.size() > 0) {
mOnGestureLockViewListener.onGestureEvent(checkAnswer());
if (mTryTimes <= 0) {
mOnGestureLockViewListener.onUnmatchedExceedBoundary();
}
}
//将终点设置为起点
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);
}
//设置划线的起点
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;
}
/**
* 恢复初始设置
*/
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;
}
/**
* 判断当前点是否在当前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;
}
/**
* 根据坐标点获取子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);
/**
* 超过尝试次数
*/
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>
布局调用,自定义的颜色没有加
<?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,"已经超出最大重试次数",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数据库的使用了,之前项目都是用的sql语句,反正写的要吐血,这次看看专门为移动的数据库会怎么样