凌宇的项目之旅-关于自定义锁屏

先上图片

自定义的锁屏界面

先说思路:首先我们要监听到系统的屏幕点亮和变暗事件这里我们可以用广播来实现,我们可以自己自定义一个服务来做,核心代码为`
注册一个屏幕亮起的广播

/* 注册广播 */
        IntentFilter mScreenOnFilter = new IntentFilter("android.intent.action.SCREEN_ON");
        ZdLockService.this.registerReceiver(mScreenOnReceiver, mScreenOnFilter);`

注册一个屏幕暗掉的广播

/* 注册广播 */
        IntentFilter mScreenOffFilter = new IntentFilter("android.intent.action.SCREEN_OFF");
        ZdLockService.this.registerReceiver(mScreenOffReceiver, mScreenOffFilter);

两个广播的具体代码如下

 private KeyguardManager mKeyguardManager = null;
    private KeyguardManager.KeyguardLock mKeyguardLock = null;
    // 屏幕变亮的广播,我们要隐藏默认的锁屏界面
    private BroadcastReceiver mScreenOnReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {

            Log.i(TAG, intent.getAction());
            Log.e("LHT", "屏幕已经变亮了");
            if (intent.getAction().equals("android.intent.action.SCREEN_ON")) {
                Log.i(TAG, "----------------- android.intent.action.SCREEN_ON------");
                // mKeyguardManager =
                // (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
                // mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");
                // mKeyguardLock.disableKeyguard();
            }
        }

    };

    // 屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定
    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            Log.i(TAG, intent.toString());
            Log.e("LHT", "屏幕已经变亮了2" + action);
            if (action.equals("android.intent.action.SCREEN_OFF") || action.equals("android.intent.action.SCREEN_ON")) {
                Log.e("LHT", "屏幕已经变亮了,开启我们自己的屏幕");
                mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
                mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");
                mKeyguardLock.disableKeyguard();
                startActivity(zdLockIntent);
            }
        }

    };

然而这代码有点问题,在geny模拟器上完全没问题,在原生手机上也是可以的,但是在某些手机系统上,比如我的mx5,会出现双锁屏!应该是在广播中处理得不好的原因!像某些手机系统,比如moto,除了会发出上述2个广播之外,还会发出另外的2个广播事件!这个问题容小弟好好去研究下,之后再贴代码进来。
为了降低服务的被杀死几率,我们可以把服务提升为前台服务,这样可以提升它的优先级:

 public int onStartCommand(Intent intent, int flags, int startId) {
        /**
         * StartCommond几个常量参数简介: 1、START_STICKY
         * 在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。
         * 不久后service就会再次尝试重新创建,因为保留在开始状态,在创建
         * service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。
         * 2、START_NOT_STICKY
         * 在运行onStartCommand后service进程被kill后,并且没有新的intent传递给它。Service将移出开始状态,
         * 并且直到新的明显的方法(startService)调用才重新创建。因为如果没有传递任何未决定的intent那么service是不会启动,
         * 也就是期间onstartCommand不会接收到任何null的intent。 3、START_REDELIVER_INTENT
         * 在运行onStartCommand后service进程被kill后,系统将会再次启动service,
         * 并传入最后一个intent给onstartCommand。直到调用stopSelf(int)才停止传递intent。
         * 如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。
         * 因此onstartCommand不会接收到任何null的intent。
         */

        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Notification.Builder builder = new Notification.Builder(getApplicationContext());
        PendingIntent contentIndent = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(contentIndent).setSmallIcon(R.mipmap.ic_launcher)//设置状态栏里面的图标(小图标)                     .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.i5))//下拉下拉列表里面的图标(大图标)        .setTicker("this is bitch!") //设置状态栏的显示的信息
                .setWhen(System.currentTimeMillis())//设置时间发生时间
                .setAutoCancel(true)//设置可以清除
                .setContentTitle(getString(R.string.app_name))//设置下拉列表里的标题
                .setContentText("提高服务优先级,防止被系统当成垃圾干掉");//设置上下文内容
        Notification notification = builder.getNotification();
        //加i是为了显示多条Notification
        notificationManager.notify(1, notification);
        return Service.START_STICKY;

    }

当是这样是不够的,该杀还是会被杀,所以我们可以重写ondestory()方法,做点恶心的操作:

 public void onDestroy() {
        super.onDestroy();
        Log.e("LHT", "你杀了我,不过你放心 我一定会回来的");
        ZdLockService.this.unregisterReceiver(mScreenOnReceiver);
        ZdLockService.this.unregisterReceiver(mScreenOffReceiver);
        // 在此重新启动
        if (MyApplication.getInstance().isReStartServer()) {
            startService(new Intent(ZdLockService.this, ZdLockService.class));
        }
    }

完整的服务代码如下:

package com.xxxx.xxxxxxxx;

