美文网首页Android进阶之旅UIandroid高级
Camera和Matrix实现真正的3D(WheelView)滚

Camera和Matrix实现真正的3D(WheelView)滚

作者: youxiaochen | 来源:发表于2017-09-27 20:12 被阅读1964次

    前言:

    通过Camera, Matrix 3d旋转+RecyclerView实现和(IOS时间地址选择3D)滚轮控件一样效果的WheelView,实现安卓QQ上滚轮一样的滑动效果

    更多文章请关注:http://www.jianshu.com/u/b1cff340957c


    一:先看效果图
    垂直方向的3D旋转 水平方向的3D旋转
    二:功能分析
    1:3D旋转效果

    WheelView的实现方式已经有很多种方式, 而且网上也有实现好的旋转效果,不过只是2D的旋转,而且要处理滑动与单击item事件比较复杂,真正的旋转是要通过Matrix, Camera类来实现,这里的Camera不是照相机里的API,Camera可以实现x,y,z轴的旋转,不清楚的可以去也解这些API的使用, 这里不详细介绍, 配合RecyclerView.ItemDecoration,在每个item中将Canvas进行3D旋转并平移,产生3D视觉效果

    这里拿垂直布局的一种状态来做示例

        /**
         * 画垂直布局时的item
         * @param c
         * @param rect
         * @param position
         * @param parentCenterX RecyclerView的中心X点
         * @param parentCenterY RecyclerView的中心Y点
         */
        void drawVerticalItem(Canvas c, Rect rect, int position, float parentCenterX, float parentCenterY) {
            int realPosition = position - itemCount;//数据中的实际位置
            float itemCenterY = rect.exactCenterY();
            float scrollOffY = itemCenterY - parentCenterY;
            float rotateDegreeX = scrollOffY * itemDegree / itemSize;//垂直布局时要以X轴为中心旋转
            int alpha = degreeAlpha(rotateDegreeX);
            if (alpha <= 0) return;
            float rotateSinX = (float) Math.sin(Math.toRadians(rotateDegreeX));
            float rotateOffY = scrollOffY - wheelRadio * rotateSinX;//因旋转导致界面视角的偏移
            //Log.i("you", "drawVerticalItem degree " + rotateDegreeX);
            //计算中心item, 优先最靠近中心区域的为中心点
            boolean isCenterItem = false;
            if (!hasCenterItem) {
                isCenterItem = Math.abs(scrollOffY) <= halfItemHeight;
                if (isCenterItem) {
                    centerItemPosition = realPosition;
                    hasCenterItem = true;
                }
            }
            //这里是旋转操作的核心,每个item在旋转成弧时,都要将item的中心在旋转后给人的视觉上的偏移计算好
            c.save();
            c.translate(0.0f, -rotateOffY);//因旋转导致界面视角的偏移
            camera.save();
    
            //旋转时离视角的z轴方向也会变化,先移动Z轴再旋转
            float z = (float) (wheelRadio * (1 - Math.abs(Math.cos(Math.toRadians(rotateDegreeX)))));
            camera.translate(0, 0, z);
    
    
            camera.rotateX(-rotateDegreeX);
            camera.getMatrix(matrix);
            camera.restore();
            matrix.preTranslate(-translateX, -itemCenterY);
            matrix.postTranslate(translateX, itemCenterY);
            c.concat(matrix);
            drawItem(c, rect, realPosition, alpha, isCenterItem, true);
            c.restore();
        }
    
    

    到这里基本已经实现了每个item距离中心点的旋转效果,接下来就是添加WheelView显示的数量在RecyclerView头与尾部的空的item

    2: 适配器定义

    滑动的时候,item要能滑动距中心点以上,也可以滑动到中心点以下,所以适配器中的item数量也要对应改变,直接上代码

    class WheelViewAdapter extends RecyclerView.Adapter<WheelViewHolder> {
    
        ...伪代码
    
        @Override
        public void onBindViewHolder(WheelViewHolder holder, int position) {
        //由于里面的文本全是画的,这里只是绑定最原始的View
        }
    
        @Override
        public int getItemCount() {
          //  这里的totalItemCount就是滑轮控件距离中心点显示的item个数 乘2
            return totalItemCount + (adapter == null ? 0 : adapter.getItemCount());
        }
    
        @Override
        public WheelViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            //适配器里根据垂直或水平布局显示
            View view = new View(parent.getContext());
            view.setLayoutParams(WheelUtils.createLayoutParams(orientation, itemSize));
            return new WheelViewHolder(view);
        }
    
    }
    
    
    最后附上源码 https://github.com/youxiaochen/WheelView-3d

    总结:

    WheelView具体使用方法,示例代码中都有详细介绍

    后面有空再加上item点击与左右偏移时的立体效果,还有封装日期选择等...


    更多文章请关注:http://www.jianshu.com/u/b1cff340957c

    相关文章

      网友评论

      • 80c36eb5a96d:蛋疼,我开始是自己算,又是算弧长又是算弧度角度的,你这个3d的方式好方便,可是我这边用不了RecyclerView,我是21的,不知道21有没有对应的RecyclerView包
      • f6e1379d5797:我使用你水平的那个布局,当我把数字内容换成城市内容后,发现不能居中了····
        f6e1379d5797:@游小陈 我好像知道了,当recyclerview的item总宽度大于屏幕宽度的时候,就不能居中了·····就是itemCount和itemSize 算出来的recyclerview子项总宽度大于屏幕宽度时候,就不能居中···········
        f6e1379d5797:@游小陈 我的是单行文字,文字内容显示的是11:30 12:30这种时间数据,但是不知道为什么,中间的那个文本不在屏幕宽度的居中位置。。。
        youxiaochen:水平的最好就只适合单行的文字,如果用比较复杂的,可以看改造的适用任意适配器, 就下一篇文章,有发布
      • 天门一只猿:这是一个好东西
      • youxiaochen:效果已经更新,现在已经完全仿IOS的滚轮效果了,代码也有更新
      • 5d60833103a0:项目效果很好,但是代码阅读起来。。。
        youxiaochen:谢谢,工作比较忙,今天之内我会再更新,带左右立体效果, 注释也会写全一点
      • 蒲导:这个厉害了,是目前看到的实现效果最好的
        youxiaochen:谢谢,今天之内我会再更新,带左右立体效果
      • 乒乓嘎:这个最像ios上的效果,谢谢分享
        youxiaochen:还在调整中,效果还不是最佳
      • HelloCsl:之前我也一直想用 RecycleView 来实现这个效果,搭配我自己写的一个 LayoutManager https://github.com/BCsl/GalleryLayoutManager,那时候总想在 getView 或者其他地方对整个 itemView 来处理,一直效果不太好。没想到还可以用 Decoration 来实现,不过这样好像也限制了只能绘制 Text
        youxiaochen:补充一点, 重写ViewGroup的drawChild方法,里面的item就会整个3D旋转,你试一下就知道喽,旋转的写法是一样的
        youxiaochen:没有限制的,可以重写drawChild方法,用一样的原理实现,只不过画别的东西,3D旋转后,不能合在一块,很难看,我都试过了,可以画任何东西

      本文标题:Camera和Matrix实现真正的3D(WheelView)滚

      本文链接:https://www.haomeiwen.com/subject/ytbyextx.html