Android仿BiliBili 图片3D切换效果

龙旋

共 5557字,需浏览 12分钟

 · 2021-12-22

 作者 |  小白彡
 地址 |  https://www.jianshu.com/p/aa6770d29376

最近刷B站看到一个比较有意思的图片切换效果,在查看一个用户发的图片的时候是平滑过渡,如果下一张图片是另一个用户发的,则会触发一个3D翻转的效果,不止是图片翻转,连带里面的布局也会一起翻转。


话不多说、先上效果图:




先分析一下这个效果由哪几部分组成:
1.左右平移。
2.上下翻转。


咦。。就这,就这

真就这么简单,先看第一个左右平移,这是一个很普通的ViewPage可以做到的效果,直接使用ViewPage就可以了,下面是使用ViewPage的代码:

        ViewPager vPage = findViewById(R.id.vpage);
List mFragments = new ArrayList<>(); mFragments.add(R.mipmap.one); mFragments.add(R.mipmap.two); mFragments.add(R.mipmap.three); mFragments.add(R.mipmap.four); mFragments.add(R.mipmap.five);
BliPageAdapter pageAdapter = new BliPageAdapter(this, mFragments); vPage.setAdapter(pageAdapter);


这样就可以实现,左右平移的效果了(ViewPage的具体使用参考别的文章或官方文档)


那第二个翻转该怎么实现呢,这才是最主要的,在效果图中可以明显看到3D的效果,那么肯定会用到Camera(非相机)来做, 那么3D效果是作用在哪个控件上呢?如果作用到ViewPage上,那么看到的画面就全部是倾斜的,如果作用到图片上,那么只有】图片倾斜,里面的内容不会倾斜,所以判断3D效果是作用在ViewPage的每一个子View上,这样的话,看到的画面是正的,但里面的内容可倾斜可不倾斜,有了这个依据,就可以开始实现了:


1.给ViewPage设置PageTransformer用来监听滑动的位置,因为3D效果会根据滑动的位置来决定倾斜的角度。

 vPage.setPageTransformer(false, new BliPageTransformer());


BliPageTransformer 实现了ViewPager.PageTransformer接口:

public class BliPageTransformer implements ViewPager.PageTransformer {
@Override public void transformPage(@NonNull View page, float position) {
}}


2.自定义自ViewPage子View,因为要在子View的dispatchDraw中对显示的内容做3D变换效果,我使用的是ConstraintLayout为最外层的容器,所以我自定义BliConstraintLayout继承自ConstraintLayout, 重    写dispatchDraw方法:

public class BliConstraintLayout extends ConstraintLayout
   @Override    protected void dispatchDraw(Canvas canvas) {        //camera保存状态        camera.save();        //camera设置绕Y轴旋转角度        camera.rotateY(rotateY);        //将变换应用到canvas上        camera.applyToCanvas(canvas);        camera.getMatrix(matrix);        if (!isLeftRotate){            //设置靠左进行旋转            matrix.preTranslate(- getWidth(), - getHeight() >> 1);            matrix.postTranslate(getWidth(), getHeight() >> 1);        } else {            //设置靠右进行旋转            matrix.preTranslate(0, - getHeight() >> 1);            matrix.postTranslate(0, getHeight() >> 1);        }        canvas.setMatrix(matrix);        //camera恢复状态        camera.restore();        super.dispatchDraw(canvas);    }


将ViewPage子View的xml文件外层容器替换成自定义的BliConstraintLayout

核心代码就在这里,Camera的用法可以参考其他博客或官方文档,


这里值得注意的是:


1.camera需要保存和恢复状态,不然下一次绘制会拿到上一次改变过的状态。


2.使用matrix对变换矩阵进行平移,因为默认变换都是针对坐标点(0,0)的,而上面的3D效果,需要的是x轴 靠左和靠右, y轴居中.


3.这些设置都要放在 super.dispatchDraw(canvas)之前,因为在super.dispatchDraw(canvas)调用后表示界面已经开始绘制的,所以要在绘制之前给它设置成我们需要变换的样子。


在后头看看BliPageTransformer中该怎么设置根据滑动位置设置3D倾斜:

    @Override    public void transformPage(@NonNull View page, float position) {        BliConstraintLayout bliConstraintLayout = (BliConstraintLayout) page;        /**         * 倾斜度         */        int tiltDegree = 34;        float v = position * tiltDegree;        bliConstraintLayout.setRotateY(v);        if (position > 0){            bliConstraintLayout.setIsLeftRotate(true);        }else{            bliConstraintLayout.setIsLeftRotate(false);        }    }


先看看方法里的参数
1.page : 当前子View
2.position:先简单了解一下position:position = -1 当前显示页的上一页。
position = 0  当前显示页。
position = 1 当前显示页的下一页。


那么在滑动到下一页的过程中position的变化就是:
0 -> -1   : 对应page为当前显示页
1 -> 0    :对应page为当前显示页的下一页
获取到对应View之后,再根据position计算对应值:
例如我设置的最大角度为34度, 那么在 1->0 的过程中,角度会由34 -> 0


再判断当前View是当前页,还是下一页,如果是当前页那么是相对布局的右边倾斜,如果是当前页的下一页那么应该相对布局的左边倾斜。


再将计算出的值设置到我们自定义的BliConstraintLayout 中,重绘画布。


下面就可以运行看看效果了:



完美复刻,一键三连。


下面贴一下关键点完整代码:

public class BliPageTransformer implements ViewPager.PageTransformer {
@Override public void transformPage(@NonNull View page, float position) { BliConstraintLayout bliConstraintLayout = (BliConstraintLayout) page; /** * 倾斜度 */ int tiltDegree = 34; float v = position * tiltDegree; bliConstraintLayout.setRotateY(v); if (position > 0){ bliConstraintLayout.setIsLeftRotate(true); }else{ bliConstraintLayout.setIsLeftRotate(false); } }}

public class BliConstraintLayout extends ConstraintLayout {
private float rotateY = 0;
private boolean isLeftRotate = true;
private Matrix matrix = new Matrix();
private Camera camera = new Camera();
public BliConstraintLayout(@NonNull Context context) { super(context); }
public BliConstraintLayout(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); }
public BliConstraintLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
@Override protected void dispatchDraw(Canvas canvas) { //camera保存状态 camera.save(); //camera设置绕Y轴旋转角度 camera.rotateY(rotateY); //将变换应用到canvas上 camera.applyToCanvas(canvas); camera.getMatrix(matrix); if (!isLeftRotate){ //设置靠左进行旋转 matrix.preTranslate(- getWidth(), - getHeight() >> 1); matrix.postTranslate(getWidth(), getHeight() >> 1); } else { //设置靠右进行旋转 matrix.preTranslate(0, - getHeight() >> 1); matrix.postTranslate(0, getHeight() >> 1); } canvas.setMatrix(matrix); //camera恢复状态 camera.restore(); super.dispatchDraw(canvas); }
public void setRotateY(float rotateY){ this.rotateY = rotateY; invalidate(); }
public void setIsLeftRotate(boolean isLeft){ this.isLeftRotate = isLeft; }}


想要源码的童鞋【龙旋】公众号后台对话框回复关键字:3D切换,即可获取下载链接。


到这里就完成啦。

浏览 77
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报