Android--利用camera打造3D效果

2020-07-02 15:30:59 浏览数 (2)

效果如下:

Camera3DView.gif

思路是利用camera对两张图片分别做旋转处理,代码如下
代码语言:javascript复制
/**
 * 使用camera实现3d效果的自定义控件
 */
public class Camera3DView extends View {
    //存放bitmap资源文件id的集合
    private List<Integer> bitmapResourceIds;
    //用于3d变换
    private Camera camera;
    //用于变换的矩阵
    private Matrix matrix;
    //view的宽高
    private float viewWidth, viewHeight;
    //是否绘制完毕
    private boolean isDrawFinished = false;
    public static final int VERTICAL = LinearLayout.VERTICAL;
    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
    //旋转方向
    private int orientation = VERTICAL;
    //旋转轴,所有旋转操作无外乎两个玩意,一个是旋转轴,一个是旋转角度
    //VERTICAL时使用rotateY,HORIZONTAL时使用rotateX
    private float rotatePivotX, rotatePivotY;
    //旋转角度
    private float degress;
    //最大旋转角度
    private float maxDegress = 90;
    //当前图片索引
    private int currentIndex;
    //下个显示图片索引
    private int nextIndex;
    //上个显示图片索引
    private int preIndex;
    private Paint mPaint;
    //是否前进
    private boolean isForward = true;
    private ValueAnimator valueAnimator;
    private boolean isAnimatiorRunning;

    public Camera3DView(Context context) {
        this(context, null);
    }

    public Camera3DView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Camera3DView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        bitmapResourceIds = new ArrayList<>();
        camera = new Camera();
        matrix = new Matrix();
        mPaint = new Paint();

        isDrawFinished = false;
    }

    /**
     * 添加图片
     *
     * @param resId
     */
    public void addResId(int resId) {
        bitmapResourceIds.add(resId);
        if (isDrawFinished) {
            invalidate();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
        viewHeight = h;
        isDrawFinished = true;
        initBitmaps();
        resetIndex();
    }

    private void initBitmaps() {
        for (int i = 0; i < bitmapResourceIds.size(); i  ) {
            int resID = bitmapResourceIds.get(i);
            getBitmapScale(resID, viewWidth, viewHeight);
        }
    }

    /**
     * 旋转一面后,调用重置索引
     */
    public void resetIndex() {
        int listSize = bitmapResourceIds.size();
        if (isForward) {//前进或者向下
            currentIndex  ;
            if (currentIndex > listSize - 1) {
                currentIndex = 0;
            }
        } else {
            currentIndex--;
            if (currentIndex < 0)
                currentIndex = listSize - 1;
        }

        nextIndex = currentIndex   1;
        preIndex = currentIndex - 1;
        if (nextIndex > listSize - 1)
            nextIndex = 0;//循环切换
        if (preIndex < 0)
            preIndex = listSize - 1;

        this.degress = 0;

        rotatePivotX = 0;
        rotatePivotY = 0;

        isForward = true;

        invalidate();
    }

    public void setDegress(int degress) {
        this.degress = degress;

        //VERTICAL时使用rotateY,HORIZONTAL时使用rotateX
        if (orientation == HORIZONTAL) {
            //x方向旋转轴随degress的变大不断下移
            rotatePivotX = degress / maxDegress * viewWidth;
        } else {
            //y方向旋转轴随degress的变大不断右移
            rotatePivotY = degress / maxDegress * viewHeight;
        }

        //刷新
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //VERTICAL时使用rotateY,HORIZONTAL时使用rotateX
        if (orientation == VERTICAL) {
            //如果是前进,则画当前图,后退则画上一张图,注释用的是前进情况
            matrix.reset();
            camera.save();
            //旋转角度 0 - -maxDegress 
            camera.rotateX(-degress);
            camera.getMatrix(matrix);
            camera.restore();

            //绕着图片top旋转
            matrix.preTranslate(-viewWidth / 2f, 0);
            //旋转轴向下平移,则图片也向下平移
            matrix.postTranslate(viewWidth / 2f, rotatePivotY);
            //如果是前进,则画当前图,后退则画上一张图,因为后退时,这里画的是动画下方出来的图片,而下方的图片是前一张图
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
                    matrix, mPaint);

            //在处理下一张图片
            matrix.reset();
            camera.save();
            //旋转角度 maxDegress - 0
            camera.rotateX(maxDegress - degress);
            camera.getMatrix(matrix);
            camera.restore();

            //绕着图片bottom旋转
            matrix.preTranslate(-viewWidth / 2f, -viewHeight);
            //旋转轴向下平移,则图片也向下平移
            matrix.postTranslate(viewWidth / 2f, rotatePivotY);
            //如果是前进,则画下一张图,后退则画当前图,后退时,这边代码画的是动画上方的图片,上方的图片是当前图片
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
                    matrix, mPaint);
        } else {
            //如果是前进,则画当前图,后退则画上一张图,注释用的是前进情况
            matrix.reset();
            camera.save();
            //旋转角度 0 - maxDegress 
            camera.rotateY(degress);
            camera.getMatrix(matrix);
            camera.restore();

            //绕着图片left旋转
            matrix.preTranslate(0, -viewHeight / 2);
            //旋转轴向右平移,则图片也向右平移
            matrix.postTranslate(rotatePivotX, viewHeight / 2);
            //如果是前进,则画当前图,后退则画上一张图,因为后退时,这里画的是动画右方出来的图片,而右方的图片是前一张图
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
                    matrix, mPaint);

            //在处理下一张图片
            matrix.reset();
            camera.save();
            //旋转角度 -maxDegress - 0
            camera.rotateY(-maxDegress   degress);
            camera.getMatrix(matrix);
            camera.restore();

            //绕着图片right旋转
            matrix.preTranslate(-viewWidth, -viewHeight / 2f);
            //旋转轴向右平移,则图片也向右平移
            matrix.postTranslate(rotatePivotX, viewHeight / 2f);
            //如果是前进,则画下一张图,后退则画当前图,后退时,这边代码画的是动画左方的图片,左方的图片是当前图片
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
                    matrix, mPaint);
        }

    }

    /**
     * 获取缩放图片
     *
     * @param resId
     * @param width
     * @param height
     * @return
     */
    private Bitmap getBitmapScale(int resId, float width, float height) {
        if (ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId)) != null) {
            return ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId));
        }
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);

        Bitmap bitmapDst = Bitmap.createScaledBitmap(bitmap, (int) width, (int) height, false);
        bitmap.recycle();

        ImageCache.getInstance().addBitmapToMemoryCache(String.valueOf(resId)
                , bitmapDst);
        return bitmapDst;
    }

    public void next() {
        createAnimator();

        if (!isAnimatiorRunning) {
            isForward = true;
            isAnimatiorRunning = true;
            valueAnimator.start();
        }
    }

    public void pre() {
        createAnimator();

        if (!isAnimatiorRunning) {
            isForward = false;
            isAnimatiorRunning = true;
            valueAnimator.reverse();
        }
    }

    private void createAnimator() {
        if (valueAnimator == null) {
            valueAnimator = ValueAnimator.ofFloat(0, 1f);
            valueAnimator.setDuration(500);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    degress = maxDegress * animation.getAnimatedFraction();
                    setDegress((int) degress);
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    resetIndex();
                    isAnimatiorRunning = false;
                }
            });
        }
    }

    public void setOrientation(int orientation) {
        this.orientation = orientation;
    }
}
项目地址:https://gitee.com/aruba/CameraApplication.git

0 人点赞