Android自定义锁屏实现----仿正点闹钟滑屏解锁

article/2025/7/2 8:24:02

 

                                                                                 本文原创,转载请注明出处:http://blog.csdn.net/qinjuning



        前几周看了下解锁的框架,基本上算是弄了个脸熟。看着别人花哨的解锁界面,心里也很痒痒的。于是,画了一天时间,

 捣鼓出了这个成果----仿正点闹钟解锁。基本功能实现了,但程序效率问题以及程序的几处Bug都没有完全解决,留待以后有机

 会弄吧。

 

            与正点闹钟对比如下:


                                                 

                                    (我们的解锁界面)                                                        (正点闹钟的解锁界面)


                           


   如何知晓正点闹钟的布局构成?

 

      毫无疑问,当我们想模仿任何一个优秀界面设计时,我们的第一想法肯定是能不能看到该App Apk包对应的资源文件(一般

 来说,编译成Apk时,src目录源文件是看不到的,res目录可以看到)。但是,为了安全性以及技术壁垒,公司都会进行加密处理。

 于是,我们想通过直接看layout布局文件泡汤了。当然,如果反编译看到那就无敌了。我不懂反编译,我就不多谈了。


      就我所知Apk下图片资源是不会加密的,于是我们可以查看这些图片以及文件名来猜测界面的可能组成情况,这对我们后面的分

析很有用处。


    真正我想说的:

      一直以来,作为程序员我们一直疏忽了一个利器:--- Hierarchy Viewer工具,可以直接观看布局文件的构成。对于该工具的

  具体用法请参考该文章:

                          Android 实用工具Hierarchy Viewer实战》


       PS : 如果你还羡慕其他人的布局 , 你就out了。  - - 


     我们针对正点闹钟的如下界面,利用工具查看,我们重点关注下图蓝色区域,如下:

 

                                                                

 

   上图中的蓝色区域利用Hierarchy Viewer工具得出如下的布局文件组成:


                                         

    

  

  怎么样,是不是很清楚了?大概知道怎么布局了吧。针对上面的布局,再逐一详解下,最后形成一个类似的layout布局文件。


                                        



     首先贴下这几张资源图片吧,大家心里也有个印象。


                                                                               

                                                           (这三张图片采用了黑色背景,便于观看)  

             

    其中最顶层RelativeLayout采用了一个背景图片,即圆弧形的图片。


    上图的编号解释如下 :

   编号 1 :TextView控件,显示一个图片资源,可能采用了android:background或者android:drawableLeft类似属性显示图片。

   编号 2 :TextView控件 ---- “向右滑动结束提醒”。so easy

   编号 3 :ImageView控件 --- 为了显示动画效果,采用了一个<animation-list>对应的动画文件,作为该控件的背景图片,

            从其图片资源文件看验证了这点,采用了三张不同效果的图片。 so easy

   编号 4 : ImageView控件,显示一张图片。 so easy

 

  呵呵,怎么样是不是看起来比较简单,该layout布局文件如下:

 

      说明: 代码块中 <SliderRelativeLayout>节点实则为<RelativeLayout>节点 , 大家请注意下。

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<SliderRelativeLayout android:id="@+id/slider_layout"
android:layout_width="fill_parent"
android:layout_height="63dip" 
android:layout_gravity="center"
android:layout_marginTop="200dip"
android:background="@drawable/step2_tip_2"
>
<!-- Lock OK icon  -->
<ImageView android:id="@+id/getup_finish_ico"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/slider_ico_alarm">
</ImageView>
<!-- Arrow Animation -->
<ImageView android:id="@+id/getup_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dip"
android:layout_toLeftOf="@id/getup_finish_ico"
android:layout_alignTop="@id/getup_finish_ico"
android:background="@anim/slider_tip_anim">
</ImageView>
<!-- hint_unlock -->
<TextView  android:layout_width="wrap_content"
android:layout_height="wrap_content" 
android:layout_toLeftOf="@id/getup_arrow"
android:layout_alignTop="@id/getup_finish_ico"
android:text="@string/hint_unlock">
</TextView>
<!-- slider img -->
<TextView  android:id="@+id/slider_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" 
android:layout_marginRight="5dip"
android:drawableTop="@drawable/getup_slider_ico_normal">
</TextView>
</SliderRelativeLayout>
</LinearLayout>


   棘手问题: 如何实现动态效果,即绘制一个更随手指移动的东东 ?

 

       先说下我的疑惑:如果参照正点闹钟显示的资源文件,即完全采用系统提供的控件,然后为这些控件提供触摸事件监听去滑动

 前面所说的编号为1的TextView控件----滑动可以调用scrollTo()或者scrollBy()方法,我试了几次但是都没有效果,原因是scrollTo()

 和scrollBy()方法并不偏移背景图片,仅仅偏移某个View的内容视图(onDraw()方法里绘制的),具体答案可以参考我的如下博客:


   《Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解》的draw()方法流程。

        

          不知道针对此,大家有没有好的意见。


  我的实现方式


       我们知道,自定义View/ViewGroup可以唯所欲为。因此,为了实现这种拖拽效果,实现了一个继承于RelativeLayout的

   自定义ViewGroup控件,并且重写了其中的onTouchEvent()方法去绘制一个跟随手指而动的Bitmap图片资源。当然,这只是

   第一步,还有很多细节需要处理,主要是一些坐标处理以及图片随着时间返回的问题,但这块内容大家看看代码注释加以理解

   吧,我是真的不好描述出来。见后文所贴代码。

 


 下面的知识主要是如何以一个APK的形式去实现解锁界面。


    知识点一 如何以APK形式去达到锁屏的目的以及 屏幕变暗以及屏幕点亮的广播。

                                   android.intent.action.SCREEN_ON  --- 屏幕变亮的广播

                                  android.intent.action.SCREEN_OFF ---- 屏幕点暗的广播

                 请参考该博客:http://www.2cto.com/kf/201111/109815.html

 

    知识点二Activity里如何屏蔽Home键和Back键?

                 请参考该博客:http://www.cnblogs.com/domybest/archive/2011/06/13/2080036.html


    知识点三KeyguardManager简介  

                  请参考该博客:http://hubingforever.blog.163.com/blog/static/171040579201191524550863/

                  

                  具体原因可以去看看我上篇博客<

