Keyguard 透视效果

来源:互联网 发布:大学生旅游的地方知乎 编辑:程序博客网 时间:2024/05/27 18:17

先上一张图,因为这个效果该叫什么我也不知道。

 

 

锁屏应用在解锁的时候,跟随手指的滑动,显示Launcher或者其他应用的内容,起初并未直接在Keyguard上直接修改,而是做了一个Demo,实现效果后再加入到Keyguard中的。

先来说说怎么实现这个Demo的,然后再来讲怎么应用到Keyguard中去。

 

import java.util.ArrayList;

import java.util.List;

import java.util.Random;

 

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.LinearGradient;

import android.graphics.Paint;

import android.graphics.PorterDuff;

import android.graphics.PorterDuffXfermode;

import android.graphics.RadialGradient;

import android.graphics.Shader;

import android.graphics.drawable.BitmapDrawable;

import android.graphics.drawable.Drawable;

import android.util.AttributeSet;

import android.util.Log;

import android.view.MotionEvent;

import android.widget.FrameLayout;

 

public class FrameView extends FrameLayout{

 

private static final String TAG = "FrameView";

private boolean isMoving = false;

private Bitmap frontBitmap = null;

private Drawable mBackground = null;

private Paint mPaint = null;

private float radius;

private float centerX;

private float centerY;

private float bubbleX;

private float bubbleY;

private Canvas mCanvas;

private Drawable mUnLockDrawable;

private List<Bubble> bubbles = new ArrayList<Bubble>();

private Random random = new Random();//生成随机数

private int widthheight;

public FrameView(Context context, AttributeSet attrs,

int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

 

public FrameView(Context context) {

super(context);

}

 

public FrameView(Context context, AttributeSet attrs) {

super(context, attrs);

}

private void init(){

//frontBitmap = CreateBitmap(Color.GRAY, getWidth(), getHeight());

mBackground = getResources().getDrawable(R.drawable.keyguard_bg);

frontBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.keyguard_bg);

//frontBitmap = Bitmap.createBitmap(frontBitmap, 0, 0, getWidth(), getHeight());

mUnLockDrawable = getResources().getDrawable(R.drawable.unlock_dewdrop);

mPaint = new Paint();

radius = 100f;

mCanvas = new Canvas();

}

@Override

public boolean onTouchEvent(MotionEvent event) {

float ax = event.getX();

float ay = event.getY();

int ac = event.getAction();

switch(ac){

case MotionEvent.ACTION_DOWN:

isMoving = false;

centerX = ax;

centerY = ay;

invalidate();

return true;

case MotionEvent.ACTION_MOVE:

Log.d(TAG,"onTouchEvent--------------ACTION_MOVE");

isMoving = true;

bubbleX = ax;

bubbleY = ay;

radius = (float) Math.sqrt((ax-centerX)*(ax-centerX)+(ay-centerY)*(ay-centerY));

invalidate();

return true;

case MotionEvent.ACTION_UP:

isMoving = false;

bubbles.clear();

invalidate();

break;

}

return super.onTouchEvent(event);

}

class BubbleThread extends Thread{

@Override

public void run() {

 

while (true) {

try {

Thread.sleep(random.nextInt(3) * 300);

catch (InterruptedException e) {

e.printStackTrace();

}

Bubble bubble = new Bubble();

int radius = random.nextInt(30);

while (radius == 0) {

radius = random.nextInt(30);

}

float speedY = random.nextFloat()*5;

while (speedY < 1) {

speedY = random.nextFloat()*5;

}

bubble.setRadius(radius);

bubble.setSpeedY(speedY);

bubble.setX(bubbleX);

bubble.setY(bubbleY);

float speedX = random.nextFloat()-0.5f;

while (speedX == 0) {

speedX = random.nextFloat()-0.5f;

}

bubble.setSpeedX(speedX*2);

bubbles.add(bubble);

}

}

}

@Override

protected void dispatchDraw(Canvas canvas) {

Log.d(TAG,"onTouchEvent--------------dispatchDraw---->>>>>>>>>>");

if(frontBitmap == null || mPaint == null || mCanvas == null){

init();

}

Bitmap unLockBitmap =  ((BitmapDrawable)mUnLockDrawable).getBitmap();

int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,

Canvas.MATRIX_SAVE_FLAG |

Canvas.CLIP_SAVE_FLAG |

Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |

Canvas.FULL_COLOR_LAYER_SAVE_FLAG |

Canvas.CLIP_TO_LAYER_SAVE_FLAG);

canvas.drawBitmap(frontBitmap, 0, 0,null);

if(isMoving){

Shader circleShader = new RadialGradient  (centerXcenterYradiusnew int[] {

Color.GREEN, Color.RED, Color.BLUE,Color.TRANSPARENT},

null, Shader.TileMode.REPEAT);

mPaint.setShader(circleShader);

mPaint.setAntiAlias(true);

mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

canvas.drawCircle(centerXcenterYradiusmPaint);

//canvas.drawBitmap(unLockBitmap,centerX,centerY,mPaint);

this.mPaint.setXfermode(null);

canvas.restoreToCount(sc);

Log.d(TAG,"onTouchEvent--------------dispatchDraw---drawCircle");

width = getWidth();

height = getHeight();

if (isMoving) {

new Thread() {

public void run() {

while (bubbles.size() <=40) {

try {

Thread.sleep(random.nextInt(3) * 300);

catch (InterruptedException e) {

e.printStackTrace();

}

Bubble bubble = new Bubble();

int radius = random.nextInt(30);

while (radius == 0) {

radius = random.nextInt(30);

}

float speedY = random.nextFloat()*5;

while (speedY < 1) {

speedY = random.nextFloat()*5;

}

bubble.setRadius(radius);

bubble.setSpeedY(speedY);

bubble.setX(bubbleX);

bubble.setY(bubbleY);

float speedX = random.nextFloat()-0.5f;

while (speedX == 0) {

speedX = random.nextFloat()-0.5f;

}

bubble.setSpeedX(speedX*2);

bubbles.add(bubble);

}

};

}.start();

}

Paint paint = new Paint();

// 绘制渐变正方形作为背景

// 我更倾向于用图片做背景

Shader shader = new LinearGradient(0, 0, 0, heightnew int[] {

getResources().getColor(android.R.color.holo_blue_bright),

getResources().getColor(android.R.color.holo_blue_light),

getResources().getColor(android.R.color.holo_blue_dark) },

null, Shader.TileMode.REPEAT);

paint.setShader(shader);

//canvas.drawRect(0, 0, width, height, paint);

//绘制气泡

//paint.reset();

paint.setColor(0xFFCCFF);

paint.setAlpha(45);//设置不透明度:透明为0,完全不透明为255

List<Bubble> list = new ArrayList<Bubble>(bubbles);

//依次绘制气泡

for (Bubble bubble : list) 

{

//碰到上边界从数组中移除

if (bubble.getY() - bubble.getSpeedY() <= 0) 

{

bubbles.remove(bubble);

}

//碰到左边界从数组中移除

else if(bubble.getX() - bubble.getRadius() <= 0)

{

bubbles.remove(bubble);

}

//碰到右边界从数组中移除

else if(bubble.getX() + bubble.getRadius() >= width)

{

bubbles.remove(bubble);

}

else 

{

int i = bubbles.indexOf(bubble);

if (bubble.getX() + bubble.getSpeedX() <= bubble.getRadius()) {

bubble.setX(bubble.getRadius());

else if (bubble.getX() + bubble.getSpeedX() >= width

- bubble.getRadius()) {

bubble.setX(width - bubble.getRadius());

else {

bubble.setX(bubble.getX() + bubble.getSpeedX());

}

bubble.setY(bubble.getY() - bubble.getSpeedY());

bubbles.set(i, bubble);

canvas.drawCircle(bubble.getX(), bubble.getY(),

bubble.getRadius(), paint);

}

}

//刷新屏幕

invalidate();

}

super.dispatchDraw(canvas);

}

 

private class Bubble {

//气泡半径 

private float radius;

//上升速度

private float speedY;

//平移速度

private float speedX;

//气泡x坐标

private float x;

// 气泡y坐标

private float y;

public float getRadius() {

return radius;

}

public void setRadius(float radius) {

this.radius = radius;

}

public float getX() {

return x;

}

public void setX(float x) {

this.x = x;

}

public float getY() {

return y;

}

 

public void setY(float y) {

this.y = y;

}

 

public float getSpeedY() {

return speedY;

}

public void setSpeedY(float speedY) {

this.speedY = speedY;

}

public float getSpeedX() {

return speedX;

}

public void setSpeedX(float speedX) {

this.speedX = speedX;

}

}

}

这里需要说明的是,气泡效果是我从网上找的一个Demo加进来的,具体是哪下的,我忘了。。。

请别在意这些细节—_—

 

关键代码在dispatchDraw中

// 保存图层

int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,

Canvas.MATRIX_SAVE_FLAG |

Canvas.CLIP_SAVE_FLAG |

Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |

Canvas.FULL_COLOR_LAYER_SAVE_FLAG |

Canvas.CLIP_TO_LAYER_SAVE_FLAG);

canvas.drawBitmap(frontBitmap, 0, 0,null);

if(isMoving){

Shader circleShader = new RadialGradient  (centerX, centerY, radius, new int[] {

Color.GREEN, Color.RED, Color.BLUE,Color.TRANSPARENT},

null, Shader.TileMode.REPEAT);

mPaint.setShader(circleShader);

mPaint.setAntiAlias(true);

         //设置画笔的叠加方式

mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

canvas.drawCircle(centerX, centerY, radius, mPaint);

this.mPaint.setXfermode(null);

           //返回图层

canvas.restoreToCount(sc);

这里主要有两点,一个是设置图层,一个是设置叠加方式。

int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,

Canvas.MATRIX_SAVE_FLAG |

Canvas.CLIP_SAVE_FLAG |

Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |

Canvas.FULL_COLOR_LAYER_SAVE_FLAG |

Canvas.CLIP_TO_LAYER_SAVE_FLAG);

保存一个新建图层,在调用canvas.restoreToCount(sc);之前所有的绘制操作会在这个图层中进行。

 

mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));设置画笔的叠加模式,这里有很多种模式可以选择进而达到我们想要的效果,关于这点我也是在别人的博客中学习到的。

 

