天天看点

Android自定义锁屏的实现

最近研究了一下锁屏,要实现锁屏大体的实现思路是自定义一个后台service监听手机屏幕开关事件,监听到屏幕关闭的时候替换系统锁屏并调用自定义的锁屏界面,自定义锁屏界面需要屏蔽返回键和home键,以及屏蔽状态栏下拉。

Android自定义锁屏的实现
Android自定义锁屏的实现

        系统屏幕开关的时候会发出相应的广播,我们可以接收对应的广播来调用自己的锁屏界面。屏幕开关的时候会发出以下两个广播事件。

android.intent.action.SCREEN_ON

android.intent.action.SCREEN_OFF

替换系统锁屏要调用KeyguardManager类相应方法去解除屏幕锁定,这里需要在配置文件中添加

<uses-permissionandroid:name="android.permission.DISABLE_KEYGUARD" />权限配置

屏蔽系统锁屏代码

mKeyguardManager= (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);

mKeyguardLock= mKeyguardManager.newKeyguardLock("");

mKeyguardLock.disableKeyguard();

以下是自定义service代码,service中动态注册了监听屏幕开关事件

[java]  view plain copy

  1. public class LockService extends Service {  
  2.     private String TAG = "LockService";  
  3.     private Intent zdLockIntent = null;  
  4.     @Override  
  5.     public IBinder onBind(Intent arg0) {  
  6.         return null;  
  7.     }  
  8.     public void onCreate() {  
  9.         super.onCreate();  
  10.         zdLockIntent = new Intent(LockService.this, ShowLockActivity.class);  
  11.         zdLockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  12.         IntentFilter mScreenOnFilter = new IntentFilter("android.intent.action.SCREEN_ON");  
  13.         LockService.this.registerReceiver(mScreenOnReceiver, mScreenOnFilter);  
  14.         IntentFilter mScreenOffFilter = new IntentFilter("android.intent.action.SCREEN_OFF");  
  15.         LockService.this.registerReceiver(mScreenOffReceiver, mScreenOffFilter);  
  16.     }  
  17.     public void onDestroy() {  
  18.         Log.i(TAG, "----------------- onDestroy------");  
  19.         super.onDestroy();  
  20.         this.unregisterReceiver(mScreenOnReceiver);  
  21.         this.unregisterReceiver(mScreenOffReceiver);  
  22.         // 在此重新启动,使服务常驻内存  
  23.         startService(new Intent(this, LockService.class));  
  24.     }  
  25.     private KeyguardManager mKeyguardManager = null;  
  26.     private KeyguardManager.KeyguardLock mKeyguardLock = null;  
  27.     // 屏幕变亮的广播,我们要隐藏默认的锁屏界面  
  28.     private BroadcastReceiver mScreenOnReceiver = new BroadcastReceiver() {  
  29.         @Override  
  30.         public void onReceive(Context context, Intent intent) {  
  31.             if (intent.getAction().equals("android.intent.action.SCREEN_ON")) {  
  32.                 Log.i(TAG, "----------------- android.intent.action.SCREEN_ON------");  
  33.             }  
  34.         }  
  35.     };  
  36.     // 屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定  
  37.     private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {  
  38.         @Override  
  39.         public void onReceive(Context context, Intent intent) {  
  40.             String action = intent.getAction();  
  41.             if (action.equals("android.intent.action.SCREEN_OFF") || action.equals("android.intent.action.SCREEN_ON")) {  
  42.                 mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);  
  43.                 mKeyguardLock = mKeyguardManager.newKeyguardLock("");  
  44.                 mKeyguardLock.disableKeyguard();  
  45.                 startActivity(zdLockIntent);  
  46.             }  
  47.         }  
  48.     };  
  49. }  

LockLayer.java这个类用来屏蔽状态啦下拉和home键的