import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;


public class ZdLockService extends Service {

    private static String TAG = "ZdLockService";
    private Intent zdLockIntent = null;

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    public void onCreate() {
        super.onCreate();
        Log.e("LHT", "你杀了我,不过没事的 我已经回来了");

        zdLockIntent = new Intent(ZdLockService.this, L_LockActivity.class);
        zdLockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        /* 注册广播 */
        IntentFilter mScreenOnFilter = new IntentFilter("android.intent.action.SCREEN_ON");
        ZdLockService.this.registerReceiver(mScreenOnReceiver, mScreenOnFilter);

        /* 注册广播 */
        IntentFilter mScreenOffFilter = new IntentFilter("android.intent.action.SCREEN_OFF");
        ZdLockService.this.registerReceiver(mScreenOffReceiver, mScreenOffFilter);
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        /**
         * StartCommond几个常量参数简介: 1、START_STICKY
         * 在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。
         * 不久后service就会再次尝试重新创建,因为保留在开始状态,在创建
         * service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。
         * 2、START_NOT_STICKY
         * 在运行onStartCommand后service进程被kill后,并且没有新的intent传递给它。Service将移出开始状态,
         * 并且直到新的明显的方法(startService)调用才重新创建。因为如果没有传递任何未决定的intent那么service是不会启动,
         * 也就是期间onstartCommand不会接收到任何null的intent。 3、START_REDELIVER_INTENT
         * 在运行onStartCommand后service进程被kill后,系统将会再次启动service,
         * 并传入最后一个intent给onstartCommand。直到调用stopSelf(int)才停止传递intent。
         * 如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。
         * 因此onstartCommand不会接收到任何null的intent。
         */

        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        Notification.Builder builder = new Notification.Builder(getApplicationContext());
        PendingIntent contentIndent = PendingIntent.getActivity(getApplicationContext(), 0,
                new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(contentIndent).setSmallIcon(R.mipmap.ic_launcher)//设置状态栏里面的图标(小图标)                     .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.i5))//下拉下拉列表里面的图标(大图标)        .setTicker("this is bitch!") //设置状态栏的显示的信息
                .setWhen(System.currentTimeMillis())//设置时间发生时间
                .setAutoCancel(true)//设置可以清除
                .setContentTitle(getString(R.string.app_name))//设置下拉列表里的标题
                .setContentText("提高服务优先级,防止被系统当成垃圾干掉");//设置上下文内容
        Notification notification = builder.getNotification();
        //加i是为了显示多条Notification
        notificationManager.notify(1, notification);
        return Service.START_STICKY;

    }

    public void onDestroy() {
        super.onDestroy();
        Log.e("LHT", "你杀了我,不过你放心 我一定会回来的");
        ZdLockService.this.unregisterReceiver(mScreenOnReceiver);
        ZdLockService.this.unregisterReceiver(mScreenOffReceiver);
        // 在此重新启动
        if (MyApplication.getInstance().isReStartServer()) {
            startService(new Intent(ZdLockService.this, ZdLockService.class));
        }
    }

    private KeyguardManager mKeyguardManager = null;
    private KeyguardManager.KeyguardLock mKeyguardLock = null;
    // 屏幕变亮的广播,我们要隐藏默认的锁屏界面
    private BroadcastReceiver mScreenOnReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {

            Log.i(TAG, intent.getAction());
            Log.e("LHT", "屏幕已经变亮了");
            if (intent.getAction().equals("android.intent.action.SCREEN_ON")) {
                Log.i(TAG, "----------------- android.intent.action.SCREEN_ON------");
                // mKeyguardManager =
                // (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
                // mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");
                // mKeyguardLock.disableKeyguard();
            }
        }

    };

    // 屏幕变暗/变亮的广播 , 我们要调用KeyguardManager类相应方法去解除屏幕锁定
    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            Log.i(TAG, intent.toString());
            Log.e("LHT", "屏幕已经变亮了2" + action);
            if (action.equals("android.intent.action.SCREEN_OFF") || action.equals("android.intent.action.SCREEN_ON")) {
                Log.e("LHT", "屏幕已经变亮了,开启我们自己的屏幕");
                mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
                mKeyguardLock = mKeyguardManager.newKeyguardLock("zdLock 1");
                mKeyguardLock.disableKeyguard();
                startActivity(zdLockIntent);
            }
        }

    };

}

有了服务是不够的,我们还需要自己做一个锁屏页面:
我直接贴代码吧:

package com.xxx.xxxxxxxxxy;


import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;



@SuppressLint("NewApi")
public class L_LockActivity extends Activity {

    private static String TAG = "LHT";

    // 自定义的滑动界面
    private SliderRelativeLayout sliderLayout = null;

    private ImageView ivArrowRight; // 右边的动画图片
    private ImageView ivArrowLeft; // 左边的动画图片
    private AnimationDrawable animArrowRightDrawable = null;
    private AnimationDrawable animationLeftDrawable = null;

