在Android 4.0上做锁屏有一段时间了,期间改了很多bug,也按不同需求做了不少锁屏,其中比较满意的作品包括两个。一是,添加一个锁屏可以和原生锁屏进行切换;二是,自己写一个锁屏view去替换原生锁屏。在这里,先把第二种实现方法写出来。
先介绍下这个锁屏view:
锁屏效果如图所示。功能简介如下:
1.相机、电话、home、短信四个icon向中间拖拽会进入相应的应用程序,拖拽的同时有波纹动画。
2.拖拽中间的图案向外任意方向解锁。
3.可以在相册中选取一张图片或者用相机拍摄一张图片作为中心的图案。
4.进入home解锁和中心图案解锁的区别是:进入home,就是进入launcher,比如有两个launcher,会弹出选择框。锁屏之前如果打开了应用程序,home解锁不会进入打开的应用程序,而中心图案解锁会进入之前打开的程序。
实现这个锁屏主要在framework中修改,当然,设置锁屏图案功能是在settings中修改。在framework中主要有三个文件:
1.frameworks\base\core\java\com\android\internal\widget\multiwaveview\FxFloatImageView.java
2.frameworks\base\core\res\res\layout\keyguard_screen_tab_unlock.xml
3.frameworks\base\policy\src\com\android\internal\policy\impl\ LockScreen.java
这三个文件其实就是自定义view、加载自定义view的xml文件、锁屏的逻辑处理。其中,自定义view一般放在frameworks\base\core\java\com\android\internal\widget\目录下,我为了利用4.0的波纹动画,才放在子目录multiwaveview下面的。
下面逐一道来。
一、自定义锁屏view:
这里给出部分代码,完整代码可以通过连接下载
final AlphaAnimation animation = new AlphaAnimation(1, 0);
private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut;
private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>();
private ArrayList<Tweener> mChevronAnimations = new ArrayList<Tweener>();
private static final int CHEVRON_INCREMENTAL_DELAY = 160;
private static final int CHEVRON_ANIMATION_DURATION = 850;
private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
};
public interface OnTriggerListener {
/**
* The interface was triggered because the user let go of the handle
* without reaching the threshold.
*/
public static final int NO_HANDLE = 0;
public static final int SMS_HANDLE = 1;
public static final int DIAR_HANDLE = 2;
public static final int CAM_HANDLE = 3;
public static final int UNLOCK_HANDLE = 4;
public static final int HOME_HANDLE = 5;
/**
* Called when the user moves a handle beyond the threshold.
*
* @param v
* The view that was triggered.
* @param whichHandle
* Which "dial handle" the user grabbed, either
* {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}.
*/
void onTrigger(View v, int mHandle);
/**
* Called when the "grabbed state" changes (i.e. when the user either
* grabs or releases one of the handles.)
*
* @param v
* the view that was triggered
* @param grabbedState
* the new state: {@link #NO_HANDLE}, {@link #LEFT_HANDLE},
* or {@link #RIGHT_HANDLE}.
*/
}
public FxFloatImageView(Context context) {
super(context);
// TODO Auto-generated constructor stub
logv("FxFloatImageView(Context context)");
}
public FxFloatImageView(Context context, AttributeSet attrs) {
super(context, attrs);
logv("FxFloatImageView(Context context, AttributeSet attrs)");
Resources res = context.getResources();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MultiWaveView);
mTracking = false;
mInit = true;
isFinish = false;
mContext = context;
resetScreenState(mTracking);
getBitmapFromQueryDB();
loadView(context);
this.setWillNotDraw(false);
// Read chevron animation drawables
final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable,
R.styleable.MultiWaveView_rightChevronDrawable,
R.styleable.MultiWaveView_topChevronDrawable,
R.styleable.MultiWaveView_bottomChevronDrawable };
for (int chevron : chevrons) {
Drawable chevronDrawable = a.getDrawable(chevron);
for (int i = 0; i < 3; i++) {
mChevronDrawables
.add(chevronDrawable != null ? new TargetDrawable(res,
chevronDrawable) : null);
}
}
hideChevrons();
// handler.postDelayed(runnable, 150);
// handler.postDelayed(runnableIndication, 500);
}
private void hideChevrons() {
for (TargetDrawable chevron : mChevronDrawables) {
if (chevron != null) {
chevron.setAlpha(0.0f);
}
}
}
protected void onDraw(Canvas canvas) {
int i =0;
for (TargetDrawable target : mChevronDrawables) {
if (target != null ) {
target.draw(canvas);
}
}
}
private void startChevronAnimation() {
final float chevronAnimationDistance = 80;
final float from[][] = {
{
mWidth / 2 - 150 + 20,
mCircleBgViewY + mCircleBgViewHeight / 2
- mChevronHeight / 2 + 20 }, // left
{
mWidth / 2 + 150 - mChevronWidth + 20,
mCircleBgViewY + mCircleBgViewHeight / 2
- mChevronHeight / 2 + 20 }, // right
{ mWidth / 2 - mChevronHeight / 2 + 25,
mCircleBgViewY + mCircleBgViewHeight / 2 - 135 + 10, }, // top
{ mWidth / 2 - mChevronHeight / 2 + 25,
mCircleBgViewY + mCircleBgViewHeight / 2 + 100 + 20 } }; // bottom
final float to[][] = {
{
mWidth / 2 - 150 + 80 + 20,
mCircleBgViewY + mCircleBgViewHeight / 2
- mChevronHeight / 2 + 20 }, // left
{
mWidth / 2 + 150 - mChevronWidth - 80 + 20,
mCircleBgViewY + mCircleBgViewHeight / 2
- mChevronHeight / 2 + 20 }, // right
{
mWidth / 2 - mChevronHeight / 2 + 25,
mCircleBgViewY + mCircleBgViewHeight / 2 - 135 + 80
+ 10, }, // top
{
mWidth / 2 - mChevronHeight / 2 + 25,
mCircleBgViewY + mCircleBgViewHeight / 2 + 100 - 80
+ 20 } }; // bottom
mChevronAnimations.clear();
final float startScale = 0.5f;
final float endScale = 2.0f;
for (int direction = 0; direction < 4; direction++) {
if(show[direction]){
for (int count = 0; count < 3; count++) {
int delay = count * CHEVRON_INCREMENTAL_DELAY;
final TargetDrawable icon = mChevronDrawables.get(direction * 3
+ count);
if (icon == null) {
continue;
}
mChevronAnimations.add(Tweener.to(icon,
CHEVRON_ANIMATION_DURATION, "ease",
mChevronAnimationInterpolator, "delay", delay, "x",
new float[] { from[direction][0], to[direction][0] },
"y",
new float[] { from[direction][1], to[direction][1] },
"alpha", new float[] { 1.0f, 0.0f }, "scaleX",
new float[] { startScale, endScale }, "scaleY",
new float[] { startScale, endScale }, "onUpdate",
mUpdateListener));
}
show[direction] = false;
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
logv("onLayout");
logv("onLayout changed=" + String.valueOf(changed));
if (changed) {
mUnlockView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mUnlockViewWidth = mUnlockView.getMeasuredWidth();
mUnlockViewHeight = mUnlockView.getMeasuredHeight();
mUnlockBgView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mUnlockBgViewWidth = mUnlockBgView.getMeasuredWidth();
mUnlockBgViewHeight = mUnlockBgView.getMeasuredWidth();
mCircleBgView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mCircleBgViewWidth = mCircleBgView.getMeasuredWidth();
mCircleBgViewHeight = mCircleBgView.getMeasuredHeight();
mSmsView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mCommonViewWidth = mSmsView.getMeasuredWidth();
mCommonViewHeight = mSmsView.getMeasuredHeight();
mChevronLeft.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mChevronWidth = mChevronLeft.getMeasuredWidth();
mChevronHeight = mChevronLeft.getMeasuredHeight();
logv("onLayout l=" + String.valueOf(l));
logv("onLayout t=" + String.valueOf(t));
logv("onLayout r=" + String.valueOf(r));
logv("onLayout b=" + String.valueOf(b));
logv("onLayout mUnlockViewWidth="
+ String.valueOf(mUnlockViewWidth));
logv("onLayout mUnlockViewHeight="
+ String.valueOf(mUnlockViewHeight));
logv("onLayout mUnlockBgViewWidth="
+ String.valueOf(mUnlockBgViewWidth));
logv("onLayout mUnlockBgViewHeight="
+ String.valueOf(mUnlockBgViewHeight));
Log.v(TAG,
"onLayout mCircleBgViewWidth="
+ String.valueOf(mCircleBgViewWidth));
logv("onLayout mCircleBgViewHeigh="
+ String.valueOf(mCircleBgViewHeight));
logv("onLayout mBotWidth=" + String.valueOf(mBotWidth));
logv("onLayout mBotHeight=" + String.valueOf(mBotHeight));
logv("onLayout mCommonViewWidth="
+ String.valueOf(mCommonViewWidth));
logv("onLayout mCommonViewHeight="
+ String.valueOf(mCommonViewHeight));
logv("the center X is:" + String.valueOf(mWidth / 2));
logv("the center Y is:"
+ String.valueOf(mCircleBgViewY + mCircleBgViewHeight / 2));
mWidth = r - l;
mHeight = b - t;
// mBgview.layout(l + (mWidth - mCircleBgViewWidth) / 2, t + top, r
// - (mWidth - mCircleBgViewWidth) / 2, t + top
// + mCircleBgViewHeight);
mUnlockViewX = l + (mWidth - mUnlockViewWidth) / 2;
mUnlockViewY = t + top + 3
+ (mCircleBgViewHeight - mUnlockViewHeight) / 2;
mUnlockBg