zoukankan      html  css  js  c++  java
  • 有关oppo蝴蝶解锁的三D技术

    oppo手机的界面设计也是很漂亮的。在很多界面中使用了3D技术塑造出了大量华丽的效果。在蝴蝶解锁中使用了两个对称的三D变幻,宛如蝴蝶翅膀上美丽的花纹。在受到用户点击后,随风缓慢上下扇动,充满浪漫的动感色彩。这里只在技术角度做一些探索。

    这个效果由两个子view合成,每个各占整个屏幕的一半。左边子view以右边界为旋转中心,手指向右滑动距离转为绕Y轴施转的角度,角度为正。右边子view以左边界为旋转中心,手指向左滑动距离转为绕Y轴旋转的角度,角度为负,这样恰好与x轴方向一致。这种效果经过转为,可以转为像两扇门一样开关,也是很有意思的。为了保证两个view的对称性,我这里使用一张图片,沿中线分割为两个view的背景。

    可以使用函数Bitmap.createBitmap实现.

     

    view的背景,LeftView.java:

     

     

    package com.magcomm.lockscrenn;
    
    import android.content.Context;
    import android.content.res.Configuration;
    import android.graphics.Bitmap;
    import android.graphics.Camera;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.view.View;
    
    class LeftView extends View {
    	Bitmap leftbm = null;
    	public int g_r = 0;
    	private int scr_w = 0, scr_h = 0;
    
    	public void refrashView(int gr) {
    		g_r = gr;
    		invalidate();
    	}
    	public LeftView(Context context) {
    		super(context);
    	}
    
    	void setBitmap(Bitmap bm) {
    		leftbm = bm;
    		scr_w = bm.getWidth() * 2;
    		scr_h = bm.getHeight();
    	}
    
    	@Override
    	protected void onConfigurationChanged(Configuration newConfig) {
    		// TODO Auto-generated method stub
    		super.onConfigurationChanged(newConfig);
    	}
    
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		// TODO Auto-generated method stub
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
    	}
    
    	@Override
    	protected void onLayout(boolean changed, int left, int top, int right,
    			int bottom) {
    		// TODO Auto-generated method stub
    		super.onLayout(changed, left, top, right, bottom);
    	}
    
    	@Override
    	protected void onDraw(Canvas canvas) {
    		// TODO Auto-generated method stub
    		super.onDraw(canvas);
    		float deg = (g_r * 1.0f / scr_w) * 180;
    		if (deg >= 90) {
    			canvas.drawBitmap(leftbm, rotateY((int) deg), null);
    
    		} else if (deg > 0) {
    			canvas.drawBitmap(leftbm, rotateY((int) deg), null);
    			// lv.bringToFront();
    		} else {
    			canvas.drawBitmap(leftbm, rotateY((int) 0), null);
    		}
    	}
    
    	private int centerX = 0, centerY = 0;
    	// 转动的总距离,跟度数比例1:1
    	private int deltaX = 0, deltaY = 0;
    	// 图片宽度高度
    	private int bWidth, bHeight;
    	// 摄像机
    	private Camera mCamera = new Camera();
    	private Matrix mMatrix = new Matrix();
    
    	Matrix rotateXY(int degreeX, int degreeY) {
    		deltaX = degreeX;
    		deltaY = degreeY;
    		centerX = scr_w / 2;
    		centerY = scr_h / 2;
    		mCamera.save();
    		mCamera.rotateY(deltaY);
    		mCamera.rotateX(-deltaX);
    		mCamera.translate(0, 0, 0);
    		mCamera.getMatrix(mMatrix);
    		mCamera.restore();
    		// 以图片的中心点为旋转中心,如果不加这两句,就是以(0,0)点为旋转中心
    		mMatrix.preTranslate(-centerX, -centerY);
    		mMatrix.postTranslate(centerX, centerY);
    		// mCamera.save();
    
    		// postInvalidate();
    		return mMatrix;
    	}
    
    	Matrix rotateY(int degreeY) {
    		return rotateXY(0, degreeY);
    	}
    
    }
    


    右View的 代码RightView.java:

     

     

    package com.magcomm.lockscrenn;
    
    import android.content.Context;
    import android.content.res.Configuration;
    import android.graphics.Bitmap;
    import android.graphics.Camera;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.view.View;
    
    class RightView extends View {
    	Bitmap rightbm = null;
    	public int g_r = 0;
    	private int scr_w = 0, scr_h = 0;
    
    	public void refrashView(int gr) {
    		g_r = gr;
    		invalidate();
    	}
    	public RightView(Context context) {
    		super(context);
    	}
    
    	void setBitmap(Bitmap bm) {
    		rightbm = bm;
    		scr_w = bm.getWidth() * 2;
    		scr_h = bm.getHeight();
    	}
    
    	@Override
    	protected void onConfigurationChanged(Configuration newConfig) {
    		// TODO Auto-generated method stub
    		super.onConfigurationChanged(newConfig);
    	}
    
    	@Override
    	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    		// TODO Auto-generated method stub
    		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    	}
    
    	@Override
    	protected void onLayout(boolean changed, int left, int top, int right,
    			int bottom) {
    		// TODO Auto-generated method stub
    		super.onLayout(changed, left, top, right, bottom);
    	}
    
    	@Override
    	protected void onDraw(Canvas canvas) {
    		// TODO Auto-generated method stub
    		super.onDraw(canvas);
    		float deg = (g_r * 1.0f / scr_w) * 180;
    		if (deg <= -90) {
    			canvas.drawBitmap(rightbm, rotateY2((int) deg), null);
    		} else if (deg < 0) {
    			canvas.drawBitmap(rightbm, rotateY2((int) deg), null);
    			// rv.bringToFront();
    
    		} else {
    			canvas.drawBitmap(rightbm, rotateY2(0), null);
    		}
    	}
    
    	// 图片的中心点坐标
    	private int centerX = 0, centerY = 0;
    	// 转动的总距离,跟度数比例1:1
    	private int deltaX = 0, deltaY = 0;
    	// 图片宽度高度
    	private int bWidth, bHeight;
    	// 摄像机
    	private Camera mCamera = new Camera();
    	private Matrix mMatrix = new Matrix();
    
    	Matrix rotateXY2(int degreeX, int degreeY) {
    		deltaX = degreeX;
    		deltaY = degreeY;
    		centerX = scr_w / 2;
    		centerY = scr_h / 2;
    		mCamera.save();
    		mCamera.rotateY(deltaY);
    		mCamera.rotateX(-deltaX);
    		mCamera.translate(scr_w / 2, 0, 0);
    		mCamera.getMatrix(mMatrix);
    		mCamera.restore();
    		// 以图片的中心点为旋转中心,如果不加这两句,就是以(0,0)点为旋转中心
    		mMatrix.preTranslate(-centerX, -centerY);
    		mMatrix.postTranslate(centerX, centerY);
    		// mCamera.save();
    
    		// postInvalidate();
    		return mMatrix;
    	}
    
    	Matrix rotateY2(int degreeY) {
    		return rotateXY2(0, degreeY);
    	}
    }
    

     

    下面代码是两个view的动画效果,手指放开时出现缓慢的上下的有衰减的扇动效果,宛如蝴蝶颤抖的翅膀。本来自己写了一个弹簧振子的模型,通过滑动距离转化为弹簧的能量波动,进而转化为扇动的初速度,加上弹簧的阻尼运动的衰减系数,使其做带负加速度的圆周运动。但运行效果不太满意,大概需要使用JNI来实现才能满足吧,后来采取了系统自带的动画实现。

     

    int ani_index = 0;
    
    	private void applyRotation(float start, float end, final View v) {
    		// 计算中心点
    		final float centerX = scr_w / 2.0f;
    		final float centerY = scr_h / 2.0f;
    		final Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, 0,
    				0, 0, 0, 0, 0, centerX, centerY, true);
    		rotation.setDuration(500);
    		// rotation.setFillAfter(true);
    		rotation.setRepeatCount(1);
    		rotation.setRepeatMode(Animation.REVERSE);
    		// rotation.setFillAfter(true);
    		// rotation.setDetachWallpaper(true);
    		rotation.setInterpolator(new AnticipateInterpolator());
    		// 设置监听
    		rotation.setAnimationListener(new Animation.AnimationListener() {
    			public void onAnimationStart(Animation animation) {
    			}
    
    			// 动画结束
    			public void onAnimationEnd(Animation animation) {
    				// tv.post(new SwapViews());
    				startAni(ani_index++);
    			}
    
    			public void onAnimationRepeat(Animation animation) {
    			}
    		});
    		v.startAnimation(rotation);
    	}
    
    	/**
    	 * 
    	 * AccelerateDecelerateInterpolator 在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
    	 * 
    	 * AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
    	 * 
    	 * AnticipateInterpolator 开始的时候向后然后向前甩
    	 * 
    	 * AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
    	 * 
    	 * BounceInterpolator 动画结束的时候弹起
    	 * 
    	 * CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
    	 * 
    	 * DecelerateInterpolator 在动画开始的地方快然后慢
    	 * 
    	 * LinearInterpolator 以常量速率改变
    	 * 
    	 * OvershootInterpolator 向前甩一定值后再回到原来位置
    	 * 
    	 * @param start
    	 * @param end
    	 * @param v
    	 * @param time
    	 */
    	private void applyRotation2(float start, float end, final View v, long time) {
    		// 计算中心点
    		final float centerX = scr_w / 2.0f;
    		final float centerY = scr_h / 2.0f;
    		final Rotate3dAnimation rotation = new Rotate3dAnimation(start, end, 0,
    				0, 0, 0, 0, 0, centerX, centerY, true);
    		rotation.setDuration(time);
    		// rotation.setFillAfter(true);
    		rotation.setRepeatCount(0);
    		rotation.setRepeatMode(Animation.REVERSE);
    		// rotation.setFillAfter(true);
    		// rotation.setDetachWallpaper(true);
    		rotation.setInterpolator(new LinearInterpolator());
    		// 设置监听
    		rotation.setAnimationListener(new Animation.AnimationListener() {
    			public void onAnimationStart(Animation animation) {
    			}
    
    			// 动画结束
    			public void onAnimationEnd(Animation animation) {
    				// tv.post(new SwapViews());
    				startAni(ani_index++);
    			}
    
    			public void onAnimationRepeat(Animation animation) {
    			}
    		});
    		v.startAnimation(rotation);
    	}
    
    	void startAni(int index) {
    		// final int ani_deg1[][] = {{60, 0}, {40, 0}, {20, 0}};
    		// final int ani_deg1[][] = {{0, 60}, {0, 40}, {0, 20}};
    		final int ani_deg1[][] = { { 0, 80 }, { 80, 0 }, { 0, 60 }, { 60, 0 },
    				{ 0, 40 }, { 40, 0 } };
    		// final int ani_deg2[][] = {{-60, 0}, {-40, 0}, {-20, 0}};
    		final int ani_deg2[][] = { { 0, -80 }, { -80, 0 }, { 0, -60 },
    				{ -60, 0 }, { 0, -40 }, { -40, 0 } };
    		final int time[] = { 500, 500, 400, 400, 200, 200 };
    		if (index > 5) {
    			ani_index = 0;
    		} else {
    			if (u_x <= (scr_w / 2)) {
    				applyRotation2(ani_deg1[index][0], ani_deg1[index][1], lv,
    						time[index]);
    			} else {
    				applyRotation2(ani_deg2[index][0], ani_deg2[index][1], rv,
    						time[index]);
    			}
    		}
    
    	}
    

    oppo的解锁效果中,view的背面加入了对窗口buffer的特殊处理。使我们能够看到一个打开新窗口的效果,如下图:

     

    需要添加如下函数。这里调用了系统的隐藏函数Surface.screenshot,必须使用系统签名。加入系统app中才能使用。当然,也许你也可以使用反射实现。

    publicBitmap getScreenBuffer()

    {

    DisplaymDisplay;

    DisplayMetricsmDisplayMetrics;

    MatrixmDisplayMatrix = new Matrix();

     

    WindowManagermWindowManager = (WindowManager) mContext

    .getSystemService(Context.WINDOW_SERVICE);

    mDisplay= mWindowManager.getDefaultDisplay();

    mDisplayMetrics= new DisplayMetrics();

    mDisplay.getRealMetrics(mDisplayMetrics);

    mDisplay.getRealMetrics(mDisplayMetrics);

    float[]dims = { mDisplayMetrics.widthPixels,

    mDisplayMetrics.heightPixels};

     

    mlock= Surface.screenshot((int) dims[0], (int) dims[1]);

    returnmlock;

    }


  • 相关阅读:
    B树、B-树、B+树、B*树介绍,和B+树更适合做文件索引的原因
    异步请求数据加载到表格后根据不同状态改变表格背景颜色【表格背景色】
    Linux/windows查看设置环境变量指令
    【周期性执行事件】MySQL事件(Event)&任务调度
    DEDE列表页调用TAG标签
    poj2488 A Knight's Journey
    [置顶] Codeforces Round #190 (Div. 2)(完全)
    SharePoint 2010 用Event Receiver将文件夹自动变成approved状态 (2)
    .NET领域驱动设计—初尝(三:穿过迷雾走向光明)
    Android解决异常apk on device '0292bea1': Unable to open sync connection!
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3320103.html
Copyright © 2011-2022 走看看