版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、ViewPager 的 3D 效果
1.ViewPager
先直接上个简单的 ViewPager 的 demo。
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
TranslateFragment
public class TranslateFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Bundle bundle = getArguments();
int layoutId = bundle.getInt("layoutId");
View view = inflater.inflate(layoutId, null);
return view;
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
private ViewPager vp;
//这三个布局界面就不贴出来,随便一个界面都可以
private int[] layouts = {
R.layout.welcome1,
R.layout.welcome2,
R.layout.welcome3
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vp = (ViewPager) findViewById(R.id.vp);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
vp.setAdapter(adapter);
}
class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment f = new TranslateFragment();
Bundle bundle = new Bundle();
bundle.putInt("layoutId", layouts[position]);
f.setArguments(bundle );
return f;
}
@Override
public int getCount() {
return 3;
}
}
}
使用 v4 支持包下的 ViewPager,可以很方便的做出分界面滑动效果。运行效果:
2.TransFormer
ViewPager 在切换界面的时候想要使用 3D 效果,需要用到一个界面转换器 PageTransformer,也是在 v4 支持包里面(v4 包会不断更新,较早的 v4 包没有 PageTransformer),PageTransformer 是一个接口,需要实现。
实现 PageTransformer 接口,需要实现 transformPage(View page, float position) 方法,在滑动过程中,每一个页面 View 都会不停调用这个方法。
transformPage(View page, float position)
page:当前页
position:当前页的位置,在屏幕正中间为 0,在左边为负,右边为正。
来个简单的 PageTransformer,滑动的时候,当前界面慢慢缩小滑出去,下一个界面慢慢放大进入屏幕。
WelcomeTransFormer
public class WelcomeTransFormer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
page.setScaleX(1 - Math.abs(position));
page.setScaleY(1 - Math.abs(position));
}
}
记得要调用 ViewPager 的 setPageTransformer。
MainActivity
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vp = (ViewPager) findViewById(R.id.vp);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
vp.setPageTransformer(true, new WelcomeTransFormer());
vp.setAdapter(adapter);
}
...
}
效果:
3.3D 翻转
在切换过程中,让当前界面与下一个界面绕公共边进行旋转即可。
WelcomeTransFormer
public class WelcomeTransFormer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
//0~45度
if (position > -1 && position < 1){
page.setPivotX(position < 0f?page.getWidth() : 0f);//左边页面:0~-1;右边的页面:1~0
//不进行设置旋转点的 Y 也可以,默认中点。
page.setPivotY(page.getHeight()*0.5f);
//0~45度
page.setRotationY(position*45f);
}
}
效果:
注:个人测试时候在华为安卓7.0的手机,旋转效果直接白屏,原因未知。有人说是华为手机在 3D 这块效果处理一直有问题,但是使用三星的模拟器,在一些 3D 效果偶尔也碰到问题。
二、Camera
android.graphics.Camera.java,系统还有个是硬件的 Camera,别导错了。
源码里面的 Camera 备注:用来计算 3D 变换,以及生成一个用于画布绘制的 matrix 矩阵。
/**
* A camera instance can be used to compute 3D transformations and
* generate a matrix that can be applied, for instance, on a
* {@link Canvas}.
*/
Camera 内部实际也是用上 OpenGL。很多时候我们用 OpenGL 做 3D 特效,其实 Camera 可以满足绝大部分的需要。 Camera 内部很多方法是 native 的,由底层 C 去实现调用。
1.Camera 的方向
Camera X 的正方向是水平向右,Y 的正方向是向上的,这点与屏幕的方向不一样,Z 的正方向是远离眼睛的方向(效果就是 Z 为正,缩小; Z 为负,放大)。
2.Camera 简单 Demo
自定义 Camera 类,直接画一个圆。
CameraView
public class CameraView extends View {
private Paint paint;
public CameraView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(400, 400, 300, paint);
}
}
在布局文件引用改控件。
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.xiaoyue.camera.MainActivity">
<com.xiaoyue.camera.CameraView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
效果:
3.使用 Camera 进行变化
CameraView
public class CameraView extends View {
private Camera camera;
private Matrix matrix;
private Paint paint;
public CameraView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
camera = new Camera();
matrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.translate(100, 100, 0);
camera.getMatrix(matrix);
camera.restore();
canvas.concat(matrix);
canvas.drawCircle(400, 400, 300, paint);
}
}
效果:
这边在 Y 方向上是正的,但是往上平移,再次说明一下:Camera 里面的坐标系跟 Android 的屏幕坐标系不一样。
4.Camera 的 Z 方向
CameraView
public class CameraView extends View {
...
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.translate(100, 100, 400);
...
}
}
效果:
Z 方向为正,即远离眼睛方向,所以对比上一个没有进行 Z 方向变化的圆,这个圆缩小了。
三、Camera 的旋转
新建一个 CameraView2 的自定义控件,对图片进行旋转。
CameraView2 :
public class CameraView2 extends View {
private final Camera camera;
private final Matrix matrix;
private Paint paint;
Bitmap bitmap;
public CameraView2(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.GREEN);
camera = new Camera();
matrix = new Matrix();
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.zuobiao);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.rotateX(45);
camera.getMatrix(matrix);
camera.restore();
canvas.drawBitmap(bitmap,matrix,paint);
}
}
效果:
绕 X 轴旋转,则图片的下方离眼睛较近,所以放大。有时候我们需要对图片进行中心旋转,我们没办法把图片的中点设为旋转点,所以需要在旋转前把图片的中点移到左上角,旋转后在移动回来。
Camera 的旋转最终是通过矩阵来实现的,可以直接对矩阵进行前乘和后乘进行设置。
CameraView2 :
public class CameraView2 extends View {
...
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
matrix.reset();
camera.save();
camera.rotateX(45);//绕着X轴旋转
camera.getMatrix(matrix);
camera.restore();
//前乘矩阵---先平移矩阵,再进行变换
matrix.preTranslate(-bitmap.getWidth()/2,-bitmap.getHeight()/2);
//变换玩之后,再恢复到原来的位置
matrix.postTranslate(bitmap.getWidth()/2,bitmap.getHeight()/2);
canvas.drawBitmap(bitmap,matrix,paint);
}
}
效果:
四、Camera 的翻转动画
先来看一下效果:
2D 滚动:
3D 整体滚动:
尾部逐渐分离再合并:
百叶窗:
各模块依次滚动:
注:这些效果我在华为 7.0 系统运行还是有一些异常,各型号手机不确定是否有效果,应该跟手机的底层对 Carema 处理有关。
这边采用一个自定义控件实现,翻转模式在 xml 中根据自定义属性进行设置。这边直接上代码了,具体内容都卸载注释里面。
Camera3DView
public class Camera3DView extends View {
private Context context;
//画笔
private Paint paint;
//画布
private Camera camera;
//矩阵
private Matrix matrix;
//图片集合
private List<Bitmap> bitmapList;
//每个图片要切割成小块的个数
private int partNumber = 1;
//切割后图片的存储(有多张图片)
private List<Bitmap[]> bitmapsList;
//滚动方向:1竖直方向 其他为水平方向
private int direction = 1;
//滚动模式
private RollMode rollMode = RollMode.SepartConbine;
//图片下标,前一个,当前个、下一个
private int preIndex = 0, currIndex = 0, nextIndex = 0;
//控件的宽高
private int viewWidth, viewHeight;
//切割小块的宽高
int averageWidth = 0, averageHeight = 0;
//动画执行的进度
private float progress = 0;
//各模块依次滚动 RollInTurn 下情况下,各小模块的动画执行时间占总动画时间比
private float partAnimatorPercent = 0;
//小模块开始执行动画时间差
private float partSpaceTime = 0;
public Camera3DView(Context context) {
this(context, null);
}
public Camera3DView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Camera3DView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public Camera3DView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.Camera3DView_Param);
int caremaRollMode = a.getInt(R.styleable.Camera3DView_Param_camera_roll_mode, 0);
partAnimatorPercent = a.getFloat(R.styleable.Camera3DView_Param_part_animator_percent, 1);
partNumber = a.getInt(R.styleable.Camera3DView_Param_partNumber, 3);
direction = a.getInt(R.styleable.Camera3DView_Param_camera_roll_direction, 1);
if (caremaRollMode == 0) {
rollMode = RollMode.Roll2D;
} else if (caremaRollMode == 1) {
rollMode = RollMode.Whole3D;
} else if (caremaRollMode == 2) {
rollMode = RollMode.SepartConbine;
} else if (caremaRollMode == 3) {
rollMode = RollMode.Jalousie;
} else {
if (partAnimatorPercent <= 0 || partAnimatorPercent >= 1) {
rollMode = RollMode.SepartConbine;
}else {
rollMode = RollMode.RollInTurn;
partSpaceTime = (1 - partAnimatorPercent) / partNumber;
}
}
//初始化
init(context);
}
/**
* 初始化一些参数
* @param context
*/
private void init(Context context) {
bitmapList = new ArrayList<>();
bitmapsList = new ArrayList<>();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
camera = new Camera();
matrix = new Matrix();
this.context = context;
}
/**
* 添加图片
* @param bitmap 要添加的图片
*/
public void addBitmap(Bitmap bitmap){
bitmapList.add(bitmap);
//这是为了处理布局完之后添加的图片
if (viewWidth != 0 && viewHeight != 0) {
//缩放处理bitmap
bitmap = scaleBitmap(bitmap);
//切割操作
initBitmap(bitmap);
}
}
/**
* 根据给定的宽和高进行拉伸
* @param origin 原图
* @return new Bitmap
*/
private Bitmap scaleBitmap(Bitmap origin) {
if (origin == null) {
return null;
}
int height = origin.getHeight();
int width = origin.getWidth();
float scaleWidth = ((float) viewWidth) / width;
float scaleHeight = ((float) viewHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
return newBM;
}
private void initBitmap(Bitmap bitmap) {
//初始化
initIndex();
Bitmap partBitmap;
Bitmap[] partBitmaps = new Bitmap[partNumber];
for (int i = 0; i < partNumber; i ++){
Rect rect;
if(rollMode != RollMode.Jalousie){
if(direction == 1){
//纵向切割
rect = new Rect(i * averageWidth, 0, (i + 1) * averageWidth, viewHeight);
//按照矩形区域切割图片
partBitmap = getPartBitmap(bitmap, i * averageWidth, 0, rect);
}else{
//横向切割
rect = new Rect(0, i * averageHeight, viewWidth, (i + 1) * averageHeight);
partBitmap = getPartBitmap(bitmap, 0, i * averageHeight, rect);
}
}else{
if (direction == 1) {
//横向切割
rect = new Rect(0, i * averageHeight, viewWidth, (i + 1) * averageHeight);
partBitmap = getPartBitmap(bitmap, 0, i * averageHeight, rect);
} else {
//纵向切割
rect = new Rect(i * averageWidth, 0, (i + 1) * averageWidth, viewHeight);
partBitmap = getPartBitmap(bitmap, i * averageWidth, 0, rect);
}
}
partBitmaps[i] = partBitmap;
}
bitmapsList.add(partBitmaps);
}
/**
* 切割图片
* @param bitmap
* @param left
* @param top
* @param rect
* @return
*/
private Bitmap getPartBitmap(Bitmap bitmap, int left, int top, Rect rect) {
return Bitmap.createBitmap(bitmap,left,top,rect.width(),rect.height());
}
/**
* 根据当前图片的下标,计算前一个与后一个的下标
*/
private void initIndex() {
int listSize = bitmapList.size();
nextIndex = currIndex +1;
preIndex = currIndex -1;
if(nextIndex > listSize -1){
nextIndex = 0;//当到了边界,再循环变换
}
if(preIndex < 0){
preIndex = listSize -1;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取控件宽高
viewWidth = getMeasuredWidth();
viewHeight = getMeasuredHeight();
//获取切割后小块宽高
averageWidth = (int)(viewWidth/partNumber);
averageHeight = (int)(viewHeight/partNumber);
//处理在布局前添加的图片
if (viewWidth != 0 && viewHeight != 0) {
for (int i = 0; i < bitmapList.size(); i ++) {
//缩放处理bitmap
bitmapList.set(i, scaleBitmap(bitmapList.get(i)));
//切割操作
initBitmap(bitmapList.get(i));
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bitmapList.size() == 0) {
return;
}
switch (rollMode){
case Roll2D:
drawRoll2D(canvas);
break;
case Whole3D:
drawWhole3D(canvas);
break;
case SepartConbine:
drawSepartConbine(canvas);
break;
case Jalousie:
drawJalousie(canvas);
break;
case RollInTurn:
drawRollInTurn(canvas);
break;
}
}
/**
* 2D 滚动绘制
* @param canvas
*/
private void drawRoll2D(Canvas canvas) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(direction == 1){
//竖直方向滑动
//当前图片滑动
matrix.postTranslate(0, progress * viewHeight);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
matrix.reset();
//下一张图片滑动
matrix.postTranslate(0, -(1 - progress) * viewHeight);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
matrix.reset();
} else {
//水平方向滑动
//当前图片滑动
matrix.postTranslate(-progress * viewHeight, 0);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
matrix.reset();
//下一张图片滑动
matrix.postTranslate((1 - progress) * viewHeight, 0);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
matrix.reset();
}
canvas.restore();
}
/**
* 3D 整体滚动
* @param canvas
*/
private void drawWhole3D(Canvas canvas) {
canvas.save();
Bitmap currWholeBitmap = bitmapList.get(currIndex);
Bitmap nextWholeBitmap = bitmapList.get(nextIndex);
if(direction == 1){
//竖直方向滑动
//两张分别进行旋转,绕着同一个轴旋转
float axisY = progress * viewHeight;
//第一张图片旋转
camera.save();
camera.rotateX(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view绕着自己的top旋转,Y 方向不需要进行平移
matrix.preTranslate(-viewWidth/2, 0);
//转完之后显示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
//第二张图片旋转
camera.save();
camera.rotateX((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view绕着自己的buttom旋转,Y 向上平移 viewHeight
matrix.preTranslate(-viewWidth/2, -viewHeight);
//转完之后显示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
}else{
//水平方向滑动
float axisX = (1 - progress) * viewWidth;
camera.save();
camera.rotateY(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
canvas.drawBitmap(currWholeBitmap, matrix, paint);
camera.save();
camera.rotateY((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
canvas.drawBitmap(nextWholeBitmap, matrix, paint);
}
canvas.restore();
}
/**
* 尾部逐渐分离再合并
* @param canvas
*/
private void drawSepartConbine(Canvas canvas) {
//跟 3D 整体滚动对比,
//3D 整体滚动是一整张图片进行翻转
//这边是切割后的一张张小图片进行翻转
canvas.save();
Bitmap[] currWholeBitmaps = bitmapsList.get(currIndex);
Bitmap[] nextWholeBitmaps = bitmapsList.get(nextIndex);
if(direction == 1){
//竖直方向滑动
//两张分别进行旋转,绕着同一个轴旋转
float axisY = progress * viewHeight;
//第一张图片旋转
camera.save();
camera.rotateX(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view绕着自己的top旋转,Y 方向不需要进行平移
matrix.preTranslate(-viewWidth/2, 0);
//转完之后显示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
//每绘制一张小图片,需要往下平移,否则会覆盖住
matrix.postTranslate(averageWidth, 0);
}
//第二张图片旋转
camera.save();
camera.rotateX((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
//下面的view绕着自己的buttom旋转,Y 向上平移 viewHeight
matrix.preTranslate(-viewWidth/2, -viewHeight);
//转完之后显示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
matrix.postTranslate(averageWidth, 0);
}
}else{
//水平方向滑动
float axisX = (1 - progress) * viewWidth;
camera.save();
camera.rotateY(-progress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
matrix.postTranslate(0, averageHeight);
}
camera.save();
camera.rotateY((1 - progress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -viewHeight / 2);
matrix.postTranslate(axisX, viewHeight / 2);
for (int i = 0; i < partNumber; i ++) {
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
matrix.postTranslate(0, averageHeight);
}
}
canvas.restore();
}
/**
* 百叶窗
* @param canvas
*/
private void drawJalousie(Canvas canvas) {
canvas.save();
Bitmap[] currWholeBitmaps = bitmapsList.get(currIndex);
Bitmap[] nextWholeBitmaps = bitmapsList.get(nextIndex);
if(direction == 1){
//竖直方向翻转
if (progress < 0.5) {
//第一张图片旋转
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋转轴不一样
float axisY = (i + 0.5f) * averageHeight;
camera.save();
camera.rotateX(-progress * 180);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2, -0.5f * averageHeight);
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
}
} else {
//第二张图片旋转
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋转轴不一样
float axisY = (i + 0.5f) * averageHeight;
camera.save();
camera.rotateX((1 - progress) * 180);
camera.getMatrix(matrix);
camera.restore();
//下面的view绕着自己的top旋转,Y 方向不需要进行平移
matrix.preTranslate(-viewWidth/2, -0.5f * averageHeight);
//转完之后显示要往下平移 axisY
matrix.postTranslate(viewWidth/2, axisY);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}
}else{
//水平方向滑动
if (progress < 0.5) {
//第一张图片旋转
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋转轴不一样
float axisY = (i + 0.5f) * averageWidth;
camera.save();
camera.rotateY(-progress * 180);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-0.5f * averageWidth, -viewHeight/2);
matrix.postTranslate(axisY, viewHeight/2);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
}
} else {
//第二张图片旋转
for (int i = 0; i < partNumber; i ++) {
//每一小部分旋转轴不一样
float axisY = (i + 0.5f) * averageWidth;
camera.save();
camera.rotateY((1 - progress) * 180);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-0.5f * averageWidth, -viewHeight/2);
matrix.postTranslate(axisY, viewHeight/2);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}
}
canvas.restore();
}
/**
* 各模块依次滚动
* @param canvas
*/
private void drawRollInTurn(Canvas canvas) {
canvas.save();
Bitmap[] currWholeBitmaps = bitmapsList.get(currIndex);
Bitmap[] nextWholeBitmaps = bitmapsList.get(nextIndex);
if(direction == 1){
//竖直方向滑动
float partProgress;
float axisY;
for (int i = 0; i < partNumber; i ++) {
//计算当前小模块执行动画进度
partProgress = getPartProgress(i);
axisY = partProgress * viewHeight;
//第一张图片旋转
camera.save();
camera.rotateX(-partProgress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2, 0);
matrix.postTranslate(viewWidth/2 + averageWidth * i, axisY);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
//第二张图片旋转
camera.save();
camera.rotateX((1 - partProgress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth/2, -viewHeight);
matrix.postTranslate(viewWidth/2 + averageWidth * i, axisY);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}else{
float partProgress;
float axisX;
for (int i = 0; i < partNumber; i ++) {
//计算当前小模块执行动画进度
partProgress = getPartProgress(i);
axisX = partProgress * viewWidth;
//第一张图片旋转
camera.save();
camera.rotateY(-partProgress * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(0, -viewHeight/2);
matrix.postTranslate(axisX, viewHeight/2 + averageWidth * i);
canvas.drawBitmap(currWholeBitmaps[i], matrix, paint);
//第二张图片旋转
camera.save();
camera.rotateY((1 - partProgress) * 90);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-viewWidth, -viewHeight/2);
matrix.postTranslate(axisX, viewHeight/2 + averageWidth * i);
canvas.drawBitmap(nextWholeBitmaps[i], matrix, paint);
}
}
canvas.restore();
}
/**
* 计算当前进度下,各小模块的进度
* @param index
* @return
*/
private float getPartProgress (int index) {
//计算当前小模块在什么进度时候启动
float begin = index * partSpaceTime;
if (progress <= begin) {
//还没开始执行
return 0;
} else if (progress - begin >= partAnimatorPercent) {
//已经执行结束
return 1;
} else {
//正在执行
return (progress - begin) / partAnimatorPercent;
}
}
/**
* 根据切换进度进行重绘
* @param progress
*/
public void setProgress (float progress){
this.progress = progress;
invalidate();
}
//滚动模式
public enum RollMode {
Roll2D, //2D 滚动
Whole3D, //3D 整体滚动
SepartConbine, //尾部逐渐分离再合并
Jalousie, //百叶窗
RollInTurn; //各模块依次滚动
}
}
attrs.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="Camera3DView_Param">
<!--翻转模式-->
<attr name="camera_roll_mode"/>
<!--翻转方向-->
<attr name="camera_roll_direction"/>
<!--分成小模块的个数-->
<attr name="partNumber" format="integer"/>
<!--每个小模块启动的动画占总动画比-->
<attr name="part_animator_percent" format="float"/>
</declare-styleable>
<attr name="camera_roll_mode">
<enum name="Roll2D" value="0"/>
<enum name="Whole3D" value="1"/>
<enum name="SepartConbine" value="2"/>
<enum name="Jalousie" value="3"/>
<enum name="RollInTurn" value="4"/>
</attr>
<attr name="camera_roll_direction">
<enum name="vertical" value="1"/>
<enum name="horizontal" value="2"/>
</attr>
</resources>
这个
这边翻转效果的执行是通过设置每个翻转过程的百分比进行绘制的,如果需要实现类似点击翻转的效果,只要简单的与 Animator 结合即可。