Android自定义view重难点突破 范围裁切 三维变换 以及绘制顺序

裁切

裁切的本质就是不管你想绘制什么,最终canvas只会在你规定的区域里绘制你想要的东西,换句话说
裁切也相当于在你绘制好的自定义view中只显示出来你裁切的那一块,其余部分不展示

这里给出一个最简单的例子:

这个布局预览器看到的灰色边框就是自定义view的大小,明显的能看出来我们实际绘制的内容距离我们自定义view的距离。 这是查验裁切效果最好的方法。

代码就给出第三张图的代码:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);

        canvas.save();
        canvas.clipRect(20,20,260,260);
        canvas.drawBitmap(bitmap,0,0,mPaint);
        canvas.restore();

        canvas.save();
        canvas.clipRect(20,300,260,460);
        canvas.drawBitmap(bitmap,0,0,mPaint);
        canvas.restore();


    }
复制代码

二维变换

先看一段简单的translate的代码:

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

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
        canvas.translate(200,100);
        canvas.drawBitmap(bitmap,0,0,mPaint);

    }
复制代码

再看旋转

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

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
//        canvas.translate(200,100);
        canvas.save();
        //注意旋转的角度 是以顺时针为正,逆时针为负
        canvas.rotate(45,200,200);
        canvas.drawBitmap(bitmap,0,0,mPaint);
        canvas.restore();

        //绘制这个中心点只是让你明白 是以哪个点为中心 进行旋转,
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(10);
        canvas.drawPoint(200,200,mPaint);
    }
复制代码

我们当然可以以图片中心为轴点,旋转个90度 这样似乎更好理解

 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);
//        canvas.translate(200,100);
        canvas.save();
        //注意旋转的角度 是以顺时针为正,逆时针为负
        canvas.rotate(90,183,275);
        canvas.drawBitmap(bitmap,0,0,mPaint);
        canvas.restore();
复制代码

放大缩小

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

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);

        //等比例缩放 这里寻找中心点的方法 直接用bitmap实际的宽高来做,比之前的写死位置的更加直观
        canvas.save();
        canvas.scale(3.3f, 3.3f, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
        canvas.drawBitmap(bitmap, 0, 0, mPaint);
        canvas.restore();

    }
复制代码

还有个错切变化,比较简单,各位自研。

canvas变换的顺序

所有canvas的变化顺序 全是反着来的,这点要注意一下。

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

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.dly);

        int centerX=bitmap.getWidth()/2;
        int centerY=bitmap.getHeight()/2;


        // 目标效果  想 先移动 x轴 100个像素  再旋转90度  绘制

        //所以代码层面 必须 先rotate 90 再 translate
        canvas.save();
        canvas.rotate(90,183,275);
        canvas.translate(100,0);
        //注意旋转的角度 是以顺时针为正,逆时针为负
        canvas.drawBitmap(bitmap,0,0,mPaint);
        canvas.restore();

    }
复制代码

三维变换

要理解好三维变化,首先要理解好android的三维坐标系,注意这个和view的canvans的二维坐标系是不一样的

再看下 三维坐标系的旋转方向

此外,最重要的一点就是 android的三维坐标系所对应的类为camera,这个camera 可不是拍照的那个camera,引入的时候

要注意了,最后特别强调。

camera所有的rotate都是以view的原点为中心 也就是(0,0,0) 这个点。 且不支持设置旋转的轴心。

要想实现类似旋转轴心的效果,我们只能先把canvas挪到原点 然后进行 旋转,然后canvas 再挪回到我们想绘制的位置即可

注意canvas的混合变换是倒序的,这点千万不要忘记了

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Point point1 = new Point(200, 200);
        canvas.save();
        camera.save();
        camera.rotateX(30);
        camera.applyToCanvas(canvas);
        camera.restore();
        canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
        canvas.restore();
    }
复制代码

然而这样的效果 显然我们不满意,按照之前的说法 我们应该canvas的坐标 先translate到原点,然后camera映射 再然后 translate 到我们目标绘制点,效果就能好很多。

注意这个translate的过程如果使用matrix则可以控制顺序 如果用原生的canvas的话 只能倒序,不要忘记这点

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //这个是我们想要绘制bitmap的 起点
        Point point1 = new Point(200, 200);

        //计算出我们bitmap的宽高
        int bitmapWidth = bitmap.getWidth();
        int bitmapHeight = bitmap.getHeight();

        //计算出 我们bitmap 绘制结束以后的中心点
        int center1X = point1.x + bitmapWidth / 2;
        int center1Y = point1.y + bitmapHeight / 2;

        camera.save();
        matrix.reset();
        camera.rotateX(30);
        camera.getMatrix(matrix);
        camera.restore();
        //先translate到0,0这个点进行 rotateX
        matrix.preTranslate(-center1X, -center1Y);
        //rotateX 结束以后 再translate 到我们目标位置 进行绘制
        matrix.postTranslate(center1X, center1Y);
        canvas.save();
        canvas.concat(matrix);
        canvas.drawBitmap(bitmap, point1.x, point1.y, paint);
        canvas.restore();
    }
复制代码

这样一看效果好很多

所谓的翻页效果也不过就是在这个基础上进行的动画操作罢了。

绘制顺序

自定义view的绘制顺序很好理解,基本原则就是 后面绘制的会盖住前面绘制的

draw()方法是用来调度 绘制顺序的,主要绘制方法有

按绘制的先后顺序来:

先调用drawBackground方法,注意这个方法是私有的 我们无法重写这个方法噢。

然后 onDraw()方法,这个不用多说了。

再然后

dispatchDraw()方法,注意这个方法一般viewgroup才使用,纯正的view这个方法是几乎用不到的。

换句话说 对于viewgroup来说,总是先ondraw绘制完自己以后 再调用dispatchDraw()来绘制子view

所以有时候我们extends某些viewgroup的时候如果仅仅是在ondraw方法里面重写我们想要的效果,

结果往往看不到,因为ondraw方法走完以后 dispatchDraw() 绘制子view 把我们绘制的内容覆盖掉了

所以谨记viewgroup 自定义的时候 到底是在ondraw还是dispatchDraw 中重写 要考虑清楚了。

最后 在 ViewGroup 的子类中重写除 dispatchDraw() 以外的绘制方法时,可能需要调用 setWillNotDraw(false); 在重写的方法有多个选择时,优先选择 onDraw()。

还有个在 onDrawForeground()这个方法是最后调用的,用来绘制滑动条和前景的。这个用的不多大家可以参考下。 有些蒙版效果 要用这个方法实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值