Android框架浅析之锁屏(Keyguard)机制原理

>的最后面所述。


     这些知识,大家多多理解咯。

 

     关于禁止下拉状态栏问题,由于不能在源码下编译,因此无法达到禁止下拉状态栏。但我们的目的主要是学习正点闹钟的布局

 以及一种对它的一种可能的实现方式。这就够了。

 

   OK,下面贴出我的自定义RelativeLayout的代码。


    为了帮助大家更好的理解代码,最后说明图片是如何回退的?

      基本思路:我们根据当前Bitmap拖拽图片的所在位置,每隔几毫秒就让Bitmap拖拽图片回退一定距离,并且请求View会

  重新绘制,直到该Bitmap拖拽图片返回初始地方,即可显示一个回退动画效果了。代码中是利用Handler来控制的。

 

   自定义RelativeLayout代码:

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对象
private int mLastMoveX = 1000;  //当前bitmap应该绘制的地方 , 初始值为足够大,可以认为看不见	
public SliderRelativeLayout(Context context) {
super(context);
mContext = context;
initDragBitmap();
}
// 初始化图片拖拽时的Bitmap对象
private void initDragBitmap() {
if (dragBitmap == null)
dragBitmap = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.getup_slider_ico_pressed);
}
@Override
protected void onFinishInflate() {
// TODO Auto-generated method stub
super.onFinishInflate();
// 该控件主要判断是否处于滑动点击区域。滑动时 处于INVISIBLE状态(消失),正常时处于VISIBLE(可见)状态
tv_slider_icon = (TextView) findViewById(R.id.slider_icon);
}
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() );
//距离在15dip以内代表解锁成功。
boolean isSucess= Math.abs(x - getRight()) <= 15 ;
if(isSucess){
Toast.makeText(mContext, "解锁成功", 1000).show();
resetViewState();	
virbate(); //震动一下
//结束我们的主Activity界面
mainHandler.obtainMessage(MainActivity.MSG_LOCK_SUCESS).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();
}
}
}
//重置初始的状态,显示tv_slider_icon图像,使bitmap不可见
private void resetViewState(){
mLastMoveX = 1000 ;
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对象
}
}

 

      补充一下,我们的Activity采用了“singleTop”启动模式,防止启动多个Activity实例。


      继续分享示例DEMO源代码,希望能帮助你的学习。示例DEMO下载地址:


                       http://download.csdn.net/detail/qinjuning/4295410



      PS:如果绝对本文对你有帮助,请给顶一下子。






