自定义手势解锁
来源:互联网 发布:广告语录音制作软件 编辑:程序博客网 时间:2024/04/28 00:00
逼逼几句
- 这几天由于UI妹子的设计图还在审核,让我有时间研究了一下手势解锁的原理,到这里得感谢一下UI妹子,废话我就少说了,前几天我在CSDN翔神的博客上看见了一篇关于手势解锁的文章,主要平时我一般亏心事干得的,所以就研究研究,改一下其中的东西,修修了bug,为自己所用了,看上感觉这个自定义的很复杂,各种状态,好几种颜色,看着都有种不想做的冲动,想要成功,必先苦其心志,然后饿几天,当你有孔子的心态的时候,此刻你就应该拥有觉老师的分享,没有破釜沉舟的心态,就算天蓬元帅八戒来了也带不动你!!!又说多了,说这篇文章,你可以设置每行3个或者4个按钮,设置你自己想要的Color,让你一直过瘾为止。
直接上效果图
- 这里我制作GIF比较麻烦就搞了一种状态
看完效果图之后,正常的你,应该有想下看的冲动,所以讲大致的思路
思路:
一. 先绘制单个的小圆(子View:GustureLockedView)
- 1.我们需要确定解锁园的构造:—个外圆、实心的内圆、三角形
- 2.确定手势按圆的几种状态(无触摸、按下、抬起),根据不同的状态设置不同的颜色值(4种Color)
- 3.计算圆的大小,以及各个尺寸大小,然后开始绘制
- 4.确定三角形线条path,画封闭的三角形(设置只有当手指抬起的时设置旋转的角度候再显示出来)
二.绘制整个的布局,把每个子View摆放上去(整个大的:GuestureLockedViewGroup)
- 1.获取自定义属性(主要是获取自己设置的颜色和每行子View的个数)
- 2.实例化画笔和线(设置画笔的链接时,显示的状态)
- 3.重写onMeasure方法,计算每个子View的大小与摆放位置(给每个子View设置一个id,然后根据id设置前后左右,或者是margin值)
- 4.重写onThouch 方法根据不同的手势去改变子View的颜色
- 5.自定义回调接口,方法密码匹配情况 与 错误的次数 以及选择的子View的id
实战步骤:
- 先放上变量,方便大家阅读
private String TAG = "GustureLockedView"; //没有触摸时,外圆颜色 private int noFigerOutColor = 0xFFE0DBDB; //没有触摸时,内圆颜色 private int noFigerInerColor = 0xFF939090; //手指在上面的时候,圆的颜色 private int onFigerColor = 0xFF378FC9; //手机抬起时,被触摸的圆的颜色 private int upFigerColor = 0xFFFF0000; //初始化手指状态 private Mode status = Mode.STATUS_NO_FIGER; //外圆的宽度 private int outRoundWeith; //内圆高度 private int outRoundHight; //外圆半径 private int mRound; //三角型的高的基数 private float heigthRate = 0.333f; //内圆半径的基数 private float mRoundRate = 0.3f; //画笔的宽度 private int mStoreWidth = 2; //画笔 Paint mPaint; //划线 Path mPath; //旋转的度数 private int mProcess = 0; /** *记录手指触摸状态 */ enum Mode{ STATUS_NO_FIGER,STATUS_FIGER_ON,STATUS_UP_FIGER }
- 首先设置子View的在不同状态下的颜色值(最初只用到两种)
public GustureLockedView(Context context,int noFigerOutColor,int noFigerInerColor, int onFigerColor,int upFigerColor) { super(context); //初始化颜色 this.noFigerOutColor = noFigerOutColor; this.noFigerInerColor = noFigerInerColor; this.onFigerColor = onFigerColor; this.upFigerColor = upFigerColor; //初始化画笔 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPath = new Path(); }
- 颜色值都有了,我们现在是不是要在onMeasure()方法里面做测量了,这面绘制的三角型的三点的坐标我也直接到这个方法做了
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); outRoundWeith = MeasureSpec.getSize(widthMeasureSpec); outRoundHight = MeasureSpec.getSize(heightMeasureSpec); outRoundWeith = outRoundWeith < outRoundHight ? outRoundWeith:outRoundHight; mRound = (outRoundWeith-mStoreWidth)/2; Log.i(TAG,"mRound::"+mRound); mPath.moveTo(outRoundWeith/2,mStoreWidth+2); //左边的点 mPath.lineTo(outRoundWeith/2mRound*heigthRate,mRound*heigthRate+mStoreWidth+2); //右边的点 mPath.lineTo(outRoundWeith/2+mRound*heigthRate,mRound*heigthRate+mStoreWidth+2); //封闭然后组成一个三角型 mPath.close(); //当绘制重叠时,能融洽的结合(主要指颜色,哈)看不懂自己百度 mPath.setFillType(Path.FillType.WINDING); }
- 三角型都有了,那我们差什么???对,没错,圆。。现在根据不同的状态绘制圆的颜色咯
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (status){ //当没有手指触摸的时候 case STATUS_NO_FIGER: //外圆 mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(noFigerOutColor); canvas.drawCircle(outRoundWeith/2,outRoundWeith/2,mRound,mPaint); //内圆 mPaint.setColor(noFigerInerColor); canvas.drawCircle(outRoundWeith / 2, outRoundWeith / 2, mRound * mRoundRate, mPaint); Log.i(TAG, "画圆了:::"+noFigerOutColor+outRoundWeith); break; //当手指在上面的时候 case STATUS_FIGER_ON: //外圆 mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mStoreWidth); mPaint.setColor(onFigerColor); canvas.drawCircle(outRoundWeith/2,outRoundWeith/2,mRound,mPaint); //内圆 mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(outRoundWeith/2,outRoundWeith/2,mRound*mRoundRate,mPaint); break;//当手指离开界面,抬起的时候 case STATUS_UP_FIGER: //外圆 mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mStoreWidth); mPaint.setColor(upFigerColor); canvas.drawCircle(outRoundWeith/2,outRoundWeith/2,mRound,mPaint); //内圆 mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(outRoundWeith/2,outRoundWeith/2,mRound*mRoundRate,mPaint); drawArrow(canvas); break; } }
- 上面的onMeasure() onDraw()都是系统自己调用的,如果我要改变自己的状态,如果只有系统的方法,是不是远远不够呢??是不是应该有自己的方法呢???下面我另外几个方面写出来,会在GuestureLockedViewGroup会调用他
/** * 在 GuestureLockedViewGroup 中调用 * 画三角型图标 */ public void drawArrow(Canvas canvas){ if (mProcess!=-1){ canvas.save(); //当手指抬起时设置旋转的三角型度数 canvas.rotate(mProcess,outRoundWeith/2,outRoundWeith/2); //前面指设置了三角型坐标,现在就要绘制了 canvas.drawPath(mPath,mPaint); canvas.restore(); } } /** * 为了设置三角型旋转的度数 */ public void setPrecess(int process){ this.mProcess= process; } /** *设置手指触摸的状态 * @param mode */ public void setMode(Mode mode){ this.status = mode; invalidate(); }
*到这里我们算完成了皮毛,因为解锁页面很多按钮,我们才完成1个,算算,如果你设置每行3个,那个是1/9,如果每行4个呢,那就1/16。。直接我把上面的做好的view 重复的放到页面上去,记住我说是重复:
* 先看注释,方便阅读
private String TAG = "GuestureLockedViewGroup"; //没有触摸时,外圆颜色 private int noFigerOutColor = 0xFFE0DBDB; //没有触摸时,内圆颜色 private int noFigerInerColor = 0xFF939090; //手指在上面的时候,圆的颜色 private int onFigerColor = 0xFF378FC9; //手机抬起时,被触摸的圆的颜色 private int upFigerColor = 0xFFFF0000; //每行子view的数量 private int mCount; //允许尝试的次数 private int mTryCount; //画笔 和 线条 private Paint mPaint; private Path mPath; //每个子view 的宽度及间距 private int mGestureLockViewWidth; private int mMarginBetweenLockView; //用来储存选中的id private List<Integer> mChoose = new ArrayList<Integer>(); //保存所有的GestureLockView private GustureLockedView[] mGestureLockViews; //选定的子view中心的坐标 private int lastViewCentX ; private int lastViewCentY ; //终点坐标 Point mPoint = new Point(); //设置密码 private int[] passWord; //是否可以解锁 private boolean isLoack = false; //监听的接口 CheckPassWordStatusListener listener;
- 获取自己定义的属性 和 设置 画笔;
public GuestureLockedViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.GuestureLockedViewGroup); noFigerOutColor = ta.getColor(R.styleable.GuestureLockedViewGroup_no_finger_color_out, 0xFFE0DBDB); noFigerInerColor = ta.getColor(R.styleable.GuestureLockedViewGroup_no_finger_color_inner, 0xFF939090); onFigerColor = ta.getColor(R.styleable.GuestureLockedViewGroup_on_finger_color, 0xFF378FC9); upFigerColor = ta.getColor(R.styleable.GuestureLockedViewGroup_up_finger_color, 0xFFFF0000); mTryCount = ta.getInt(R.styleable.GuestureLockedViewGroup_m_try_time, 3); mCount = ta.getInt(R.styleable.GuestureLockedViewGroup_mcount, 4); ta.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(); }
- 通过测量摆放每个子View的位置
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取控件的宽度 int weigth = MeasureSpec.getSize(widthMeasureSpec); int higth = MeasureSpec.getSize(heightMeasureSpec); weigth = weigth < higth ? weigth : higth; //计算每个控件的大小 // 计算每个GestureLockView的宽度 mGestureLockViewWidth = (int) (4 * weigth * 1.0f / (5 * mCount + 1)); //计算每个GestureLockView的间距 mMarginBetweenLockView = (int) (mGestureLockViewWidth * 0.25); // 设置画笔的宽度为GestureLockView的内圆直径稍微小点(不喜欢的话,随便设) mPaint.setStrokeWidth(mGestureLockViewWidth * 0.29f); //设置子view的个数,以及摆放的位置 if (mGestureLockViews==null){ mGestureLockViews = new GustureLockedView[mCount*mCount]; //设置大小 for (int i=0; i<mGestureLockViews.length; i++){ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mGestureLockViewWidth,mGestureLockViewWidth); //初始化id mGestureLockViews[i] = new GustureLockedView(getContext(),noFigerOutColor,noFigerInerColor ,onFigerColor,upFigerColor); //设置id mGestureLockViews[i].setId(i + 1); //不是第一列的数,都在其的右边 if (i%mCount!=0){ params.addRule(RelativeLayout.RIGHT_OF, mGestureLockViews[i - 1].getId()); } //不是第一行都在指定Id的下边 if(i>=mCount){ params.addRule(RelativeLayout.BELOW,mGestureLockViews[i-mCount].getId()); } int leftMargin=0; int topMargin=0; int rightMargin = mGestureLockViewWidth/4; int belowMargin = mGestureLockViewWidth/4; //第一行 if (i<mCount){ topMargin = mGestureLockViewWidth/4; } //第一列 if(i%mCount==0){ leftMargin = mGestureLockViewWidth/4; } params.setMargins(leftMargin,topMargin,rightMargin,belowMargin); mGestureLockViews[i].setMode(GustureLockedView.Mode.STATUS_NO_FIGER); addView(mGestureLockViews[i], params); } } }
- 然后我们根据手势改变状态咯,获取手指的触摸点,看是落到那个子View上面
@Override public boolean onTouchEvent(MotionEvent event) { if(!isLoack){ int lastX = (int) event.getX(); int lastY = (int) event.getY(); Log.i(TAG,"lastX::"+lastX+".........."+"lastY:::"+lastY); switch (event.getAction()){ case MotionEvent.ACTION_MOVE: mPaint.setColor(onFigerColor); mPaint.setAlpha(50); GustureLockedView view = checkRange(lastX,lastY); Log.i(TAG,"view为Null::"+view); if (view!=null){ int childId = view.getId(); if (!mChoose.contains(childId)){ mChoose.add(childId); view.setMode(GustureLockedView.Mode.STATUS_FIGER_ON); lastViewCentX = (view.getLeft()+view.getRight())/2; lastViewCentY = (view.getTop()+view.getBottom())/2; //如果size为1,开始的子view if (mChoose.size()==1){ mPath.moveTo(lastViewCentX,lastViewCentY); }else{ mPath.lineTo(lastViewCentX,lastViewCentY); }// if (mChoose.size() == 1)// 当前添加为第一个// {// mPath.moveTo(mLastPathX, mLastPathY);// } else// // 非第一个,将两者使用线连上// {// mPath.lineTo(mLastPathX, mLastPathY);// } } } mPoint.x = lastX; mPoint.y = lastY; break; case MotionEvent.ACTION_UP: mPoint.x = lastViewCentX; mPoint.y = lastViewCentY; mPaint.setColor(upFigerColor); mPaint.setAlpha(50); for (int i=0; i<mChoose.size();i++){ mGestureLockViews[mChoose.get(i)-1].setMode(GustureLockedView.Mode.STATUS_UP_FIGER); } for (int i=0; i<mChoose.size()-1; i++){ //设置被选中的View的状态 GustureLockedView startView = (GustureLockedView) findViewById(mChoose.get(i)); GustureLockedView endView = (GustureLockedView) findViewById(mChoose.get(i+1)); int tanX = endView.getLeft() - startView.getLeft(); int tanY = endView.getTop() - startView.getTop(); // 计算角度 int angle = (int) Math.toDegrees(Math.atan2(tanY, tanX)) + 90; startView.setPrecess(angle); } boolean isRight = contrastsPassVoid(); if (listener!=null&&mChoose.size()>0){ listener.checkPassWordIsRight(isRight); } break; } invalidate(); } return true; } private boolean contrastsPassVoid(){ if (passWord.length!=mChoose.size()) return false; for (int i=0; i<passWord.length; i++){ if (passWord[i]!=mChoose.get(i)){ return false; } } return true; } /** * 检测范围 * @param x * @param y */ public GustureLockedView checkRange(int x, int y){ for (GustureLockedView gustureLockedView: mGestureLockViews){ int mRang = (int) (mGestureLockViewWidth*0.15); if (gustureLockedView.getLeft()+mRang<x&&gustureLockedView.getRight()-mRang>x&& gustureLockedView.getTop()+mRang<y&&gustureLockedView.getBottom()-mRang>y){ return gustureLockedView; } } return null; }
- 上面的主要的方法,我都列举出来了,下面写几个一看就懂的方法
/** * 一切初始化,在activity中调用。 */ public void resetView(){ mPath.reset(); mChoose.clear(); for (GustureLockedView gustureLockedView : mGestureLockViews){ gustureLockedView.setPrecess(-1); gustureLockedView.setMode(GustureLockedView.Mode.STATUS_NO_FIGER); } } /** * 设置密码 * @param passWord */ public void setPassWord(int[] passWord){ this.passWord = passWord; } public interface CheckPassWordStatusListener{ void checkPassWordIsRight(boolean isRight); } /** * 设置回调的方法 * @param checkPassWordStatusListener */ public void setCheckPassWordStatusListener(CheckPassWordStatusListener checkPassWordStatusListener){ this.listener = checkPassWordStatusListener; } /** * 如果超过设置的次数就设置ture,用户就不能再滑动了 * @param isLocked */ public void setIsLocked(boolean isLocked){ this.isLoack = isLocked; }
- 还没有看懂童鞋,在给你们看看activity中处理的逻辑了
public class MainActivity extends Activity { private SharedPreferences sp ; private SharedPreferences.Editor editor; private GuestureLockedViewGroup gvLockedView; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sp = getSharedPreferences("mTryCount", MODE_PRIVATE); editor = sp.edit(); //默认最多四次 editor.putInt("allCount", 5); editor.commit(); gvLockedView = (GuestureLockedViewGroup) findViewById(R.id.gv_locked_view); gvLockedView.setPassWord(new int[]{1,2,3,4,5,6}); gvLockedView.setCheckPassWordStatusListener(new GuestureLockedViewGroup.CheckPassWordStatusListener() { @Override public void checkPassWordIsRight(boolean isRight) { if (isRight){ Toast.makeText(MainActivity.this,"觉哥,你既然一次性解锁成功,你应该去盗银行!!",Toast.LENGTH_SHORT).show(); editor.putInt("allCount",5); editor.commit(); }else{ int count = sp.getInt("allCount",0); if (count==1){ //不能再解锁了 gvLockedView.setIsLocked(true); } count = --count; editor.putInt("allCount",count); editor.commit(); Toast.makeText(MainActivity.this,"你以为你可以像觉哥一样牛B轰轰的,还有"+count+"机会哦",Toast.LENGTH_SHORT).show(); } mHandler.postDelayed(new Runnable() { @Override public void run() { //按密码完成,恢复最初的状态。 gvLockedView.resetView(); gvLockedView.postInvalidate(); } },1000); } }); } public Handler mHandler = new Handler();}
- 另外附上 布局和自定义属性
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xj="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <aguche.gestureunlocked.com.view.GuestureLockedViewGroup android:id="@+id/gv_locked_view" android:layout_width="match_parent" android:layout_height="match_parent" xj:m_try_time="3" xj:mcount="3" xj:no_finger_color_inner="#FF939090" xj:no_finger_color_out="#FFE0DBDB" xj:on_finger_color="#FF378FC9" xj:up_finger_color="#FFFF0000" /></RelativeLayout>
- 今天就到这里了,觉老师得下班吃饭去了,别忘了点个赞。给我顶顶!!!!!!!!
1 0
- android自定义手势解锁
- 自定义手势解锁
- android自定义手势解锁View
- 自定义九宫格手势解锁
- Android 自定义手势解锁控件
- 安卓手势解锁-手势锁-自定义手势解锁-手势锁源码
- 手势解锁
- 手势解锁
- 手势解锁
- 手势解锁
- 手势解锁
- 自定义View----Android九宫格手势密码解锁
- 九点(九宫格)式手势解锁自定义view
- android 自定义view实现九宫格手势解锁
- 九点(九宫格)式手势解锁自定义view
- 自定义View----Android九宫格手势密码解锁
- 自定义view之九宫格手势解锁空间
- 指纹解锁和手势解锁
- linux的基本命令
- 关于c++ 学习
- 经典论文及其代码收集
- 从零开始学习java(1)
- inf & nan
- 自定义手势解锁
- 【宠物远程监控DIY】openwrt+rt5350+mjpg摄像头
- git、github、gitlab之间的关系
- 嵌入式必学知识
- css-position,float,display的关系和优先级
- Linux 安装 Oracle 11g——准备工作
- Python 基础入门
- L2-012. 关于堆的判断
- 修改DEDECMS织梦标题字数限制的方法