[java]  view plain copy

  1. public class LockLayer {  
  2.     private Activity mActivty;  
  3.     private WindowManager mWindowManager;  
  4.     private View mLockView;  
  5.     private LayoutParams mLockViewLayoutParams;  
  6.     private boolean isLocked;  
  7.     // 这个值具体用于实现全屏  
  8.     private final static int FLAG_APKTOOL_VALUE = 1280;  
  9.     public LockLayer(Activity act) {  
  10.         mActivty = act;  
  11.         init();  
  12.     }  
  13.     private void init() {  
  14.         isLocked = false;  
  15.         mWindowManager = mActivty.getWindowManager();  
  16.         mLockViewLayoutParams = new LayoutParams();  
  17.         mLockViewLayoutParams.width = LayoutParams.MATCH_PARENT;  
  18.         mLockViewLayoutParams.height = LayoutParams.MATCH_PARENT;  
  19.         // 这一行实现屏蔽Home  
  20.         mLockViewLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;  
  21.         mLockViewLayoutParams.flags = FLAG_APKTOOL_VALUE;  
  22.     }  
  23.     public synchronized void lock() {  
  24.         if (mLockView != null && !isLocked) {  
  25.             mWindowManager.addView(mLockView, mLockViewLayoutParams);  
  26.         }  
  27.         isLocked = true;  
  28.     }  
  29.     public synchronized void unlock() {  
  30.         if (mWindowManager != null && isLocked) {  
  31.             mWindowManager.removeView(mLockView);  
  32.         }  
  33.         isLocked = false;  
  34.     }  
  35.     public synchronized void setLockView(View v) {  
  36.         mLockView = v;  
  37.     }  
  38. }  

RoundSpinView.java 自定义锁屏界面