http://chatgpt.dhexx.cn/article/owInGeFt.shtml

相关文章

易安卓打开Android系统中的解锁方式选择页面(锁屏方式选择)

感谢名单 感谢fylfyl2写的https://blog.csdn.net/fyilun/article/details/21257595 E4A打开锁屏方式页面 Intent intent new Intent(); ComponentName cm new ComponentName("com.android.settings","com.android.settings.ChooseLockGeneric"); inte…

Android锁屏的解锁(九个点),使用画的方式

一、效果展示: 这篇博客有解释大概的步骤: https://blog.csdn.net/weixin_44614751/article/details/103101199 二、代码部分: MainActivity.java中的代码: package com.example.drawunlock1;import androidx.appcompat.app.AppCompatActivity;import android.content.re…

让电脑不被锁屏的方法,亲测有效

通过JS来控制键盘&#xff0c;定时按下SCROLLLOCK键&#xff0c;达到电脑不会被锁屏的效果。 通常公司电脑都会自动锁屏&#xff0c;只是时间有长短。有时候闲着了不用电脑&#xff0c;但是却不想让电脑锁屏。那么可以用js代码来控制键盘的按键循环按下实现不锁屏的效果&#x…

android 强制锁屏app,自制力app强制锁屏

自制力app强制锁屏非常适合在学习工作中没有自律性的用户们&#xff0c;当打开app后开启锁屏状态&#xff0c;手机就打不开了&#xff0c;重启也不可能解除锁屏&#xff1b;在此期间&#xff0c;就可以免于手机的打扰&#xff0c;专注学习&#xff1b;感兴趣的小伙伴们快来下载…

android系统密码设置功能,手机锁屏密码怎么设置 三种安卓手机锁屏方式推荐

手机中有很多应用都是与金钱挂钩&#xff0c;特别是微信与支付宝等等既涉及到隐私又与财产关联&#xff0c;这是后手机的安全就尤为重要的&#xff0c;而手机的锁屏密码就是一道最基本的防护措施&#xff0c;那么手机锁屏密码怎么设置?来看看小编推荐的三种安卓手机锁屏方式吧…

Mac锁屏的几种方式

刚换了工作&#xff0c;公司里给配了MacBook&#xff0c;第一次使用&#xff0c;很多常见操作都不知道快捷键&#xff0c;今天来记录下锁屏的几种方式&#xff1a;电脑为MacBook Pro&#xff0c;OS为MacOS Sierra 10.12.3 1. ctrl shift 右上角开关机键 2. option comm…

C语言for循环结构经典练习

文章目录 一、for循环基本知识二、经典例题及解析1.水仙花数2.求规定范围内的完数3.求规定范围内质数4.计算阶乘之和5.计算55555555555555(类型)6.计算112123123412345(类型)7.判断用户输入正整数的位数8.判断某正整数是否为回文数9.九九乘法表10.统计用户输入的字符中&#xf…

Java基础语法——循环结构

每日正能量 趁你现在还有时间&#xff0c;尽你自己最大的努力&#xff0c;努力做成你最想做的那件事&#xff0c;成为你最想成为的那种人&#xff0c;过着你最想过的那种生活。这个世界永远比你想的要更精彩&#xff0c;不要败给生活。 循环结构 【本章内容】1. while循环 2. d…

Python - 循环结构

循环结构 &#x1f40d;While循环&#x1f40d;While…else…循环&#x1f40d;for循环&#x1f40d;for…else…循环&#x1f40d;循环体结束语句&#x1f40d;嵌套循环 本次主要介绍的是程序的循环结构逻辑。 循环就是按照一定的条件重复的去做一件事情&#xff0c;当条件不成…