canvas.drawCircle(centerX, centerY, radius, mPaint);

根据手指移动绘制一个圆圈,这个圆圈就是我们透视的区域


 

 

还有一点是需要注意的是,layout或者view的背景图必须是draw上去的,不能通过设置setBackground或者在布局文件中指定,因为setBackground的绘制流程是在onDrawDrawdispatchDraw之前的。

 

其实整个Demo很简单,代码就300行的样子,核心代码就是上面贴出来的,下面再说说怎么把这个效果应用到锁屏Keyguard模块中去。

 

   首先与4.2不同的是4.4.2源码中的Keyguard代码是位于framework/base/package/Keyguard中的,编译生成的是一个apk,而不再是一个jar包了。

这个源码中其实就只有一个包:


 

这个com.sprd.keyguard包是展讯平台的包,可以忽略。代码量其实不多,时间充裕的,其实完全可以看一遍。

Keyguard修改的文件其实也不多,主要是KeyguardViewManager这个类,在这个类里有个ViewManagerHost内部类,ViewManagerHost是一个FrameLayout,是Keyguard最顶层的布局,所以我们在这个地方修改,修改方法就是上面Demo的方法,不再赘述。

这个地方修改完之后的效果其实只改变了手指滑动的效果,锁屏方式并没有改变。修改锁屏方式是在KeyguardHostViewshowPrimarySecurityScreen方法中:

    void showPrimarySecurityScreen(boolean turningOff) {

        SecurityMode securityMode = mSecurityModel.getSecurityMode();

        if (DEBUG) Log.v(TAG"showPrimarySecurityScreen(turningOff=" + turningOff + ")");

        if (!turningOff &&

                KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {

            securityMode = mSecurityModel.getAlternateFor(securityMode);

        }

        if (mUniverseui_support && !mLockPatternUtils.isLockScreenDisabled() && KeyguardViewManager.IsShowUUILock()

                && mUniverseui_double_lockscreen_support) {

            securityMode = SecurityMode.None;

        }

// 在这里修改,securityMode可以自己去自定义一个

//        showSecurityScreen(securityMode);   

}

 

下一步就是布局了,Keyguard默认的解锁模式是Selector,不同的解锁方式对应不同的布局,关于这个布局问题,最好是自己去看看Keyguard原生的布局层次,这里就不介绍了,逼近也不可能完全套用。

在KeyguardHostView中,有两处地方都是需要根据securityMode来获取布局或者其id的

getSecurityViewIdForMode和showNextSecurityScreenOrFinish。

修改到这里了,布局就替换过来了界面的显示效果就基本出来了,此时应该只能够看到一个黑色圈圈,这个圈圈为什么不是透明的呢?前面说到了Demo中是设置过Activity为透明,而Keyguard是没有Activity的,只有一个Service。Keyguard的布局是很奇怪的,上面说到ViewManagerHost是顶层布局,但其实在其上方,还有一层布局。这个布局是在framework中添加的,所以我们在Keyguard源码中并没有查到。具体添加的位置是:

       frameworks\base\policy\src\com\android\internal\policy\impl\keyguard\KeyguardServiceDelegate.java

createScrim方法中

lp.setTitle("KeyguardScrim");

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        wm.addView(view, lp);

这里的wm.addView(view,lp);需要注释掉,不然在锁屏的最顶层,总有一层黑色的布局,我们修改的ViewManagerHost就不是顶层布局了

 

另外

frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java

    public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) {

        return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD;

    }

Return 也需要修改成false

 

同时,KeyguardViewManager类里的maybeCreateKeyguardLocked也需要修改一个地方,

            int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN

                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR

                    | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;

//                    | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;

FLAG_SHOW_WALLPAPER这个flags需要注释掉。

 

到此,整个锁屏就差不多了,至于新增了securityMode,需要新的布局什么的,就自己仿造KeyguardSelectorView写一个就行,把不需要的GlowPadView去掉就行。

 

Ps:WPS真心难用—_—,博客写的少,大牛们的那种编辑风格还不会用

0 0
原创粉丝点击