    private Context mContext = null;

    public static int MSG_LOCK_UNLOCK = 1;

    public static int MSG_LOCK_TASK = 2;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = L_LockActivity.this;
        /*设置全屏,无标题*/
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.l_activity_lock);
        initViews();
        sliderLayout.setMainHandler(mHandler);
    }

    private void initViews() {
        sliderLayout = (SliderRelativeLayout) findViewById(R.id.slider_layout);
        //获得动画,并开始转动
        ivArrowRight = (ImageView) findViewById(R.id.lock_right_lock);
        ivArrowLeft = (ImageView) findViewById(R.id.lock_left_lock);
        animArrowRightDrawable = (AnimationDrawable) ivArrowRight.getBackground();
        animationLeftDrawable = (AnimationDrawable) ivArrowLeft.getBackground();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //设置动画
        mHandler.postDelayed(AnimationDrawableTask, 300);  //开始绘制动画
    }

    @Override
    protected void onPause() {
        super.onPause();
        animArrowRightDrawable.stop();
        animationLeftDrawable.stop();
    }

    protected void onDestory() {
        super.onDestroy();
    }

    //通过延时控制当前绘制bitmap的位置坐标
    private Runnable AnimationDrawableTask = new Runnable() {

        public void run() {
            animArrowRightDrawable.start();
            animationLeftDrawable.start();
            mHandler.postDelayed(AnimationDrawableTask, 300);
        }
    };

    private Handler mHandler = new Handler() {

        public void handleMessage(Message msg) {
            if (MSG_LOCK_UNLOCK == msg.what) {
                // TODO: 2016/5/19  右滑解锁的操作 
                finish(); // 锁屏成功时,结束我们的Activity界面
            }
            if (MSG_LOCK_TASK == msg.what) {
                // TODO: 2016/5/19  左滑任务列表的操作
                Toast.makeText(L_LockActivity.this, "任务列表,不过我没有,所以你还是右滑吧", Toast.LENGTH_SHORT).show();
            }
        }
    };

    //屏蔽掉Back键
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
            return true;
        else
            return super.onKeyDown(keyCode, event);

    }

}

这里是自定义的相对布局:



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.os.Message;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;




public class SliderRelativeLayout extends RelativeLayout {

    private static String TAG = "SliderRelativeLayout";

    private TextView tv_slider_icon = null; // 初始控件,用来判断是否为拖动?

    private Bitmap dragBitmap = null; //拖拽图片
    private Context mContext = null; // 初始化图片拖拽时的Bitmap对象


    private Handler mainHandler = null; //与主Activity通信的Handler对象

    public SliderRelativeLayout(Context context) {
        super(context);
        mContext = context;
        initDragBitmap();
    }

