剛剛跟着傳智播客的黎活明老師的視頻學習了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. 源代碼
我也是看了別人的源碼,所以也分享我的源碼