现在的位置: 首页 > 综合 > 正文

Android Jelly Bean滑动解锁控件实现

2013年01月09日 ⁄ 综合 ⁄ 共 4089字 ⁄ 字号 评论关闭

锁屏界面实现:

主要研究Jelly Bean中的解锁界面实现。

界面源码位置:

frameworks\base\Policy\src\com\android\internal\policy\impl\LockScreen.java

根据设备状态,决定解锁界面的显示。继承自LinearLayout,实现包内定义的KeyguardScreen接口。布局由frameworks\base\core\res\layout\keyguard_screen_tab_unlock.xml定义。关键的控件声明为了成员变量mUnlockWidget = findViewById(R.id.unlock_widget);而该id的对应的描述为:

<com.android.internal.widget.multiwaveview.GlowPadView
            android:id="@+id/unlock_widget"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentBottom="true"
            android:gravity="top"
            android:focusable="true"
            android:targetDrawables="@array/lockscreen_targets_with_camera"
android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
          android:directionDescriptions="@array/lockscreen_direction_descriptions"
            android:handleDrawable="@drawable/ic_lockscreen_handle"
            android:outerRingDrawable="@drawable/ic_lockscreen_outerring"
            android:outerRadius="@dimen/glowpadview_target_placement_radius"
            android:innerRadius="@dimen/glowpadview_inner_radius"
            android:snapMargin="@dimen/glowpadview_snap_margin"
            android:feedbackCount="1"
            android:vibrationDuration="20"
            android:glowRadius="@dimen/glowpadview_glow_radius"
            android:pointDrawable="@drawable/ic_lockscreen_glowdot"
            />

代码中对GlowPadView的注释:

A re-usable widget containing a center, outer ring and wave animation.

Glow发热,洋溢,绚丽夺目。命名大概来自于当用户点击激活解锁状态时,那泛起的一圈圈光点吧。

           

“波光”的状态机包含六种状态:(状态的切换由switchToState(int)完成)

IDLE ]  [ START ]  [ FIRST_TOUCH ]  [ TRACKING ]  [ SNAP ]  [ FINISH ]

1、手指触碰中央圆环内:

START  --> FIRST_TOUCH  -->  TRACKING  (TRACKING状态由MotionEvent.MOVE导致,即使手指不动,也会不断的产生)

2、手指未触碰边缘图标抬起:

FINISH --> IDLE

3、手指停留在边缘图标上:

SNAP --> SNAP --> ...

4、手指在边缘图标上抬起:

FINISH -- -(期间触发OnTreggerListener接口的onTrigger) --->  IDLE

OnTreggerListener的接口定义:

1用户手指在中央处落下时,执行 onGrabbed()  -->  onGrabbedStateChang()

2、用户手指未接触到边缘图标抬起:onRelease  -->  onGrabbedStateChange()

3、用户接触边缘图标后抬起:onTrigger  -->  onRelease  -->  onGrabbedStateChange  -->  onFinishFinalAnimation

ArrayList<TargetDrawable> mTargetDrawables是一个重要的成员变量,用于存放可选择的边缘图标和中央解锁按钮的图片资源及控件状态。整个View按角度均分地将各个TargetDrawable渲染在屏幕上。TargetDrawableDrawable对象及状态进行了封装,拥有三种状态STATE_ACTIVESTATE_INACTIVESTATE_FOCUSED

代码中定义了AnimationBundle类,继承自ArrayList<Tweenr>,Tweener封装了ObjectAnimator的使用。类中包含三种动画:mWaveAnmations,mTargetAnimations,mGlowAnimations都是AnimationBundle类型的对象。Glow表示的是当手指停留在屏幕上,在触摸点周围一个圆形区域内生成的光点群。Wave则指的是当用户手指抬起时,且未选中任何边缘目标图标时,从中心向周围扩散开直至全部消失的效果,由GlowPadView的ping()方法产生。


GlowPadView中的光点由PointCloud.java定义,其中主要包含一个Point的线性表(Point封装了点的位置和绘制半径)。PointCloud中实现了计算光点半径,透明度及如何绘制的方法。内部还定义了两个类WaveManager和GlowManager作为MVC中Model,记录两种动作的状态。外围环形的半径是手指在控件上移动时形成的环形区域半径的两倍。PointCloud初始时的内圈的光点数量定义为8个。

初始化时,由内圆的半径计算出最内圆周上相邻两Point的弧长,然后以此弧长为单位,半径每增加一个弧长就画一圈的光点。然后在绘制时,光点的大小由其与中心位置的距离成线性关系。透明度与点距中心的关系,实在没看懂,只能确定是一个变化率随距离增大而增大的递减函数。


回到GlowPadView,View初始化完成了许多属性的加载,这些属性名都定义在frameworks/base/core/res/values/attrs.xml中。重点在于状态之间的切换,状态的改变影响动画和图片的显示。状态的切换由用户的触摸事件引起。


在onTouchEvent()中,对Down、Move、Up和Cancel四种事件进行了处理。


对于MotionEvent.ACTION_CANCEL,表示手势的终止,可视作UP但最好与UP区别处理。猜想可能是手指滑动过程中,屏幕的熄灭,其他手指事件的介入导致的。对于以上每一种事件,都对应一个handleXXX方法,对于MOVE外的三种事件处理,也要调用handleMove(event)进行处理。对于DOWN,先执行handleDown(event)再执行handleMove(event),对于UP或CANCEL事件则率先执行handleMove(event)。DOWN事件使状态首先改变为START,然后再根据触点位置判断是否需要设置成FIRST_TOUCH,UP和CANCEL事件使状态转变为FINISH。


MOVE事件的处理相对较为复杂,需要检测是否接触到边缘图标,检测的方法是判断触点所在角度是否在目标图标所在角度范围内并且离圆心距离大于外圆半径与图标有效半径的差。


另外,由于对ACTION_DOWN,ACTION_MOVE和ACTION_UP事件顺序和机制的不理解,就写了demo来研究一下。结果发现,DOWN与UP之间可以有很多个ACTION_MOVE事件,而且当手指停留在屏幕上时,也会不断地产生DOWN事件,打出的Log显示两个事件之间的时间都比较固定,两部手机,一部20ms,一部15ms,在这一时间段内如果出现较长的滑动轨迹,则会根据一定频率采样关键点封装到一个MotionEvent中,通过getHistoricalX(int)与getHistoricalY(int)获取历史点位置。MotionEvent间的时间片可能是由ActivityThread.main()中的Looper.loop()的执行时间(CPU运行频率?)决定的,因为对框架层不是很了解,所以暂时只能这样理解了。


由于一个MotionEvent持续的时间很短,所以其跨越的距离不会很长。这样也解释了为什么handleMove(MotionEvent)的实现中,一个MOVE事件的历史点中只要有一个触碰到了target即使后面的点远离图标也没关系的原因。


同目录下还有一个MultiWaveView,分析后发现这是ICS上的解锁界面的实现。与GlowPadView设计和实现都很相似。

4.2的闹钟响起时的控制界面也跟解锁界面很相似,代码位于packages/apps/DeskClock/src/com/andorid/deskclock/widget/multiwaveview下。代码组成跟锁屏界面也几乎一样。(左为时钟应用内的代码,右为锁屏界面的代码)

        

图案解锁控件

Frameworks\base\core\java\com\android\internal\widget\LockPatternView.java

抱歉!评论已关闭.