From 48fd30c9ef5869bb3396b7f52d3eb9dde5540a9f Mon Sep 17 00:00:00 2001 From: Mr_Worldwide <1905563644@qq.com> Date: Thu, 15 Mar 2018 09:00:23 +0800 Subject: [PATCH] src\MyApplication\app\src\main\java\com\example\administrator\myapplication\widget\CircleImageView.java src\MyApplication\app\src\main\java\com\example\administrator\myapplication\widget\HTQDragGridView.java --- .../myapplication/widget/CircleImageView.java | 253 +++++++ .../myapplication/widget/HTQDragGridView.java | 682 ++++++++++++++++++ .../widget/NoteItemCircleView.java | 31 + .../myapplication/widget/ToggleButton.java | 336 +++++++++ .../widget/patternlock/LockPatternView.java | 400 ++++++++++ .../widget/patternlock/Point.java | 76 ++ .../widget/patternlock/RotateDegrees.java | 42 ++ 7 files changed, 1820 insertions(+) create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/CircleImageView.java create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/HTQDragGridView.java create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/NoteItemCircleView.java create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/ToggleButton.java create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/LockPatternView.java create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/Point.java create mode 100644 src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/RotateDegrees.java diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/CircleImageView.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/CircleImageView.java new file mode 100644 index 0000000..3295480 --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/CircleImageView.java @@ -0,0 +1,253 @@ +package com.htq.baidu.coolnote.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageView; +import com.htq.baidu.coolnote.R; + + +/** + * 圆形ImageView组件 + * + */ +public class CircleImageView extends ImageView { + + private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP; + + private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; + private static final int COLORDRAWABLE_DIMENSION = 1; + + private static final int DEFAULT_BORDER_WIDTH = 0; + private static final int DEFAULT_BORDER_COLOR = Color.BLACK; + + private final RectF mDrawableRect = new RectF(); + private final RectF mBorderRect = new RectF(); + + private final Matrix mShaderMatrix = new Matrix(); + private final Paint mBitmapPaint = new Paint(); + private final Paint mBorderPaint = new Paint(); + + private int mBorderColor = DEFAULT_BORDER_COLOR; + private int mBorderWidth = DEFAULT_BORDER_WIDTH; + + private Bitmap mBitmap; + private BitmapShader mBitmapShader; + private int mBitmapWidth; + private int mBitmapHeight; + + private float mDrawableRadius; + private float mBorderRadius; + + private boolean mReady; + private boolean mSetupPending; + + //默认显示圆形 + private boolean isDisplayCircle = true; + + public CircleImageView(Context context) { + this(context, null); + } + + public CircleImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CircleImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + super.setScaleType(SCALE_TYPE); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0); + + mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH); + mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR); + + a.recycle(); + + mReady = true; + + if (mSetupPending) { + setup(); + mSetupPending = false; + } + } + + @Override + public ScaleType getScaleType() { + return SCALE_TYPE; + } + + @Override + public void setScaleType(ScaleType scaleType) { + if (scaleType != SCALE_TYPE) { + throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType)); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if(!isDisplayCircle) { + super.onDraw(canvas); + return; + } + if (getDrawable() == null) { + return; + } + + canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint); + if(mBorderWidth != 0){ + canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + setup(); + } + + public void setDisplayCircle(boolean isDisplayCircle) { + this.isDisplayCircle = isDisplayCircle; + } + + public int getBorderColor() { + return mBorderColor; + } + + public void setBorderColor(int borderColor) { + if (borderColor == mBorderColor) { + return; + } + + mBorderColor = borderColor; + mBorderPaint.setColor(mBorderColor); + invalidate(); + } + + public int getBorderWidth() { + return mBorderWidth; + } + + public void setBorderWidth(int borderWidth) { + if (borderWidth == mBorderWidth) { + return; + } + + mBorderWidth = borderWidth; + setup(); + } + + @Override + public void setImageBitmap(Bitmap bm) { + super.setImageBitmap(bm); + mBitmap = bm; + setup(); + } + + @Override + public void setImageDrawable(Drawable drawable) { + super.setImageDrawable(drawable); + mBitmap = getBitmapFromDrawable(drawable); + setup(); + } + + @Override + public void setImageResource(int resId) { + super.setImageResource(resId); + mBitmap = getBitmapFromDrawable(getDrawable()); + setup(); + } + + private Bitmap getBitmapFromDrawable(Drawable drawable) { + if (drawable == null) { + return null; + } + + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + try { + Bitmap bitmap; + + if (drawable instanceof ColorDrawable) { + bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG); + } else { + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); + } + + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } catch (OutOfMemoryError e) { + return null; + } + } + + private void setup() { + if (!mReady) { + mSetupPending = true; + return; + } + + if (mBitmap == null) { + return; + } + + mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + + mBitmapPaint.setAntiAlias(true); + mBitmapPaint.setShader(mBitmapShader); + + mBorderPaint.setStyle(Paint.Style.STROKE); + mBorderPaint.setAntiAlias(true); + mBorderPaint.setColor(mBorderColor); + mBorderPaint.setStrokeWidth(mBorderWidth); + + mBitmapHeight = mBitmap.getHeight(); + mBitmapWidth = mBitmap.getWidth(); + + mBorderRect.set(0, 0, getWidth(), getHeight()); + mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2); + + mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth); + mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2); + + updateShaderMatrix(); + invalidate(); + } + + private void updateShaderMatrix() { + float scale; + float dx = 0; + float dy = 0; + + mShaderMatrix.set(null); + + if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) { + scale = mDrawableRect.height() / (float) mBitmapHeight; + dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f; + } else { + scale = mDrawableRect.width() / (float) mBitmapWidth; + dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f; + } + + mShaderMatrix.setScale(scale, scale); + mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth); + + mBitmapShader.setLocalMatrix(mShaderMatrix); + } + +} \ No newline at end of file diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/HTQDragGridView.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/HTQDragGridView.java new file mode 100644 index 0000000..077f6a4 --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/HTQDragGridView.java @@ -0,0 +1,682 @@ +package com.htq.baidu.coolnote.widget; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Vibrator; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; +import android.view.WindowManager; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.AdapterView; +import android.widget.GridView; +import android.widget.ImageView; +import android.widget.ListAdapter; + +import java.util.LinkedList; +import java.util.List; + +/** + * 感谢这篇博客的作者,http://blog.csdn.net/xiaanming/article/details/17718579
+ * 在这个基础上解决了原作者的问题:Adapter无法使用ViewHolder优化的问题,优化了手势识别率,并添加了trashView功能, + * 优化自定义控件对外扩展性,解决在上下拉环境下手势冲突问题
+ * + * + */ +public class HTQDragGridView extends GridView { + + private long dragResponseMS = 700; // item长按响应的时间 + private int mDragPosition;// 正在拖拽的position + + private boolean isDrag = false; // 是否可以拖拽,用于控件内部逻辑实现 + private boolean canDrag = true; // 是否可用拖拽,主要用于外部开放设置 + private boolean mAnimationEnd = true; + + private int mDownX; + private int mDownY; + private int moveX; + private int moveY; + + private View mStartDragItemView = null; // 刚开始拖拽的item对应的View + private ImageView mDragImageView; // 用于拖拽时显示的幻影镜像 + private Bitmap mDragBitmap; // 幻影镜像对应的Bitmap + private View mTrashView; // 删除item的垃圾桶图标 + + private final Vibrator mVibrator; // 震动器 + private final int mStatusHeight;// 状态栏的高度 + private final WindowManager mWindowManager; + private WindowManager.LayoutParams mWindowLayoutParams; // item镜像的布局参数 + + private int mPoint2ItemTop; // 按下的点到所在item的上边缘的距离 + private int mPoint2ItemLeft; + private int mOffset2Top; // DragGridView距离屏幕顶部的偏移量 + private int mOffset2Left; + + private int mDownScrollBorder; // DragGridView自动向下滚动的边界值 + private int mUpScrollBorder; // DragGridView自动向上滚动的边界值 + + private DragGridBaseAdapter mDragAdapter; + private int mNumColumns; + private int mColumnWidth; + private boolean mNumColumnsSet; + private int mHorizontalSpacing; + + private static final int speed = 20; // DragGridView自动滚动的速度 + private static final int MOVE_OFFSET = 25; + private boolean moved = false; + + public static final int HANDLE_START = 0x3587; + public static final int HANDLE_CANCLE = 0x3588; + public static final int HANDLE_FINISH = 0x3589; + private static OnMoveListener moveListener; // 拖拽开始与结束监听器 + private OnDeleteListener deleteListener; // 移动到垃圾桶时的监听器 + + private final TouchRect moveRect = new TouchRect(); + private final TouchRect gridRect = new TouchRect(); + private final TouchRect trashRect = new TouchRect(); + + public HTQDragGridView(Context context) { + this(context, null); + } + + public HTQDragGridView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public HTQDragGridView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mVibrator = (Vibrator) context + .getSystemService(Context.VIBRATOR_SERVICE); + mWindowManager = (WindowManager) context + .getSystemService(Context.WINDOW_SERVICE); + mStatusHeight = getStatusHeight(context); // 获取状态栏的高度 + + if (!mNumColumnsSet) { + mNumColumns = AUTO_FIT; + } + } + + /** + * 获取状态栏的高度 + * + * @param context + * @return + */ + private static int getStatusHeight(Context context) { + int statusHeight = 0; + Rect localRect = new Rect(); + + ((Activity) context).getWindow().getDecorView() + .getWindowVisibleDisplayFrame(localRect); + statusHeight = localRect.top; + if (0 == statusHeight) { + Class localClass; + try { + localClass = Class.forName("com.android.internal.R$dimen"); + Object localObject = localClass.newInstance(); + int i5 = Integer.parseInt(localClass + .getField("status_bar_height").get(localObject) + .toString()); + statusHeight = context.getResources().getDimensionPixelSize(i5); + } catch (Exception e) { + e.printStackTrace(); + } + } + return statusHeight; + } + + private static final Handler mHandler = new Handler() { + @Override + public void handleMessage(android.os.Message msg) { + if (moveListener != null) { + if (msg.what == HANDLE_START) { + moveListener.startMove(); + } else if (msg.what == HANDLE_FINISH) { + moveListener.finishMove(); + } else if (msg.what == HANDLE_CANCLE) { + moveListener.cancleMove(); + } + } + }; + }; + + // 用来处理是否为长按的Runnable + + private final Runnable mLongClickRunnable = new Runnable() { + @Override + public void run() { + if (!canDrag) { + return; + } + isDrag = true; // 设置可以拖拽 + moved = true; + mHandler.sendEmptyMessage(HANDLE_START); + mVibrator.vibrate(50); // 震动一下 + + mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏该item + + createDragImage(mDragBitmap, mDownX, mDownY); + mDragBitmap = null; + } + }; + + /** + * 若设置为AUTO_FIT,计算有多少列 + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mNumColumns == AUTO_FIT) { + int numFittedColumns = 1; // 可用列 + + if (mColumnWidth > 0) { + int gridWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec) + - getPaddingLeft() - getPaddingRight(), 0); + numFittedColumns = gridWidth / mColumnWidth; + if (numFittedColumns > 0) { + while (numFittedColumns != 1) { + if (numFittedColumns * mColumnWidth + + (numFittedColumns - 1) * mHorizontalSpacing > gridWidth) { + numFittedColumns--; + } else { + break; + } + } + } + } else { + numFittedColumns = 2; + } + mNumColumns = numFittedColumns; + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + initRecord(); + } + + /** + * 初始化坐标数据 + */ + private void initRecord() { + gridRect.left = this.getLeft(); + gridRect.right = this.getRight(); + gridRect.top = this.getTop(); + gridRect.bottom = this.getBottom(); + if (mTrashView != null) { + trashRect.left = mTrashView.getLeft(); + trashRect.right = mTrashView.getRight(); + trashRect.bottom = mTrashView.getBottom(); + trashRect.top = mTrashView.getTop(); + } + } + + /******************** preference method ********************/ + + @Override + public void setAdapter(ListAdapter adapter) { + super.setAdapter(adapter); + + if (adapter instanceof DragGridBaseAdapter) { + mDragAdapter = (DragGridBaseAdapter) adapter; + } else { + throw new IllegalStateException( + "the adapter must be implements DragGridAdapter"); + } + } + + @Override + public void setNumColumns(int numColumns) { + super.setNumColumns(numColumns); + mNumColumnsSet = true; + this.mNumColumns = numColumns; + } + + @Override + public void setColumnWidth(int columnWidth) { + super.setColumnWidth(columnWidth); + mColumnWidth = columnWidth; + } + + @Override + public void setHorizontalSpacing(int horizontalSpacing) { + super.setHorizontalSpacing(horizontalSpacing); + this.mHorizontalSpacing = horizontalSpacing; + } + + /** + * 设置响应拖拽的毫秒数,默认是700毫秒 + * + * @param dragResponseMS + */ + public void setDragResponseMS(long dragResponseMS) { + this.dragResponseMS = dragResponseMS; + } + + public void setOnDeleteListener(OnDeleteListener l) { + this.deleteListener = l; + } + + public void setTrashView(View trashView) { + this.mTrashView = trashView; + } + + public void setDragEnable(boolean isDrag) { + this.canDrag = isDrag; + if (canDrag) { + mHandler.removeCallbacks(mLongClickRunnable); + } + } + + /******************** touch method ********************/ + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (canDrag) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mDownX = (int) ev.getX(); + mDownY = (int) ev.getY(); + moveRect.left = mDownX - MOVE_OFFSET; + moveRect.right = mDownX + MOVE_OFFSET; + moveRect.top = mDownY - MOVE_OFFSET; + moveRect.bottom = mDownY + MOVE_OFFSET; + + // 根据按下的X,Y坐标获取所点击item的position + mDragPosition = pointToPosition(mDownX, mDownY); + + if (mDragPosition == AdapterView.INVALID_POSITION) { + return super.dispatchTouchEvent(ev); + } + + // 使用Handler延迟dragResponseMS执行mLongClickRunnable + mHandler.postDelayed(mLongClickRunnable, dragResponseMS); + + // 根据position获取该item所对应的View + mStartDragItemView = getChildAt(mDragPosition + - getFirstVisiblePosition()); + + mPoint2ItemTop = mDownY - mStartDragItemView.getTop(); + mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft(); + + mOffset2Top = (int) (ev.getRawY() - mDownY); + mOffset2Left = (int) (ev.getRawX() - mDownX); + + // 获取DragGridView自动向上滚动的偏移量,小于这个值,DragGridView向下滚动 + mDownScrollBorder = getHeight() / 5; + // 大于这个值,DragGridView向上滚动 + mUpScrollBorder = getHeight() * 4 / 5; + + // 开启mDragItemView绘图缓存 + mStartDragItemView.setDrawingCacheEnabled(true); + // 获取mDragItemView在缓存中的Bitmap对象 + mDragBitmap = Bitmap.createBitmap(mStartDragItemView + .getDrawingCache()); + // 这一步很关键,释放绘图缓存,避免出现重复的镜像 + mStartDragItemView.destroyDrawingCache(); + break; + case MotionEvent.ACTION_MOVE: + // 如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnable + if (!isTouchInItem(moveRect, ev.getX(), ev.getY())) { + mHandler.removeCallbacks(mLongClickRunnable); + } + break; + case MotionEvent.ACTION_UP: + mHandler.removeCallbacks(mScrollRunnable); + mHandler.removeCallbacks(mLongClickRunnable); + if (moved && getAdapter().getCount() > 0) { + mHandler.sendEmptyMessage(HANDLE_FINISH); + } else { + mHandler.sendEmptyMessage(HANDLE_CANCLE); + } + moved = false; + break; + } + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (isDrag && canDrag && mDragImageView != null) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + initRecord(); + break; + case MotionEvent.ACTION_MOVE: + moveX = (int) ev.getX(); + moveY = (int) ev.getY(); + + onDragItem(moveX, moveY);// 拖动item + + if (mTrashView != null) { + if (inTrash(moveX, moveY)) { + mTrashView.setScaleX(1.7f); + mTrashView.setScaleY(1.7f); + } else { + mTrashView.setScaleX(1f); + mTrashView.setScaleY(1f); + } + } + break; + case MotionEvent.ACTION_UP: + onStopDrag(); + isDrag = false; + if (deleteListener != null && inTrash(ev.getX(), ev.getY())) { + deleteListener.onDelete(mDragPosition); + } + break; + } + return true; + } + return super.onTouchEvent(ev); + } + + /** + * 是否点击在GridView的item上面 + * + * @param x + * @param y + * @return + */ + private boolean isTouchInItem(TouchRect moveRect, float x, float y) { + // 防止手抖的处理,如果是横向在item上移动是没有影响的, + // 但是纵向由于外层上下拉控件还是会有影响,具体解决请看NoteBookFragment类中的mSwipeRefreshLayout.setOnTouchListener方法 + if (x < moveRect.right && x > moveRect.left && y < moveRect.bottom + && y > moveRect.top) { + return true; + } else { + return false; + } + } + + /** + * 创建拖动的镜像 + * + * @param bitmap + * @param downX + * 按下的点相对父控件的X坐标 + * @param downY + * 按下的点相对父控件的X坐标 + */ + private void createDragImage(Bitmap bitmap, int downX, int downY) { + mWindowLayoutParams = new WindowManager.LayoutParams(); + mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; // 图片之外的其他地方透明 + + mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; + mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left; + mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top + - mStatusHeight; + mWindowLayoutParams.alpha = 0.55f; // 透明度 + + + mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; + mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; + mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + + mDragImageView = new ImageView(getContext()); + mDragImageView.setImageBitmap(bitmap); + mWindowManager.addView(mDragImageView, mWindowLayoutParams); + } + + /** + * 从界面上面移动拖动镜像 + */ + private void removeDragImage() { + if (mDragImageView != null) { + mWindowManager.removeView(mDragImageView); + mDragImageView = null; + } + } + + /** + * 拖动item,在里面实现了item镜像的位置更新,item的相互交换以及GridView的自行滚动 + * + * @param moveX + * @param moveY + */ + private void onDragItem(int moveX, int moveY) { + mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left; + mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top + - mStatusHeight; + mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); // 更新镜像的位置 + onSwapItem(moveX, moveY); + // GridView自动滚动 + mHandler.post(mScrollRunnable); + } + + /** + * 手指当前处于垃圾桶图标上 + * + * @param x + * @param y + * @return + */ + private boolean inTrash(float x, float y) { + x += gridRect.left; + y += gridRect.top; + if (x > trashRect.left && x < trashRect.right && y > trashRect.top + && y < trashRect.bottom) { + if (mHandler != null && mScrollRunnable != null) { + mHandler.removeCallbacks(mScrollRunnable); + } + if (mDragImageView != null) { + mDragImageView.setScaleX(0.6f); + mDragImageView.setScaleY(0.6f); + } + return true; + } else { + if (mDragImageView != null) { + mDragImageView.setScaleX(1f); + mDragImageView.setScaleY(1f); + } + return false; + } + } + + /** + * 当moveY的值大于向上滚动的边界值,触发GridView自动向上滚动 当moveY的值小于向下滚动的边界值,触发GridView自动向下滚动 + * 否则不进行滚动 + */ + private final Runnable mScrollRunnable = new Runnable() { + + @Override + public void run() { + int scrollY; + if (getFirstVisiblePosition() == 0 + || getLastVisiblePosition() == getCount() - 1) { + mHandler.removeCallbacks(mScrollRunnable); + } + + if (moveY > mUpScrollBorder) { + scrollY = speed; + mHandler.postDelayed(mScrollRunnable, 25); + } else if (moveY < mDownScrollBorder) { + scrollY = -speed; + mHandler.postDelayed(mScrollRunnable, 25); + } else { + scrollY = 0; + mHandler.removeCallbacks(mScrollRunnable); + } + + smoothScrollBy(scrollY, 10); + } + }; + + /** + * 交换item,并且控制item之间的显示与隐藏效果 + * + * @param moveX + * @param moveY + */ + private void onSwapItem(int moveX, int moveY) { + // 获取我们手指移动到的那个item的position + + final int tempPosition = pointToPosition(moveX, moveY); + + // 假如tempPosition 改变了并且tempPosition不等于-1,则进行交换 + + if (tempPosition != mDragPosition + && tempPosition != AdapterView.INVALID_POSITION + && mAnimationEnd) { + mDragAdapter.reorderItems(mDragPosition, tempPosition); + mDragAdapter.setHideItem(tempPosition); + + final ViewTreeObserver observer = getViewTreeObserver(); + observer.addOnPreDrawListener(new OnPreDrawListener() { + + @Override + public boolean onPreDraw() { + observer.removeOnPreDrawListener(this); + animateReorder(mDragPosition, tempPosition); + mDragPosition = tempPosition; + return true; + } + }); + + } + } + + /** + * 创建移动动画 + * + * @param view + * @param startX + * @param endX + * @param startY + * @param endY + * @return + */ + private AnimatorSet createTranslationAnimations(View view, float startX, + float endX, float startY, float endY) { + ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX", + startX, endX); + ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY", + startY, endY); + AnimatorSet animSetXY = new AnimatorSet(); + animSetXY.playTogether(animX, animY); + return animSetXY; + } + + /** + * item的交换动画效果 + * + * @param oldPosition + * @param newPosition + */ + private void animateReorder(final int oldPosition, final int newPosition) { + boolean isForward = newPosition > oldPosition; + List resultList = new LinkedList(); + if (isForward) { + for (int pos = oldPosition; pos < newPosition; pos++) { + View view = getChildAt(pos - getFirstVisiblePosition()); + if (view == null) { + continue; + } + if ((pos + 1) % mNumColumns == 0) { + resultList.add(createTranslationAnimations(view, + -view.getWidth() * (mNumColumns - 1), 0, + view.getHeight(), 0)); + } else { + resultList.add(createTranslationAnimations(view, + view.getWidth(), 0, 0, 0)); + } + } + } else { + for (int pos = oldPosition; pos > newPosition; pos--) { + View view = getChildAt(pos - getFirstVisiblePosition()); + if ((pos + mNumColumns) % mNumColumns == 0) { + resultList.add(createTranslationAnimations(view, + view.getWidth() * (mNumColumns - 1), 0, + -view.getHeight(), 0)); + } else { + resultList.add(createTranslationAnimations(view, + -view.getWidth(), 0, 0, 0)); + } + } + } + + AnimatorSet resultSet = new AnimatorSet(); + resultSet.playTogether(resultList); + resultSet.setDuration(300); + resultSet.setInterpolator(new AccelerateDecelerateInterpolator()); + resultSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mAnimationEnd = false; + } + + @Override + public void onAnimationEnd(Animator animation) { + mAnimationEnd = true; + } + }); + resultSet.start(); + } + + /** + * 停止拖拽我们将之前隐藏的item显示出来,并将镜像移除 + */ + private void onStopDrag() { + View view = getChildAt(mDragPosition - getFirstVisiblePosition()); + if (view != null) { + view.setVisibility(View.VISIBLE); + } + mDragAdapter.setHideItem(-1); + removeDragImage(); + + if (mTrashView != null) { + mTrashView.setScaleX(1f); + mTrashView.setScaleY(1f); + } + } + + public void setOnMoveListener(OnMoveListener l) { + moveListener = l; + } + + public interface OnMoveListener { + void startMove(); + + void finishMove(); + + void cancleMove(); + } + + public interface OnDeleteListener { + void onDelete(int position); + } + + public interface DragGridBaseAdapter { + /** + * 移动时回调 + */ + public void reorderItems(int oldPosition, int newPosition); + + /** + * 隐藏时回调 + */ + public void setHideItem(int hidePosition); + } + + private class TouchRect { + int top; + int bottom; + int left; + int right; + } +} \ No newline at end of file diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/NoteItemCircleView.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/NoteItemCircleView.java new file mode 100644 index 0000000..1db79f0 --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/NoteItemCircleView.java @@ -0,0 +1,31 @@ +package com.htq.baidu.coolnote.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; + +/** + * 圆形ImageView组件 点击事件不会穿透 + * + * @author kymjs(kymjs123@gmail.com) + */ +public class NoteItemCircleView extends CircleImageView { + + public NoteItemCircleView(Context context) { + super(context); + } + + public NoteItemCircleView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NoteItemCircleView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); + return true; + } +} \ No newline at end of file diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/ToggleButton.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/ToggleButton.java new file mode 100644 index 0000000..732efae --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/ToggleButton.java @@ -0,0 +1,336 @@ +package com.htq.baidu.coolnote.widget; + +import android.animation.ObjectAnimator; +import android.animation.TypeEvaluator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Cap; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Property; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.htq.baidu.coolnote.R; + + +/** + * @author Qiujuer + */ +public class ToggleButton extends View { + /** */ + private float radius; + /** + * 开启颜色 + */ + private int onColor = Color.parseColor("#4ebb7f"); + /** + * 关闭颜色 + */ + private int offBorderColor = Color.parseColor("#dadbda"); + /** + * 灰色带颜色 + */ + private int offColor = Color.parseColor("#ffffff"); + /** + * 手柄颜色 + */ + private int spotColor = Color.parseColor("#ffffff"); + /** + * 边框颜色 + */ + private int borderColor = offBorderColor; + /** + * 画笔 + */ + private Paint paint; + /** + * 开关状态 + */ + private boolean toggleOn = false; + /** + * 边框大小 + */ + private int borderWidth = 2; + /** + * 垂直中心 + */ + private float centerY; + /** + * 按钮的开始和结束位置 + */ + private float startX, endX; + /** + * 手柄X位置的最小和最大值 + */ + private float spotMinX, spotMaxX; + /** + * 手柄大小 + */ + private int spotSize; + /** + * 手柄X位置 + */ + private float spotX; + /** + * 关闭时内部灰色带高度 + */ + private float offLineWidth; + /** */ + private RectF rect = new RectF(); + + private OnToggleChanged listener; + + private ToggleButton(Context context) { + super(context); + } + + public ToggleButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(attrs); + } + + public ToggleButton(Context context, AttributeSet attrs) { + super(context, attrs); + setup(attrs); + } + + public void setup(AttributeSet attrs) { + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStyle(Style.FILL); + paint.setStrokeCap(Cap.ROUND); + + this.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + toggle(); + } + }); + + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ToggleButton); + offBorderColor = typedArray.getColor(R.styleable.ToggleButton_offBorderColor, offBorderColor); + onColor = typedArray.getColor(R.styleable.ToggleButton_onColor, onColor); + spotColor = typedArray.getColor(R.styleable.ToggleButton_spotColor, spotColor); + offColor = typedArray.getColor(R.styleable.ToggleButton_offColor, offColor); + borderWidth = typedArray.getDimensionPixelSize(R.styleable.ToggleButton_toggle_border_width, borderWidth); + typedArray.recycle(); + } + + public void toggle() { + toggleOn = !toggleOn; + animateCheckedState(toggleOn); + if (listener != null) { + listener.onToggle(toggleOn); + } + } + + public void toggleOn() { + setToggleOn(); + if (listener != null) { + listener.onToggle(toggleOn); + } + } + + public void toggleOff() { + setToggleOff(); + if (listener != null) { + listener.onToggle(toggleOn); + } + } + + /** + * 设置显示成打开样式,不会触发toggle事件 + */ + public void setToggleOn() { + toggleOn = true; + setAnimatorProperty(true); + } + + /** + * 设置显示成关闭样式,不会触发toggle事件 + */ + public void setToggleOff() { + toggleOn = false; + setAnimatorProperty(false); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + + final int width = getWidth(); + final int height = getHeight(); + radius = Math.min(width, height) * 0.5f; + centerY = radius; + startX = radius; + endX = width - radius; + spotMinX = startX + borderWidth; + spotMaxX = endX - borderWidth; + spotSize = height - 4 * borderWidth; + + // update values + setAnimatorProperty(toggleOn); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + rect.set(0, 0, getWidth(), getHeight()); + paint.setColor(borderColor); + canvas.drawRoundRect(rect, radius, radius, paint); + + if (offLineWidth > 0) { + final float cy = offLineWidth * 0.5f; + rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy); + paint.setColor(offColor); + canvas.drawRoundRect(rect, cy, cy, paint); + } + + rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius); + paint.setColor(borderColor); + canvas.drawRoundRect(rect, radius, radius, paint); + + final float spotR = spotSize * 0.5f; + rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR); + paint.setColor(spotColor); + canvas.drawRoundRect(rect, spotR, spotR, paint); + } + + /** + * ============================================================================================= + * The Animate + * ============================================================================================= + */ + private static final Interpolator ANIMATION_INTERPOLATOR = new DecelerateInterpolator(); + private static final int ANIMATION_DURATION = 280; + private ObjectAnimator mAnimator; + + private void animateCheckedState(boolean newCheckedState) { + AnimatorProperty property = new AnimatorProperty(); + if (newCheckedState) { + property.color = onColor; + property.offLineWidth = 10; + property.spotX = spotMaxX; + } else { + property.color = offBorderColor; + property.offLineWidth = spotSize; + property.spotX = spotMinX; + } + + if (mAnimator == null) { + mAnimator = ObjectAnimator.ofObject(this, ANIM_VALUE, new AnimatorEvaluator(mCurProperty), property); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) + mAnimator.setAutoCancel(true); + mAnimator.setDuration(ANIMATION_DURATION); + mAnimator.setInterpolator(ANIMATION_INTERPOLATOR); + } else { + mAnimator.cancel(); + mAnimator.setObjectValues(property); + } + mAnimator.start(); + } + + /** + * ============================================================================================= + * The custom properties + * ============================================================================================= + */ + + private AnimatorProperty mCurProperty = new AnimatorProperty(); + + private void setAnimatorProperty(AnimatorProperty property) { + this.spotX = property.spotX; + this.borderColor = property.color; + this.offLineWidth = property.offLineWidth; + invalidate(); + } + + private void setAnimatorProperty(boolean isOn) { + AnimatorProperty property = mCurProperty; + if (isOn) { + property.color = onColor; + property.offLineWidth = 10; + property.spotX = spotMaxX; + } else { + property.color = offBorderColor; + property.offLineWidth = spotSize; + property.spotX = spotMinX; + } + setAnimatorProperty(property); + } + + private final static class AnimatorProperty { + private int color; + private float offLineWidth; + private float spotX; + } + + private final static class AnimatorEvaluator implements TypeEvaluator { + private final AnimatorProperty mProperty; + + public AnimatorEvaluator(AnimatorProperty property) { + mProperty = property; + } + + @Override + public AnimatorProperty evaluate(float fraction, AnimatorProperty startValue, AnimatorProperty endValue) { + // Values + mProperty.spotX = (int) (startValue.spotX + (endValue.spotX - startValue.spotX) * fraction); + + mProperty.offLineWidth = (int) (startValue.offLineWidth + (endValue.offLineWidth - startValue.offLineWidth) * (1 - fraction)); + + // Color + int startA = (startValue.color >> 24) & 0xff; + int startR = (startValue.color >> 16) & 0xff; + int startG = (startValue.color >> 8) & 0xff; + int startB = startValue.color & 0xff; + + int endA = (endValue.color >> 24) & 0xff; + int endR = (endValue.color >> 16) & 0xff; + int endG = (endValue.color >> 8) & 0xff; + int endB = endValue.color & 0xff; + + mProperty.color = (startA + (int) (fraction * (endA - startA))) << 24 | + (startR + (int) (fraction * (endR - startR))) << 16 | + (startG + (int) (fraction * (endG - startG))) << 8 | + (startB + (int) (fraction * (endB - startB))); + + return mProperty; + } + } + + private final static Property ANIM_VALUE = new Property(AnimatorProperty.class, "animValue") { + @Override + public AnimatorProperty get(ToggleButton object) { + return object.mCurProperty; + } + + @Override + public void set(ToggleButton object, AnimatorProperty value) { + object.setAnimatorProperty(value); + } + }; + + + /** + * @author ThinkPad + */ + public interface OnToggleChanged { + /** + * @param on + */ + public void onToggle(boolean on); + } + + + public void setOnToggleChanged(OnToggleChanged onToggleChanged) { + listener = onToggleChanged; + } +} diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/LockPatternView.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/LockPatternView.java new file mode 100644 index 0000000..7b867a6 --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/LockPatternView.java @@ -0,0 +1,400 @@ +package com.htq.baidu.coolnote.widget.patternlock; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.support.design.widget.Snackbar; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Display; +import android.view.MotionEvent; +import android.view.View; + + +import com.htq.baidu.coolnote.R; + +import java.util.ArrayList; +import java.util.List; + + +public class LockPatternView extends View { + //判断线的状态 + private boolean isLineState = true; + //判断点是否被实例化了 + private boolean isInitPoint = false;//不能被定义为static,否则很可能导致mPoints内存泄漏 + //判断手指是否离开屏幕 + private boolean isFinish = false; + //判断手指点击屏幕时是否选中了九宫格中的点 + private boolean isSelect = false; + // 创建MyPoint的数组 + private Point[][] mPoints; + // 声明屏幕的宽和高 + private int mScreenHeight; + private int mScreenWidth; + // 声明点线的图片的半径 + private float mPointRadius; + // 声明线的图片的高(即是半径) + private float mLineHeight; + // 声明鼠标移动的x,y坐标 + private float mMoveX, mMoveY; + // 声明屏幕上的宽和高的偏移量 + private int mScreenHeightOffSet = 0; + private int mScreenWidthOffSet = 0; + // 创建一个画笔 + private Paint mPaint; + // 声明资源图片 + private Bitmap mBitmapNormal; + private Bitmap mBitmapPressed; + private Bitmap mBitmapError; + private Bitmap mLinePressed; + private Bitmap mLineError; + // 创建一个矩阵 + private Matrix mMatrix = new Matrix(); + // 创建MyPoint的列表 + private List mPointList = new ArrayList(); + // 实例化鼠标点 + private Point mMousePoint = new Point(); + // 用获取从activity中传过来的密码字符串 + private String mPassword = ""; + + private Context mContext; + private OnLockListener mListener; + private Activity activity; + + public LockPatternView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mContext = context; + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPoints = new Point[3][3]; + } + + public LockPatternView(Context context, AttributeSet attrs) { + super(context, attrs); + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPoints = new Point[3][3]; + } + + public LockPatternView(Context context) { + super(context); + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPoints = new Point[3][3]; + } + + /** + * 画点和画线 + */ + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (!isInitPoint) { + initPoint(); // 先初始化 + } + canvasPoint(canvas); // 开始画点 + + // 开始画线 + if (mPointList.size() > 0) { + Point b = null; + Point a = mPointList.get(0); + for (int i = 1; i < mPointList.size(); i++) { + b = mPointList.get(i); + canvasLine(a, b, canvas); + a = b; + } + if (!isFinish) { + canvasLine(a, mMousePoint, canvas); + } + } + if(isFinish) + { + + try { + Thread.sleep(700); + } catch (InterruptedException e) { + e.printStackTrace(); + } + changePointToNormal(); + postInvalidate(); + } + } + + + /** + * @author htq + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + // + Display d=activity.getWindowManager().getDefaultDisplay(); + setMeasuredDimension(d.getWidth(),d.getWidth()); + } + + + /** + * 手指点击手机屏幕 + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + mMoveX = event.getX(); + mMoveY = event.getY(); + // 设置移动点的坐标 + mMousePoint.setX(mMoveX); + mMousePoint.setY(mMoveY); + Point mPoint = null; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + isLineState = true; + isFinish = false; +// // 每次点击时就会将pointList中元素设置转化成普通状态 +// for (int i = 0; i < mPointList.size(); i++) { +// mPointList.get(i).setState(Point.BITMAP_NORMAL); +// } +// // 将pointList中的元素清除掉 +// mPointList.clear(); + changePointToNormal(); + // 判断是否点中了九宫格中的点 + mPoint = getIsSelectedPoint(mMoveX, mMoveY); + if (mPoint != null) { + isSelect = true; + } + break; + case MotionEvent.ACTION_MOVE: + if (isSelect == true) { + mPoint = getIsSelectedPoint(mMoveX, mMoveY); + } + + break; + case MotionEvent.ACTION_UP: + isFinish = true; + isSelect = false; + // 规定至少要有4个点被连线才有可能是正确 + // 其他种情况都是错误的 + if (mPointList.size() >= 4) {// 正确情况 + for (int j = 0; j < mPointList.size(); j++) { + mPassword += mPointList.get(j).getIndex(); + } + Log.i("password",mPassword); + //将连线后得到的密码传给activity + mListener.getStringPassword(mPassword); + mPassword = ""; + //经过activity判断传过来是否正确 + if (mListener.isPassword()) { + for (int i = 0; i < mPointList.size(); i++) { + mPointList.get(i).setState(Point.BITMAP_PRESS); + + } + isLineState = true; + } else { + for (int i = 0; i < mPointList.size(); i++) { + mPointList.get(i).setState(Point.BITMAP_ERROR); + + } + Log.i("correct","is not correct"); + isLineState = false; + } + // 错误情况 + } else if (mPointList.size() < 4 && mPointList.size() > 1) { + for (int i = 0; i < mPointList.size(); i++) { + mPointList.get(i).setState(Point.BITMAP_ERROR); + Snackbar.make(this,"为了您的App安全,手势至少得包含4个点",Snackbar.LENGTH_LONG).show(); + } + isLineState = false; + // 如果只有一个点被点中时为普通情况 + } else if (mPointList.size() == 1) { + for (int i = 0; i < mPointList.size(); i++) { + mPointList.get(i).setState(Point.BITMAP_NORMAL); + } + } + break; + + } + // 将mPoint添加到pointList中 + if (isSelect && mPoint != null) { + if (mPoint.getState() == Point.BITMAP_NORMAL) { + mPoint.setState(Point.BITMAP_PRESS); + mPointList.add(mPoint); + } + } +// Log.i("draw","draw -----"); + // 每次发生OnTouchEvent()后都刷新View + postInvalidate(); + return true; + } + + /** + * @author htq + */ + private void changePointToNormal() + { + // 每次点击时就会将pointList中元素设置转化成普通状态 + for (int i = 0; i < mPointList.size(); i++) { + mPointList.get(i).setState(Point.BITMAP_NORMAL); + } + // 将pointList中的元素清除掉 + mPointList.clear(); + } + + /** + * 判断九宫格中的某个点是否被点中了,或者某个点能否被连线 + * + * @param moveX + * @param moveY + * @return + */ + private Point getIsSelectedPoint(float moveX, float moveY) { + Point myPoint = null; + for (int i = 0; i < mPoints.length; i++) { + for (int j = 0; j < mPoints[i].length; j++) { + if (mPoints[i][j].isWith(mPoints[i][j], moveX, moveY, + mPointRadius)) { + myPoint = mPoints[i][j]; + } + } + } + + return myPoint; + } + + /** + * 画线 + * + * @param a 起始点 + * @param b 目的点 + * @param canvas 画布 + */ + private void canvasLine(Point a, Point b, Canvas canvas) { + // Math.sqrt(平方+平方) + float abInstance = (float) Math.sqrt( + (a.getX() - b.getX()) * (a.getX() - b.getX()) + + (a.getY() - b.getY()) * (a.getY() - b.getY()) + ); + canvas.rotate(RotateDegrees.getDegrees(a, b), a.getX(), a.getY()); + + mMatrix.setScale(abInstance / mLineHeight, 1); + mMatrix.postTranslate(a.getX(), a.getY()); + if (isLineState) { + canvas.drawBitmap(mLinePressed, mMatrix, mPaint); + } else { + canvas.drawBitmap(mLineError, mMatrix, mPaint); + } + + canvas.rotate(-RotateDegrees.getDegrees(a, b), a.getX(), a.getY()); + } + + /** + * 画点 + * + * @param canvas + */ + private void canvasPoint(Canvas canvas) { + + for (int i = 0; i < mPoints.length; i++) { + for (int j = 0; j < mPoints[i].length; j++) { + if (mPoints[i][j].getState() == Point.BITMAP_NORMAL) { + canvas.drawBitmap(mBitmapNormal, + mPoints[i][j].getX() - mPointRadius, + mPoints[i][j].getY() - mPointRadius, mPaint); + } else if (mPoints[i][j].getState() == Point.BITMAP_PRESS) { + canvas.drawBitmap(mBitmapPressed, + mPoints[i][j].getX() - mPointRadius, + mPoints[i][j].getY() - mPointRadius, mPaint); + } else { + canvas.drawBitmap(mBitmapError, + mPoints[i][j].getX() - mPointRadius, + mPoints[i][j].getY() - mPointRadius, mPaint); + } + } + } + } + + /** + * 实例化九宫格中所有点和所有的资源图片 + */ + private void initPoint() { + // 获取View的宽高 + mScreenWidth = getWidth(); + mScreenHeight = getHeight(); + if (mScreenHeight > mScreenWidth) { + // 获取y轴上的偏移量 + mScreenHeightOffSet = (mScreenHeight - mScreenWidth) / 2; + // 将屏幕高的变量设置成与宽相等,目的是为了new Point(x,y)时方便操作 + mScreenHeight = mScreenWidth; + } else { + // 获取x轴上的偏移量 + mScreenWidthOffSet = (mScreenWidth - mScreenHeight) / 2; + // 将屏幕宽的变量设置成与高相等,目的是为了new Point(x,y)时方便操作 + mScreenWidth = mScreenHeight; + } + + /** + * 实例化所有的资源图片 + */ + mBitmapError = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_error); + mBitmapNormal = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_normal); + mBitmapPressed = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_pressed); + mLineError = BitmapFactory.decodeResource(getResources(), R.drawable.line_error); + mLinePressed = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed); + + mPointRadius = mBitmapNormal.getWidth() / 2; + mLineHeight = mLinePressed.getHeight(); + + /** + * 开始实例化九宫格中点 + */ + mPoints[0][0] = new Point(mScreenWidthOffSet + mScreenWidth / 4, + mScreenHeightOffSet + mScreenHeight / 4); + mPoints[0][1] = new Point(mScreenWidthOffSet + mScreenWidth / 2, + mScreenHeightOffSet + mScreenHeight / 4); + mPoints[0][2] = new Point(mScreenWidthOffSet + mScreenWidth * 3 / 4, + mScreenHeightOffSet + mScreenHeight / 4); + + mPoints[1][0] = new Point(mScreenWidthOffSet + mScreenWidth / 4, + mScreenHeightOffSet + mScreenHeight / 2); + mPoints[1][1] = new Point(mScreenWidthOffSet + mScreenWidth / 2, + mScreenHeightOffSet + mScreenHeight / 2); + mPoints[1][2] = new Point(mScreenWidthOffSet + mScreenWidth * 3 / 4, + mScreenHeightOffSet + mScreenHeight / 2); + + mPoints[2][0] = new Point(mScreenWidthOffSet + mScreenWidth / 4, + mScreenHeightOffSet + mScreenHeight * 3 / 4); + mPoints[2][1] = new Point(mScreenWidthOffSet + mScreenWidth / 2, + mScreenHeightOffSet + mScreenHeight * 3 / 4); + mPoints[2][2] = new Point(mScreenWidthOffSet + mScreenWidth * 3 / 4, + mScreenHeightOffSet + mScreenHeight * 3 / 4); + + + // 设置九宫格中的各个index + int index = 1; + for (int i = 0; i < mPoints.length; i++) { + for (int j = 0; j < mPoints[i].length; j++) { + mPoints[i][j].setIndex(index + ""); + // 在没有任何操作的情况下默認点的状态 + mPoints[i][j].setState(Point.BITMAP_NORMAL); + index++; + } + } + + // 将isInitPoint设置为true + isInitPoint = true; + } + + public interface OnLockListener { + public void getStringPassword(String password); + + + // public void setErrorMsg(String msg); + // public void setSuccessMsg(String msg); + public boolean isPassword(); + } + + + public void setLockListener(OnLockListener listener) { + this.mListener = listener; + } + public void setActivityContext(Activity aty){this.activity=aty;} +} diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/Point.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/Point.java new file mode 100644 index 0000000..968ba0c --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/Point.java @@ -0,0 +1,76 @@ +package com.htq.baidu.coolnote.widget.patternlock; + +public class Point { + public static int BITMAP_NORMAL = 0; // 正常 + public static int BITMAP_ERROR = 1; // 错误 + public static int BITMAP_PRESS = 2; // 按下 + + //九宫格中的点的下标(即每个点代表一个值) + private String index; + //点的状态 + private int state; + //点的坐标 + private float x; + private float y; + + public Point() { + super(); + } + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + public String getIndex() { + return index; + } + + public int getState() { + return state; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public void setIndex(String index) { + this.index = index; + } + + public void setState(int state) { + this.state = state; + } + + public void setX(float x) { + this.x = x; + } + + public void setY(float y) { + this.y = y; + } + + /** + * 判断屏幕上的九宫格中的点能否可以进行连线 + * + * @param a + * @param moveX + * @param moveY + * @param radius 点bitmap的半径 + * @return 布尔型 + */ + public boolean isWith(Point a, float moveX, float moveY, float radius) { + float result = (float) Math.sqrt((a.getX() - moveX) + * (a.getX() - moveX) + (a.getY() - moveY) + * (a.getY() - moveY)); + if (result < 5 * radius / 4) { + return true; + } + return false; + } + +} diff --git a/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/RotateDegrees.java b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/RotateDegrees.java new file mode 100644 index 0000000..a67c391 --- /dev/null +++ b/src/MyApplication/app/src/main/java/com/example/administrator/myapplication/widget/patternlock/RotateDegrees.java @@ -0,0 +1,42 @@ +package com.htq.baidu.coolnote.widget.patternlock; + +public class RotateDegrees { + + public static float getDegrees(Point a, Point b) { + float degrees = 0; + float ax = a.getX(); + float ay = a.getY(); + float bx = b.getX(); + float by = b.getY(); + + if (ax == bx) { + if (by > ay) { + degrees = 90; + } else { + degrees = 270; + } + } else if (by == ay) { + if (ax > bx) { + degrees = 180; + } else { + degrees = 0; + } + } else { + if (ax > bx) { + if (ay > by) { // 第三象限 + degrees = 180 + (float) (Math.atan2(ay - by, ax - bx) * 180 / Math.PI); + } else { // 第二象限 + degrees = 180 - (float) (Math.atan2(by - ay, ax - bx) * 180 / Math.PI); + } + } else { + if (ay > by) { // 第四象限 + degrees = 360 - (float) (Math.atan2(ay - by, bx - ax) * 180 / Math.PI); + } else { // 第一象限 + degrees = (float) (Math.atan2(by - ay, bx - ax) * 180 / Math.PI); + } + } + } + return degrees; + } + +}