有关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;

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: oppo r15工具是一款用于oppo r15手机的工具软件。通过使用该工具,用户可以轻松地oppo r15手机的屏幕、密码和指纹等所有类型的定。这个工具可以决很多用户遗忘了自己的手机密码或者是购买了二手手机没有得到密码的问题。 使用oppo r15工具的方法非常简单。首先用户需要下载并安装该工具软件,然后连接oppo r15手机到电脑上。接下来按照软件的提示步骤进行操作,工具会自动检测手机是否需要,并且会给出对应的方法。在整个过程中,工具会保证用户的数据安全,不会因为而导致数据丢失。 不过需要注意的是,使用oppo r15工具是存在风险的。某些Oppo R15手机的屏幕或者密码需要花费一定的时间,而且可能会导致手机数据损失。因此,在使用这个工具之前,用户需要备份好自己手机上的重要数据,以免发生数据损失。 总之,oppo r15工具是很方便的一款软件工具,可以帮助oppo r15手机的用户快速各种定,方便用户的使用和维护。 ### 回答2: OPPO R15是一款具有出色性能和美观外观的智能手机。它采用了创新的技术,为用户带来了不便的情况。这时,我们可以使用OPPO R15的工具来决这个问题。 OPPO R15的工具是一种软件程序,可用于手机上的屏幕和密码。它具有简单易用的界面,用户只需按照提示进行操作,即可轻松手机。 使用工具时,首先需要将手机连接到电脑上。然后,运行工具并选择相应的模式。根据具体情况,可以选择使用屏幕密码、指纹或面部识别。然后,根据操作指引完成过程。 值得注意的是,使用工具可能会造成数据丢失。因此,使用前请务必备份手机中的重要数据,以免造成不必要的损失。 总的来说,OPPO R15的工具是一种方便快捷的工具,非常适合那些忘记密码或无法正常手机的用户。通过使用工具,用户可以轻松地决手机问题,并重新使用手机的各项功能。 ### 回答3: OPPO R15工具可以帮助用户手机,让其可以自由地切换运营商或安装第三方ROM。工具可以通过一系列步骤来除手机的定状态。首先,用户需要在手机的设置中找到“关于手机”选项,并点击多次版本号,以开发者选项。接下来,用户需要在开发者选项中启用“OEM”选项。一旦完成这些步骤,用户就可以连接手机到电脑上,并使用工具来OPPO R15。 然而,需要注意的是,在进行之前,用户需要备份所有重要的数据,因为过程会将所有数据清除。同时,手机也会导致保修失效,因此用户需要在之前仔细考虑。 总的来说,OPPO R15工具可以帮助用户手机,使其可以自由地进行一些操作,但用户需要注意过程中可能带来的风险和后果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值