自定义UI 使用Camera做三维变换

Posted Notzuonotdied

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义UI 使用Camera做三维变换相关的知识,希望对你有一定的参考价值。

系列文章目录

  1. 自定义UI 基础知识
  2. 自定义UI 绘制饼图
  3. 自定义UI 圆形头像
  4. 自定义UI 自制表盘
  5. 自定义UI 简易图文混排
  6. 自定义UI 使用Camera做三维变换
  7. 自定义UI 属性动画
  8. 自定义UI 自定义布局

文章目录


前言

这系列的文章主要是基于扔物线的HenCoderPlus课程的源码来分析学习。



这一篇文章主要介绍的是Camera做三维变换,更多细节请见:HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助

创建绘制对象

我们需要创建一个画笔🖌Paint来绘制我们的头像的边框,也需要提前加载我们的头像到内存(Bitmap)中。

public class CameraView extends View 
    // 抗锯齿
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CameraView(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
    

加载图片

为避免出现java.lang.OutOfMemory异常,请先检查位图的尺寸,然后再对其进行解码,除非您绝对信任该来源可为您提供大小可预测的图片数据,以轻松适应可用的内存。——引用自Android官方文档:高效加载大型位图

下述源码的分析请见官方的说明:将按比例缩小的版本加载到内存中

public class CameraView extends View 
    // 图片大小
    private static final int IMAGE_WIDTH = 600;

    // 头像的Bitmap
    Bitmap bitmap;

    public CameraView(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
    

    
        bitmap = Utils.getAvatar(getResources(), IMAGE_WIDTH);
    


public class Utils 
    public static Bitmap getAvatar(Resources res, int width) 
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, R.drawable.avatar_rengwuxian, options);
        options.inJustDecodeBounds = false;
        options.inDensity = options.outWidth;
        options.inTargetDensity = width;
        return BitmapFactory.decodeResource(res, R.drawable.avatar_rengwuxian, options);
    

自定义绘制内容

先上一下我们使用Camera做三维变换的效果图。

左边是原图,右边是3D变换效果图

定义绘制的位置

坐标轴的单位都是px
public class CameraView extends View 
    // 图片大小
    private static final int IMAGE_WIDTH = 600;
    // 图片绘制的偏移量(left、top)
    private static final int BITMAP_OFFSET = 200;

    public CameraView(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
    

变换流程解析

摘录自:HenCoder Android 开发进阶:自定义 View 1-4 Canvas 对绘制的辅助

Canvas 的几何变换顺序是反的,所以 Canvas 的位置变换 translate 、图形旋转 rotate 等都是反着写的。

变换参数

public class CameraView extends View 
    // 抗锯齿
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 相机
    Camera camera = new Camera();

    // 图片大小
    private static final int IMAGE_WIDTH = 600;
    // 图片的大小的一半
    private static final int HALF_OF_IMAGE_WIDTH = IMAGE_WIDTH / 2;
    // 图片绘制的偏移量(left、top)
    private static final int BITMAP_OFFSET = 200;
    // Canvas移动到圆点的偏移量(dx、dy)
    private static final int OFFSET = HALF_OF_IMAGE_WIDTH + BITMAP_OFFSET;
    // Canvas旋转的角度
    private static final int ROTATE_ANGLE = 45;


先放上两部分单独的绘制效果:

左边是正常的图片,右边是经过Camera变换后的图形

左上部分变换

1. 将图片的中心移动到视图的圆点位置
public class CameraView extends View 

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        // 绘制上半部分
        canvas.save();
        // ... 省略 ...
		// 1. 将图片的中心移动到视图的圆点位置
        canvas.translate(-OFFSET, -OFFSET);
        canvas.drawBitmap(bitmap, BITMAP_OFFSET, BITMAP_OFFSET, paint);
        canvas.restore();
    

2. 图像以视图圆点为中心,旋转45°
3. 裁剪图像,保留x轴上方的灰色区域
public class CameraView extends View 

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        // 绘制上半部分
        canvas.save();
        // ... 省略 ...
        // 3. 裁剪图像,保留x轴上方的灰色区域
        canvas.clipRect(-IMAGE_WIDTH, -IMAGE_WIDTH, IMAGE_WIDTH, 0);
        // 2. 图像以视图圆点为中心,旋转45°
        canvas.rotate(ROTATE_ANGLE);
		// 1. 将图片的中心移动到视图的圆点位置
        canvas.translate(-OFFSET, -OFFSET);
        canvas.drawBitmap(bitmap, BITMAP_OFFSET, BITMAP_OFFSET, paint);
        canvas.restore();
    

这里简单解释下 canvas.clipRect 入参采用的是 IMAGE_WIDTH 的原因:作者懒 计算方便……╮(╯▽╰)╭

IMAGE_WIDTH W W W,那么可以得出以下几个坐标位置:

坐标点坐标位置对应点坐标位置
A( − 1 2 W 2 -\\sqrt\\frac12W^2 21W2 0 0 0)left( − W -W W 0 0 0)
B( 0 0 0 1 2 W 2 \\sqrt\\frac12W^2 21W2 )top( 0 0 0 W W W)
C( 1 2 W 2 \\sqrt\\frac12W^2 21W2 0 0 0)right( W W W 0 0 0)
D( 0 0 0 − 1 2 W 2 -\\sqrt\\frac12W^2 21W2 )bottom( 0 0 0 − W -W W)

由上,易得: W > 1 2 W 2 W > \\sqrt\\frac12W^2 W>21W2


4. 图像以视图圆点为中心,旋转-45°
5. 将图像中心从视图圆点恢复到原来位置
public class CameraView extends View 

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        // 绘制上半部分
        canvas.save();
        // 5. 将图像中心从视图圆点恢复到原来位置
        canvas.translate(OFFSET, OFFSET);
        // 4. 图像以视图圆点为中心,旋转-45°
        canvas.rotate(-ROTATE_ANGLE);
        // 3. 裁剪图像,保留x轴上方的灰色区域
        canvas.clipRect(-IMAGE_WIDTH, -IMAGE_WIDTH, IMAGE_WIDTH, 0);
        // 2. 图像以视图圆点为中心,旋转45°
        canvas.rotate(ROTATE_ANGLE);
		// 1. 将图片的中心移动到视图的圆点位置
        canvas.translate(-OFFSET, -OFFSET);
        canvas.drawBitmap(bitmap, BITMAP_OFFSET, BITMAP_OFFSET, paint);
        canvas.restore();
    

右下部分变换

右下部分基本和上一章节一致,这里仅仅介绍不一样的地方。

2. 图像以视图圆点为中心,旋转45°
3. 裁剪图像,保留x轴下方的灰色区域
public class CameraView extends View 

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        // 绘制上半部分
        canvas.save();
        // ... 省略 ...
        // 3. 裁剪图像,保留x轴上方的灰色区域
        canvas.clipRect(-IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_WIDTH);
        // 2. 图像以视图圆点为中心,旋转45°
        canvas.rotate(ROTATE_ANGLE);
		// 1. 将图片的中心移动到视图的圆点位置
        canvas.translate(-OFFSET, -OFFSET);
        canvas.drawBitmap(bitmap, BITMAP_OFFSET, BITMAP_OFFSET, paint);
        canvas.restore();
    

3D变换

这里有一些API的介绍放到后面章节再讲,可以先忽略。

public class CameraView extends View 
    // 相机
    Camera camera = new Camera();

    public CameraView(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
    

    
        bitmap = Utils.getAvatar(getResources(), IMAGE_WIDTH);
        // 沿着x轴旋转45°
        camera.rotateX(45);
        // 设置相机的z轴位置
        camera.setLocation(0, 0, Utils.getZForCamera()); // -8 = -8 * 72
    

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);

        // 绘制下半部分
        canvas.save();
        // 6. 将图像中心从视图圆点恢复到原来位置
        // canvas.translate(OFFSET, OFFSET);
		// 5. 图像以视图圆点为中心,旋转-45°
        // canvas.rotate(-ROTATE_ANGLE);
		// 4. 应用3D变换
        camera.applyToCanvas(canvas); // 关键代码
        // 3. 裁剪图像,保留x轴下方的灰色区域
        canvas.clipRect(-IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_WIDTH);
        // 2. 图像以视图圆点为中心,旋转45°
        canvas.rotate(ROTATE_ANGLE);
        // 1. 将图片的中心移动到视图的圆点位置
        canvas.translate(-OFFSET, -OFFSET);
        canvas.drawBitmap(bitmap, BITMAP_OFFSET, BITMAP_OFFSET, paint);
        canvas.restore();
    

3. 裁剪图像,保留x轴下方的灰色区域(第一张图)
4. 应用3D变换(第二张图)
5. 图像以视图圆点为中心,旋转-45°(第三张图)
最后放下合起来的效果(第四张图)

3D变换知识点

这里贴下上面章节缺失的一个方法的定义。

public class CameraView extends View 
    // 相机
    Camera camera = new Camera();

    public CameraVie

以上是关于自定义UI 使用Camera做三维变换的主要内容,如果未能解决你的问题,请参考以下文章

自定义UI 使用Camera做三维变换

自定义UI 自制表盘

自定义UI 自制表盘

自定义UI 属性动画

自定义UI 属性动画

自定义UI 属性动画