    public SliderRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
        mContext = context;
        initDragBitmap();
    }

    public SliderRelativeLayout(Context context, AttributeSet attrs,
                                int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        initDragBitmap();
    }

    // 初始化图片拖拽时的Bitmap对象
    private void initDragBitmap() {
        if (dragBitmap == null)
            dragBitmap = BitmapFactory.decodeResource(mContext.getResources(),
                    R.mipmap.l_lock_center);
    }

    @Override
    protected void onFinishInflate() {
        // TODO Auto-generated method stub
        super.onFinishInflate();
        // 该控件主要判断是否处于滑动点击区域。滑动时 处于INVISIBLE(不可见)状态,滑动时处于VISIBLE(可见)状态
        tv_slider_icon = (TextView) findViewById(R.id.slider_icon);
    }

    private int mLastMoveX = 100000;  //当前bitmap应该绘制的地方 , 初始值为足够大,可以认为看不见

    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        Log.i(TAG, "onTouchEvent" + " X is " + x + " Y is " + y);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastMoveX = (int) event.getX();
                //处理Action_Down事件:  判断是否点击了滑动区域
                return handleActionDownEvenet(event);
            case MotionEvent.ACTION_MOVE:
                mLastMoveX = x; //保存了X轴方向
                invalidate(); //重新绘制
                return true;
            case MotionEvent.ACTION_UP:
                //处理Action_Up事件:  判断是否解锁成功,成功则结束我们的Activity ;否则 ,缓慢回退该图片。
                handleActionUpEvent(event);
                return true;
        }
        return super.onTouchEvent(event);
    }

    // 绘制拖动时的图片
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //Log.(TAG, "onDraw ######" );
        // 图片更随手势移动
        invalidateDragImg(canvas);
    }

    // 图片更随手势移动
    private void invalidateDragImg(Canvas canvas) {
        //Log.e(TAG, "handleActionUpEvenet : invalidateDragImg" );
        //以合适的坐标值绘制该图片
        int drawXCor = mLastMoveX - dragBitmap.getWidth();
        int drawYCor = tv_slider_icon.getTop();
        Log.i(TAG, "invalidateDragImg" + " drawXCor " + drawXCor + " and drawYCor" + drawYCor);
        canvas.drawBitmap(dragBitmap, drawXCor < 0 ? 5 : drawXCor, drawYCor, null);
    }

    // 手势落下是,是否点中了图片,即是否需要开始移动
    private boolean handleActionDownEvenet(MotionEvent event) {
        Rect rect = new Rect();
        tv_slider_icon.getHitRect(rect);
        boolean isHit = rect.contains((int) event.getX(), (int) event.getY());

        if (isHit)  //开始拖拽 ,隐藏该图片
            tv_slider_icon.setVisibility(View.INVISIBLE);

        //Log.e(TAG, "handleActionDownEvenet : isHit" + isHit);

        return isHit;
    }

    //回退动画时间间隔值
    private static int BACK_DURATION = 20;   // 20ms
    //水平方向前进速率
    private static float VE_HORIZONTAL = 0.7f;  //0.1dip/ms

    //判断松开手指时,是否达到末尾即可以开锁了 , 是,则开锁,否则,通过一定的算法使其回退。
    private void handleActionUpEvent(MotionEvent event) {
        int x = (int) event.getX();
        Log.e(TAG, "handleActionUpEvent : x -->" + x + "   getRight() " + getRight());
        Log.e(TAG, "handleActionUpEvent : x -->" + x + "   getLeft() " + getLeft());
        //距离在15dip以内代表解锁成功。
        boolean isRightSucess = Math.abs(x - getRight()) <= 15;
        boolean isLeftSucess = Math.abs(x - getLeft()) <= 150;
        if (isRightSucess) {
            resetViewState();
            virbate(); //震动一下
            //结束我们的主Activity界面
            mainHandler.obtainMessage(L_LockActivity.MSG_LOCK_TASK).sendToTarget();
        } else {//没有成功解锁,以一定的算法使其回退
            //每隔20ms , 速率为0.6dip/ms ,  使当前的图片往后回退一段距离,直到到达最左端
            mLastMoveX = x;  //记录手势松开时,当前的坐标位置。
            int distance = x - tv_slider_icon.getRight();
            //只有移动了足够距离才回退
            Log.e(TAG, "handleActionUpEvent : mLastMoveX -->" + mLastMoveX + " distance -->" + distance);
            if (distance >= 0)
                mHandler.postDelayed(BackDragImgTask, BACK_DURATION);
            else {  //复原初始场景
                resetViewState();
            }
        }
        if (isLeftSucess) {
            resetViewState();
            virbate(); //震动一下
            //结束我们的主Activity界面
            mainHandler.obtainMessage(L_LockActivity.MSG_LOCK_UNLOCK).sendToTarget();
        } else {
            //每隔20ms , 速率为0.6dip/ms ,  使当前的图片往后回退一段距离,直到到达最左端
            mLastMoveX = x;  //记录手势松开时,当前的坐标位置。
            int distance = x - tv_slider_icon.getLeft();
            //只有移动了足够距离才回退
            Log.e(TAG, "handleActionUpEvent : mLastMoveX -->" + mLastMoveX + " distance -->" + distance);
            if (distance >= 0)
                mHandler.postDelayed(BackDragImgTask, BACK_DURATION);
            else {  //复原初始场景
                resetViewState();
            }
        }
    }

    //重置初始的状态,显示tv_slider_icon图像,使bitmap不可见
    private void resetViewState() {
        mLastMoveX = 100000;
        tv_slider_icon.setVisibility(View.VISIBLE);
        invalidate();        //重绘最后一次
    }

    //通过延时控制当前绘制bitmap的位置坐标
    private Runnable BackDragImgTask = new Runnable() {

        public void run() {
            //一下次Bitmap应该到达的坐标值
            mLastMoveX = mLastMoveX - (int) (BACK_DURATION * VE_HORIZONTAL);

            Log.e(TAG, "BackDragImgTask ############# mLastMoveX " + mLastMoveX);

            invalidate();//重绘
            //是否需要下一次动画 ? 到达了初始位置,不在需要绘制
            boolean shouldEnd = Math.abs(mLastMoveX - tv_slider_icon.getRight()) <= 8;
            if (!shouldEnd)
                mHandler.postDelayed(BackDragImgTask, BACK_DURATION);
            else { //复原初始场景
                resetViewState();
            }
        }
    };

    private Handler mHandler = new Handler() {

        public void handleMessage(Message msg) {

            Log.i(TAG, "handleMessage :  #### ");

        }
    };

    //震动一下下咯
    private void virbate() {
        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
        vibrator.vibrate(200);
    }

    public void setMainHandler(Handler handler) {
        mainHandler = handler;//activity所在的Handler对象
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值