[java]  view plain copy

  1. public class RoundSpinView extends View {  
  2.     private String TAG = "RoundSpinView";  
  3.     private Activity act;  
  4.     private Context ctx;  
  5.     private Paint mPaint = new Paint();  
  6.     // 图片列表列表  
  7.     private BigStone[] mStones;  
  8.     // 中心点stone  
  9.     private BigStone centerStones;  
  10.     // 数目  
  11.     private static final int STONE_COUNT = 4;  
  12.     // 圆心坐标  
  13.     private int mPointX = 0, mPointY = 0;  
  14.     // 半径  
  15.     private int mRadius = 0;  
  16.     // 每两个点间隔的角度  
  17.     private int mDegreeDelta;  
  18.     //   
  19.     private Bitmap lockscre_pressed_bit ;  
  20.     private Bitmap lockscreen_normal_bit;  
  21.     private Bitmap select_bg_bit;  
  22.     private int[] normal_img = { R.drawable.ic_lockscreen_message_normal, R.drawable.ic_lockscreen_unlock_normal, R.drawable.ic_lockscreen_phone_normal, R.drawable.ic_tab_theme_normal };  
  23.     private int[] select_img = { R.drawable.ic_lockscreen_message_activated, R.drawable.ic_lockscreen_unlock_activated, R.drawable.ic_lockscreen_phone_activated, R.drawable.ic_tab_theme_selected };  
  24.     private Bitmap[] normal_img_bitmap = new Bitmap[STONE_COUNT];  
  25.     private Bitmap[] select_img_bitmap = new Bitmap[STONE_COUNT];  
  26.     public RoundSpinView(Context context,Activity act, int px, int py, int radius) {  
  27.         super(context);  
  28.         this.ctx = context;  
  29.         this.act = act;  
  30.         init(px, py, radius);  
  31.     }  
  32.     public RoundSpinView(Context context, AttributeSet attrs, int defStyle) {  
  33.         super(context, attrs, defStyle);  
  34.     }  
  35.     public RoundSpinView(Context context, AttributeSet attrs) {  
  36.         super(context, attrs);  
  37.     }  
  38.     public RoundSpinView(Context context) {  
  39.         super(context);  
  40.     }  
  41.     public void init(int px, int py, int radius) {  
  42.         mPaint.setColor(Color.WHITE);  
  43.         mPaint.setStrokeWidth(0);  
  44.         mPaint.setAntiAlias(true);  
  45.         mPaint.setStyle(Style.STROKE);  
  46.         lockscre_pressed_bit = BitmapFactory.decodeResource(getResources(), R.drawable.lockscre_pressed);  
  47.         lockscreen_normal_bit = BitmapFactory.decodeResource(getResources(), R.drawable.lockscreen_normal);  
  48.         select_bg_bit = BitmapFactory.decodeResource(getResources(), R.drawable.template_checkbox_normal);  
  49.         for (int index = 0; index < STONE_COUNT; index++) {  
  50.             normal_img_bitmap[index] = BitmapFactory.decodeResource(getResources(), normal_img[index]);  
  51.             select_img_bitmap[index] = BitmapFactory.decodeResource(getResources(), select_img[index]);  
  52.         }  
  53.         setBackgroundResource(R.drawable.bg1);  
  54.         mPointX = px / 2;  
  55.         mPointY = py / 3 * 2;  
  56.         mRadius = radius;  
  57.         setupStones();  
  58.         computeCoordinates();  
  59.     }  
  60.     private void setupStones() {  
  61.         mStones = new BigStone[STONE_COUNT];  
  62.         BigStone stone;  
  63.         int angle = 0;  
  64.         mDegreeDelta = 360 / STONE_COUNT;  
  65.         centerStones = new BigStone();  
  66.         centerStones.angle = angle;  
  67.         centerStones.x = mPointX;  
  68.         centerStones.y = mPointY;  
  69.         for (int index = 0; index < STONE_COUNT; index++) {  
  70.             stone = new BigStone();  
  71.             stone.angle = angle;  
  72.             angle += mDegreeDelta;  
  73.             mStones[index] = stone;  
  74.         }  
  75.     }  
  76.     private void computeCoordinates() {  
  77.         BigStone stone;  
  78.         for (int index = 0; index < STONE_COUNT; index++) {  
  79.             stone = mStones[index];  
  80.             stone.x = mPointX + (float) ((mRadius + select_bg_bit.getWidth()/2) * Math.cos(stone.angle * Math.PI / 180));  
  81.             stone.y = mPointY + (float) ((mRadius + select_bg_bit.getHeight()/2) * Math.sin(stone.angle * Math.PI / 180));  
  82.             stone.bitmap = normal_img_bitmap[index];  
  83.             stone.angle = computeCurrentAngle(stone.x, stone.y);  
  84.         }  
  85.     }  
  86.     private int computeCurrentAngle(float x, float y) {  
  87.         float distance = (float) Math.sqrt(((x - mPointX) * (x - mPointX) + (y - mPointY) * (y - mPointY)));  
  88.         int degree = (int) (Math.acos((x - mPointX) / distance) * 180 / Math.PI);  
  89.         if (y < mPointY) {  
  90.             degree = -degree;  
  91.         }  
  92.         return degree;  
  93.     }  
  94.     private boolean isPressLock = false;// 标记是否按住中心锁图片  
  95.     @Override  
  96.     public boolean dispatchTouchEvent(MotionEvent event) {  
  97.         float x, y;  
  98.         int action = event.getAction();  
  99.         switch (action) {  
  100.         case MotionEvent.ACTION_DOWN:  
  101.             x = event.getX();  
  102.             y = event.getY();  
  103.             isPressLock = isPressLockPic(x, y);  
  104.             setIsVisible(isPressLock);  
  105.             invalidate();  
  106.             break;  
  107.         case MotionEvent.ACTION_MOVE:  
  108.             x = event.getX();  
  109.             y = event.getY();  
  110.             // 算出当前坐标和圆心的距离  
  111.             centerStones.angle = computeCurrentAngle(x, y);  
  112.             if (isPressLock) {  
  113.                 centerStones.bitmap = lockscre_pressed_bit;  
  114.                 computeCoordinates();  
  115.                 if (getDistance(x, y) <= mRadius) {  
  116.                     centerStones.x = x;  
  117.                     centerStones.y = y;  
  118.                 } else {// 大于直径时根据角度算出坐标  
  119.                     centerStones.x = mPointX + (float) ((mRadius) * Math.cos(centerStones.angle * Math.PI / 180));  
  120.                     centerStones.y = mPointY + (float) ((mRadius) * Math.sin(centerStones.angle * Math.PI / 180));  
  121.                     if (centerStones.angle <= (mStones[0].angle + 15) && centerStones.angle >= (mStones[0].angle - 15)) {  
  122.                         mStones[0].bitmap = select_img_bitmap[0];  
  123.                         centerStones.bitmap = select_bg_bit;  
  124.                         centerStones.x = mStones[0].x;  
  125.                         centerStones.y = mStones[0].y;  
  126.                     }  
  127.                     if (centerStones.angle <= (mStones[1].angle + 15) && centerStones.angle >= (mStones[1].angle - 15)) {  
  128.                         mStones[1].bitmap = select_img_bitmap[1];  
  129.                         centerStones.bitmap = select_bg_bit;  
  130.                         centerStones.x = mStones[1].x;  
  131.                         centerStones.y = mStones[1].y;  
  132.                     }  
  133.                     if (centerStones.angle <= (mStones[2].angle + 15) && centerStones.angle >= (mStones[2].angle - 15)) {  
  134.                         mStones[2].bitmap = select_img_bitmap[2];  
  135.                         centerStones.bitmap = select_bg_bit;  
  136.                         centerStones.x = mStones[2].x;  
  137.                         centerStones.y = mStones[2].y;  
  138.                     }  
  139.                     if (centerStones.angle <= (mStones[3].angle + 15) && centerStones.angle >= (mStones[3].angle - 15)) {  
  140.                         mStones[3].bitmap = select_img_bitmap[3];  
  141.                         centerStones.bitmap = select_bg_bit;  
  142.                         centerStones.x = mStones[3].x;  
  143.                         centerStones.y = mStones[3].y;  
  144.                     }  
  145.                 }  
  146.                 invalidate();  
  147.             }  
  148.             break;  
  149.         case MotionEvent.ACTION_UP:  
  150.             //处理Action_Up事件:  判断是否解锁成功,成功则结束我们的Activity ;否则 ,缓慢回退该图片。  
  151.             handleActionUpEvent(event);  
  152.             break;  
  153.         }  
  154.         return true;  
  155.     }  
  156.     private void handleActionUpEvent(MotionEvent event){  
  157.         boolean islocksuc = false;// 是否解锁成功  
  158.         float x = event.getX();  
  159.         float y = event.getY();  
  160.         centerStones.angle = computeCurrentAngle(x, y);  
  161.         if (getDistance(x, y) >= mRadius) {  
  162.             if (centerStones.angle <= (mStones[0].angle + 15) && centerStones.angle >= (mStones[0].angle - 15) && mStones[0].isVisible) {  
  163.                 islocksuc = true;  
  164.                 Log.i(TAG,"解锁-短信 跳转到短信界面");  
  165.                 act.finish();  
  166.             }  
  167.             if (centerStones.angle <= (mStones[1].angle + 15) && centerStones.angle >= (mStones[1].angle - 15) && mStones[1].isVisible) {  
  168.                 islocksuc = true;  
  169.                 Log.i(TAG,"解锁-解锁");  
  170.                 act.finish();  
  171.             }  
  172.             if (centerStones.angle <= (mStones[2].angle + 15) && centerStones.angle >= (mStones[2].angle - 15) && mStones[2].isVisible) {  
  173.                 islocksuc = true;  
  174.                 Log.i(TAG,"解锁-电话 跳转到电话界面");  
  175.                 act.finish();  
  176.             }  
  177.             if (centerStones.angle <= (mStones[3].angle + 15) && centerStones.angle >= (mStones[3].angle - 15) && mStones[3].isVisible) {  
  178.                 islocksuc = true;  
  179.                 Log.i(TAG,"解锁-相机 跳转到相机界面");  
  180.                 act.finish();  
  181.             }  
  182.         }   
  183.         if(!islocksuc) { // 未解锁成功  
  184.             backToCenter();  
  185.         }  
  186.     }  
  187.     //回退动画时间间隔值   
  188.     private static int BACK_DURATION = 20 ;   // 20ms  
  189.     //水平方向前进速率  
  190.     private static float VE_HORIZONTAL = 0.8f ;  //0.1dip/ms  
  191.     private Handler mHandler =new Handler ();  
  192.     private void backToCenter() {  
  193.         mHandler.postDelayed(BackDragImgTask, BACK_DURATION);  
  194.     }  
  195.     //通过延时控制当前绘制bitmap的位置坐标  
  196.     private Runnable BackDragImgTask = new Runnable(){  
  197.         public void run(){  
  198.             //一下次Bitmap应该到达的坐标值  
  199.             if(centerStones.x>=mPointX){  
  200.                 centerStones.x = centerStones.x - BACK_DURATION * VE_HORIZONTAL;  
  201.                 if(centerStones.x<mPointX){  
  202.                     centerStones.x = mPointX;  
  203.                 }  
  204.             } else {  
  205.                 centerStones.x = centerStones.x + BACK_DURATION * VE_HORIZONTAL;  
  206.                 if(centerStones.x>mPointX){  
  207.                     centerStones.x = mPointX;  
  208.                 }  
  209.             }   
  210.             centerStones.y = mPointY + (float) ((centerStones.x-mPointX) * Math.tan(centerStones.angle * Math.PI / 180));  
  211.             invalidate();//重绘         
  212.             boolean shouldEnd = getDistance(centerStones.x, centerStones.y) <= 8 ;             
  213.             if(!shouldEnd)  
  214.                 mHandler.postDelayed(BackDragImgTask, BACK_DURATION);  
  215.             else { //复原初始场景  
  216.                 centerStones.x = mPointX;  
  217.                 centerStones.y = mPointY;  
  218.                 isPressLock = false;  
  219.                 setIsVisible(isPressLock);  
  220.                 invalidate();  
  221.             }                 
  222.         }  
  223.     };  
  224.     private float getDistance(float x, float y) {  
  225.         float distance = (float) Math.sqrt(((x - mPointX) * (x - mPointX) + (y - mPointY) * (y - mPointY)));  
  226.         return distance;  
  227.     }  
  228.     private boolean isPressLockPic(float x, float y) {  
  229.         float l = centerStones.x - centerStones.bitmap.getWidth() / 2;  
  230.         float r = centerStones.x + centerStones.bitmap.getWidth() / 2;  
  231.         float t = centerStones.y - centerStones.bitmap.getHeight() / 2;  
  232.         float b = centerStones.y + centerStones.bitmap.getHeight() / 2;  
  233.         if (x >= l && x <= r && y >= t && y <= b) {  
  234.             return true;  
  235.         }  
  236.         return false;  
  237.     }  
  238.     @Override  
  239.     public void onDraw(Canvas canvas) {  
  240.         if (isPressLock) {// 手指按下状态  
  241.             canvas.drawCircle(mPointX, mPointY, mRadius, mPaint);// 画圆  
  242.             drawInCenter(canvas, centerStones.bitmap, centerStones.x, centerStones.y);// 画中心锁图片  
  243.             for (int index = 0; index < STONE_COUNT; index++) {  
  244.                 if (!mStones[index].isVisible)  
  245.                     continue;  
  246.                 drawInCenter(canvas, mStones[index].bitmap, mStones[index].x, mStones[index].y);  
  247.             }  
  248.         } else {  
  249.             centerStones.bitmap = lockscreen_normal_bit;  
  250.             drawInCenter(canvas, centerStones.bitmap, centerStones.x, centerStones.y);// 画中心锁图片  
  251.         }  
  252.     }  
  253.     void drawInCenter(Canvas canvas, Bitmap bitmap, float left, float top) {  
  254.         canvas.drawBitmap(bitmap, left - bitmap.getWidth() / 2, top - bitmap.getHeight() / 2, null);  
  255.     }  
  256.     private void setIsVisible(boolean isVisible){  
  257.         for (int index = 0; index < STONE_COUNT; index++) {  
  258.             mStones[index].isVisible = isVisible;  
  259.         }  
  260.     }  
  261.     class BigStone {  
  262.         // 图片  
  263.         public Bitmap bitmap;  
  264.         // 角度  
  265.         public int angle;  
  266.         // x坐标  
  267.         public float x;  
  268.         // y坐标  
  269.         public float y;  
  270.         // 是否可见  
  271.         public boolean isVisible = false;  
  272.     }  
  273. }