android 滑动解锁实现
刚刚跟着传智播客的黎活明老师的视频学习了android,很想做一个自己的锁屏软件
网上翻看了一些资料,自己也琢磨了一些,把心得体会写下来也算是一个回顾
1.效果图
这是我机器上的效果图
2.源码和解释(我觉得还是贴一些源码好说明问题)
2.1 主显示的LockScreenActivity.java
1 package com.lihua.lockscreen; 2 3 import android.os.Bundle; 4 import android.os.Handler; 5 import android.os.Message; 6 import android.os.Vibrator; 7 import android.app.Activity; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.util.Log; 11 import android.view.KeyEvent; 12 import android.view.Window; 13 import android.view.WindowManager; 14 import android.widget.Toast; 15 16 public class LockScreenActivity extends Activity { 17 private final String TAG = "LockScreenActivity"; 18 private SliderRelativeLayout sliderRelativeLayout; 19 public static int MSG_LOCK_SUCESS = 1; 20 21 @Override 22 protected void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 25 requestWindowFeature(Window.FEATURE_NO_TITLE); //不要title 26 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 27 WindowManager.LayoutParams.FLAG_FULLSCREEN); //全屏显示 28 29 setContentView(R.layout.activity_lock_screen); 30 31 sliderRelativeLayout = (SliderRelativeLayout) findViewById(R.id.sliderLayout); 32 sliderRelativeLayout.setMainHandler(handler); 33 sliderRelativeLayout.getBackground().setAlpha(180); //设置背景的透明度 34 35 startService(new Intent(LockScreenActivity.this, LockScreenService.class)); //这里要显示的调用服务 36 } 37 38 private Handler handler = new Handler(){ 39 @Override 40 public void handleMessage(Message msg) { 41 if(MSG_LOCK_SUCESS == msg.what){ 42 //Toast.makeText(getApplicationContext(), R.string.lockSuccess, 1).show(); 43 virbate(); 44 finish(); 45 } 46 } 47 }; 48 49 /** 50 * 震动 51 */ 52 private void virbate(){ 53 Vibrator vibrator = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE); 54 vibrator.vibrate(200); 55 } 56 57 /** 58 * 屏蔽掉Home键 59 */ 60 @Override 61 public void onAttachedToWindow() { 62 this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 63 super.onAttachedToWindow(); 64 } 65 66 /** 67 * 屏蔽掉返回键 68 */ 69 @Override 70 public boolean onKeyDown(int keyCode, KeyEvent event) { 71 if(event.getKeyCode() == KeyEvent.KEYCODE_BACK){ 72 return true; 73 } else { 74 return super.onKeyDown(keyCode, event); 75 } 76 } 77 }
2.2 主布局文件activity_lock_screen.xml
1 <RelativeLayout xmlns:tools="http://schemas.android.com/tools" 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:background="@drawable/bg" 6 tools:context=".LockScreenActivity" > 7 <com.lihua.lockscreen.SliderRelativeLayout 8 android:id="@+id/sliderLayout" 9 android:layout_height="66dp" 10 android:layout_width="fill_parent" 11 android:layout_marginTop="400dp" 12 android:background="@drawable/lockbg"> 13 14 <RelativeLayout 15 android:id="@+id/relativeLayout1" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_alignParentLeft="true" 19 android:layout_centerHorizontal="true" 20 android:layout_centerVertical="true"> 21 <ImageView 22 android:id="@+id/leftRing" 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:src="@drawable/circlebg"/> 26 27 <TextView 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:layout_centerHorizontal="true" 31 android:layout_centerVertical="true" 32 android:text="@string/li" 33 android:textColor="#FF0000" /> 34 </RelativeLayout> 35 <RelativeLayout 36 android:layout_width="wrap_content" 37 android:layout_height="wrap_content" 38 android:layout_alignParentRight="true" 39 android:layout_centerHorizontal="true" 40 android:layout_centerVertical="true" > 41 <ImageView 42 android:id="@+id/rightRing" 43 android:layout_width="wrap_content" 44 android:layout_height="wrap_content" 45 android:src="@drawable/circlebg"/> 46 47 <TextView 48 android:layout_width="wrap_content" 49 android:layout_height="wrap_content" 50 android:layout_centerHorizontal="true" 51 android:layout_centerVertical="true" 52 android:text="@string/shuang" 53 android:textColor="#FF0000" /> 54 </RelativeLayout> 55 <RelativeLayout 56 android:layout_width="222dp" 57 android:layout_height="wrap_content" 58 android:layout_marginLeft="50dp" 59 > 60 61 <ImageView 62 android:id="@+id/loveView" 63 android:layout_width="wrap_content" 64 android:layout_height="wrap_content" 65 android:layout_alignParentLeft="true" 66 android:layout_alignParentTop="true" 67 android:src="@drawable/love" /> 68 69 </RelativeLayout> 70 71 72 73 </com.lihua.lockscreen.SliderRelativeLayout> 74 </RelativeLayout>
2.3 自定义布局类SliderRelativeLayout.java
package com.lihua.lockscreen; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import android.widget.RelativeLayout; public class SliderRelativeLayout extends RelativeLayout { private final static String TAG = "SliderRelativeLayout"; private Context context; private Bitmap dragBitmap = null; //拖拽图片 private int locationX = 0; //bitmap初始绘图位置,足够大,可以认为看不见 private ImageView heartView = null; //主要是获取相对布局的高度 private ImageView leftRingView = null; private ImageView rightRingView = null; private Handler handler = null; //信息传递 private static int BACK_DURATION = 10 ; // 回退动画时间间隔值 20ms private static float VE_HORIZONTAL = 0.9f ; // 水平方向前进速率 0.1dip/ms public SliderRelativeLayout(Context context) { super(context); SliderRelativeLayout.this.context = context; intiDragBitmap(); } public SliderRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs, 0); SliderRelativeLayout.this.context = context; intiDragBitmap(); } public SliderRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); SliderRelativeLayout.this.context = context; intiDragBitmap(); } /** * 得到拖拽图片 */ private void intiDragBitmap() { if(dragBitmap == null){ dragBitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.love); } } /** * 这个方法里可以得到一个其他资源 */ @Override protected void onFinishInflate() { super.onFinishInflate(); heartView = (ImageView) findViewById(R.id.loveView); leftRingView = (ImageView) findViewById(R.id.leftRing); rightRingView = (ImageView) findViewById(R.id.rightRing); } /** * 对拖拽图片不同的点击事件处理 */ @Override public boolean onTouchEvent(MotionEvent event) { int X = (int) event.getX(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: locationX = (int) event.getX(); Log.i(TAG, "是否点击到位=" + isActionDown(event)); return isActionDown(event);//判断是否点击了滑动区域 case MotionEvent.ACTION_MOVE: //保存x轴方向,绘制图画 locationX = X; invalidate(); //重新绘图 return true; case MotionEvent.ACTION_UP: //判断是否解锁成功 if(!isLocked()){ //没有解锁成功,动画应该回退 handleActionUpEvent(event); //动画回退 } return true; } return super.onTouchEvent(event); } /** * 回退动画 * @param event */ private void handleActionUpEvent(MotionEvent event) { int x = (int) event.getX(); int toLeft = leftRingView.getWidth(); locationX = x - toLeft; if(locationX >= 0){ handler.postDelayed(ImageBack, BACK_DURATION); //回退 } } /** * 未解锁时,图片回退 */ private Runnable ImageBack = new Runnable() { @Override public void run() { locationX = locationX - (int) (VE_HORIZONTAL*BACK_DURATION); if(locationX >= 0){ handler.postDelayed(ImageBack, BACK_DURATION); //回退 invalidate(); } } }; /** * 判断是否点击到了滑动区域 * @param event * @return */ private boolean isActionDown(MotionEvent event) { Rect rect = new Rect(); heartView.getHitRect(rect); boolean isIn = rect.contains((int)event.getX()-heartView.getWidth(), (int)event.getY()); if(isIn){ heartView.setVisibility(View.GONE); return true; } return false; } /** * 绘制拖动时的图片 */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); invalidateDragImg(canvas); } /** * 图片随手势移动 * @param canvas */ private void invalidateDragImg(Canvas canvas) { int drawX = locationX - dragBitmap.getWidth(); int drawY = heartView.getTop(); if(drawX < leftRingView.getWidth()){ //划到最左边 heartView.setVisibility(View.VISIBLE); return; } else { if(isLocked()){ //判断是否成功 return; } heartView.setVisibility(View.GONE); canvas.drawBitmap(dragBitmap, drawX < 0 ? 5 : drawX,drawY,null); } } /** * 判断是否解锁 */ private boolean isLocked(){ if(locationX > (getScreenWidth() - rightRingView.getWidth())){ handler.obtainMessage(LockScreenActivity.MSG_LOCK_SUCESS).sendToTarget(); return true; } return false; } /** * 获取屏幕宽度 * @return */ private int getScreenWidth(){ WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); int width = manager.getDefaultDisplay().getWidth(); return width; } /** * 与主activity通信 * @param handler */ public void setMainHandler(Handler handler){ this.handler = handler; } }
2.4 开机广播 LockScreenReceiver.java
1 package com.lihua.lockscreen; 2 3 import android.app.KeyguardManager; 4 import android.content.BroadcastReceiver; 5 import android.content.Context; 6 import android.content.Intent; 7 import android.util.Log; 8 9 /** 10 * 开机广播,启动activity 11 * @author lihua 12 * 13 */ 14 public class LockScreenReceiver extends BroadcastReceiver { 15 private final static String TAG = "LockScreenReceiver"; 16 private KeyguardManager keyguardManager = null; 17 private KeyguardManager.KeyguardLock keyguardLock = null; 18 19 @Override 20 public void onReceive(Context context, Intent intent) { 21 if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){ 22 Intent mIntent = new Intent(context, LockScreenActivity.class); 23 mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 24 25 keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); 26 keyguardLock = keyguardManager.newKeyguardLock(""); 27 keyguardLock.disableKeyguard(); 28 29 context.startActivity(mIntent); 30 } 31 } 32 33 }
2.5 监听服务 LockScreenService.java
1 package com.lihua.lockscreen; 2 3 import android.app.KeyguardManager; 4 import android.app.Service; 5 import android.content.BroadcastReceiver; 6 import android.content.Context; 7 import android.content.Intent; 8 import android.content.IntentFilter; 9 import android.os.IBinder; 10 import android.util.Log; 11 12 public class LockScreenService extends Service { 13 private final static String TAG = "LockScreenService"; 14 private Intent lockIntent; 15 private KeyguardManager keyguardManager = null; 16 private KeyguardManager.KeyguardLock keyguardLock = null; 17 18 @Override 19 public IBinder onBind(Intent arg0) { 20 return null; 21 } 22 23 @Override 24 public void onCreate() { 25 super.onCreate(); 26 27 lockIntent = new Intent(LockScreenService.this, LockScreenActivity.class); 28 lockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 29 30 //注册广播 31 IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 32 LockScreenService.this.registerReceiver(mScreenOffReceiver, mScreenOffFilter); 33 } 34 35 @Override 36 public void onDestroy() { 37 super.onDestroy(); 38 LockScreenService.this.unregisterReceiver(mScreenOffReceiver); 39 //重新启动activity 40 startService(new Intent(LockScreenService.this, LockScreenService.class)); 41 } 42 43 @Override 44 public int onStartCommand(Intent intent, int flags, int startId) { 45 return Service.START_STICKY; 46 } 47 48 /** 49 * 屏幕变亮的广播,这里要隐藏系统的锁屏界面 50 */ 51 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 52 @Override 53 public void onReceive(Context context, Intent intent) { 54 Log.i(TAG, intent.getAction()); 55 if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF) 56 || intent.getAction().equals(Intent.ACTION_SCREEN_ON)){ 57 58 keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); 59 keyguardLock = keyguardManager.newKeyguardLock(""); 60 keyguardLock.disableKeyguard(); //这里就是取消系统默认的锁屏 61 62 startActivity(lockIntent); //注意这里跳转的意图 63 } 64 } 65 }; 66 }
2.6 主配置文件AndroidManifest.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.lihua.lockscreen" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="16" /> 10 11 <application 12 android:allowBackup="true" 13 android:icon="@drawable/ic_launcher" 14 android:label="@string/app_name" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name="com.lihua.lockscreen.LockScreenActivity" 18 android:label="@string/app_name" 19 android:launchMode="singleTask"> 20 <intent-filter> 21 <action android:name="android.intent.action.MAIN" /> 22 23 <category android:name="android.intent.category.LAUNCHER" /> 24 </intent-filter> 25 </activity> 26 <service android:name=".LockScreenService"></service> 27 <receiver android:name=".LockScreenReceiver"> 28 <intent-filter> 29 <action android:name="android.intent.action.BOOT_COMPLETED"></action> 30 <category android:name="android.intent.category.HOME"/> 31 </intent-filter> 32 </receiver> 33 </application> 34 35 <uses-permission android:name="android.permission.DISABLE_KEYGUARD"></uses-permission> 36 <uses-permission android:name="android.permission.VIBRATE"></uses-permission> 37 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> 38 </manifest>
3. 锁屏思路
整个锁屏的思路是:
注册监听开机的广播
1 <receiver android:name=".LockScreenReceiver"> 2 <intent-filter> 3 <action android:name="android.intent.action.BOOT_COMPLETED"></action> 4 <category android:name="android.intent.category.HOME"/> 5 </intent-filter> 6 </receiver>
接收开机广播开启activity,activity里开启服务
<service android:name=".LockScreenService"></service>
服务里监听屏幕off和on的广播,然后关闭系统锁屏,开启自己的锁屏,最后的就是展示自己的锁屏界面
4.软件难点
4.1 怎么保证软件坚决不能被关闭(关闭了就是系统锁屏了),特别是内存管理软件(貌似网上的说法是通过各种广播把自己启动起来,比如监听电池电量变化广播等)
4.2 怎么有良好的锁屏效果,我的这个滑动解锁也是参考了别人的源码,针对不同的局部具体的方法会有所不同
4.3 滑动效果的实现
大概说一下思路,自定义一个布局类SliderRelativeLayout,处理不同的onTouchEvent方法
@Override public boolean onTouchEvent(MotionEvent event) { int X = (int) event.getX(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: locationX = (int) event.getX(); Log.i(TAG, "是否点击到位=" + isActionDown(event)); return isActionDown(event);//判断是否点击了滑动区域 case MotionEvent.ACTION_MOVE: //保存x轴方向,绘制图画 locationX = X; invalidate(); //重新绘图 return true; case MotionEvent.ACTION_UP: //判断是否解锁成功 if(!isLocked()){ //没有解锁成功,动画应该回退 handleActionUpEvent(event); //动画回退 } return true; } return super.onTouchEvent(event); }
三种状态:点击,滑动,离开点击
点击的时候判断是否是心形点击区域,滑动的时候通过Bitmap在canvas里重绘,判断是否到达右边可解锁区域,离开点击判断是否达到解锁区域,没有到达就动画回滚到原来的状态
大概就是这个流程了,具体的就要各位自己去理解了,通过不同的布局,效果和实现也不一样
5. 心得体会
六月份就要出去实习了,感觉有时间还是要多学习学习新的技术,业余时间把android学了一下,正好这学期也是有这个课程
很多人都说面向国内用户做android开发赚不了钱,原因是国人没有很好的付费习惯,但总的来说android还是个很好的东西,蛮有意思的
(其实这个程序就是我做给我妹子的,嘿嘿!)
6. 源代码
我也是看了别人的源码,所以也分享我的源码