Android-Camera和Matrix实现真正的3D(WheelView)日期,地址选择滚轮控件

2017/10/12 posted in  Android

前言

Camera和Matrix实现真正的3D(WheelView)日期,地址选择滚轮控件

先看效果图

2017110515098153104205.gif
2017110515098153104205.gif

垂直方向的3D旋转

2017110515098153351203.gif
2017110515098153351203.gif

水平方向的3D旋转

功能分析

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();
        camera.rotateX(-rotateDegreeX);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-parentCenterX, -itemCenterY);
        matrix.postTranslate(parentCenterX, itemCenterY);
        c.concat(matrix);
        drawItem(c, rect, realPosition, alpha, isCenterItem, true);
        c.restore();
    }

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

适配器定义

滑动的时候,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);
    }
}

总结:

WheelView具体使用方法,示例代码中都有详细介绍,由于工作忙没有时间详细介绍里面的内容,源码里都有适当的注释,也可以一起讨论更佳的效果

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