While循环结构

1.while循环语句 1.1循环概念 循环是程序设计语言中反复执行某些代码的一种计算机处理过程 1.2 while循环的作用 重复执行某些代码 1.3 while循环的基本语法 while 条件&#xff1a; ____反复执行的代码 …… while及while中的代码块就是while循环代码块。看以下示例&#…

三种循环结构

循环结构&#xff1a;循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。它由循环体中的条件&#xff0c;判断继续执行某个功能还是退出循环。根据判断条件,循环结构又可细分为以下两种形式:先判断后执行的循环结构和先执行后判断的循环结构。下面将对各个循环结…

c语言中循环结构有什么作用,浅谈C语言中循环结构程序设计

高茂婵 吕雪 彭星星 孙新杰 摘要:现在人们对计算机中的算法的要求越来越高,顺序结构已经满足不了人们的需求,我们需要探索更高层次的操作算法。在程序设计中,我们操作的时候经常遇到需要重复执行的情况,而循环结构就恰恰满足了这个要求。在程序设计中,循环结构是算法中必…

Python循环结构

今天我们讲一下python的循环结构&#xff0c;习题比较多&#xff0c;大家多联系&#xff0c;有问题可以给我留言。 目录 一、Python中循环的介绍1、什么是循环&#xff1f;2、循环的作用3、循环的种类 二、while循环基本语法及其应用1、while循环的基本语法2、while循环的执行…

LabVIEW循环结构

LabVIEW可提供For循环和While循环两种循环结构。For循环必须指定循环总次数&#xff0c;达到指定循环次数后程序会自动退出循环&#xff1b;而While循环则不用指定循环次数&#xff0c;只需要指定循环退出条件&#xff0c;如果循环退出条件成立&#xff0c;则退出循环。所以知道…

Python循环结构详解

今天继续给大家介绍Python相关知识&#xff0c;本文主要内容是Python循环结构。 循环是一种编程语言的重要结构&#xff0c;在Python中&#xff0c;存在着两种循环&#xff0c;一种是遍历循环&#xff0c;一种是while循环。 一、遍历循环 所谓遍历循环&#xff0c;即遍历一个…

C语言基础——循环结构

C语言的基本结构之一。在程序中有连续执行的操作可以用循环结构&#xff0c;简化函数&#xff0c;C语言中有三种循环结构&#xff0c;for循环&#xff0c;while循环&#xff0c;do-while循环&#xff0c;其中for循环最常用。 一循环结构概述 1.什么是循环执行 循环执行是循环…

Python中的循环结构

Python中循环的介绍 1、什么是循环 现实生活中&#xff0c;也有很多循环的应用场景&#xff1a; &#xff08;1&#xff09;食堂阿姨打菜&#xff1a;接过顾客的餐盘→询问菜品→打菜→递回餐盘&#xff0c;重复以上过程&#xff0c;直到所有顾客的菜都打完了 &#xff08;…

C++循环结构

C循环结构 一、循环结构1、while 循环1.1 语法1.2 练习题1.3 代码找错 2、do-while 循环2.1 语法2.2 练习题 3、for 循环3.1 语法3.2 练习题 4、三种循环执行顺序 二、循环嵌套1.输出正方形2.输出乘法口诀3.输出平行四边形4.输出数字菱形 三、跳转语句1.break语句1.1 在 switch…

MATLAB循环结构

目录 1、for语句 注意事项 (1)for语句针对行向量的每一个元素执行一次循环语句体&#xff0c;循环的次数就是向量中元素的个数&#xff0c;也可以针对任意向量。 (2)可以在for循环语句体中修改循环变量的值&#xff0c;当程序执行流程再次回到循环开始时&#xff0c;就会自…

【python】循环结构大归纳看这里就够了~

一、循环结构 1.认识循环&#xff1a;反复做同一件事情的情况&#xff0c;称之循环。 2.循环的分类&#xff1a;(1) while循环 &#xff08;2&#xff09;for循环 (3) 循环的嵌套 二、while循环的使用 1.while循环 while循环&#xff1a;代码一直重复&#xff0c;直到条件…