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 width, height;
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 (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);
//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, height, new 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的绘制流程是在onDraw,Draw和dispatchDraw之前的。
其实整个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的方法,不再赘述。
这个地方修改完之后的效果其实只改变了手指滑动的效果,锁屏方式并没有改变。修改锁屏方式是在KeyguardHostView的showPrimarySecurityScreen方法中:
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真心难用—_—,博客写的少,大牛们的那种编辑风格还不会用
- Keyguard 透视效果
- 透视效果
- keyguard
- Keyguard
- iOS实现透视效果
- iOS实现透视效果
- 三维效果(透视转换)
- QuartzCore CATransform3D 设置透视效果
- CATransform3D 透视的形变效果
- Silverlight之三维透视效果(11)
- (android控件)界面配置透视效果按钮
- JS伪3D 图形透视效果
- cocos2d-x实现透视朦胧光照效果
- cocos2d-x实现透视朦胧光照效果
- AR技术应用 の 照片透视效果
- Photoshop入门实例教程:透视效果…
- 3D转换加上透视效果
- css 开门-透视perspective动画效果
- 安装apk未知来源默认选上
- Xcode编译Undefined symbols for architecture xxx 错误总结
- RevitAPI:不要在遍历(Iterate)过滤出来的收集器(FilteredElementCollector)时修改文档(Document)
- 计算器代码
- 修改默认重力感应
- Keyguard 透视效果
- windows信号量使用
- Adaboost 算法
- bat中echo 和 @echo有何区别?
- 这是一篇测试
- 一例硬盘逻辑坏道修复案例
- python defaultdict
- iOS开发常用的RGB色值
- 数组