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. 源代碼

這里下載

我也是看了別人的源碼,所以也分享我的源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM