(二十五)3D 翻转效果

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、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 结合即可。

五、附

代码链接:http://download.csdn.net/download/qq_18983205/10033613

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值