Android 自定义View出现的绘制背景区域断开的情况

先把自定义View 运行出来的效果贴一下:(贴上两张图,注意箭头标记的地方)

然后发现有缺陷,会出现线条,大概还和innerWidth/innerHeight的奇偶数有关系,虽然上面代码中有除2: “innerWidth / 2” ,会存在小数,但是绘制方法(canvas.drawRect)里的参数都是Float,怎么还会有问题呢?

贴下相关代码:(自定义View的代码见:【CustomView】扫码中的扫描框(ViewfinderView)-简单实现

    // 视图不需要对其大小进行特殊控制,您只需替换一个方法,即 onSizeChanged()
    override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
        super.onSizeChanged(width, height, oldWidth, oldHeight)
        viewWidth = width.toFloat()
        viewHeight = height.toFloat()

        val xMid = (viewWidth / 2)
        val yMid = (viewHeight / 2)
        val leftOffset = (xMid - innerWidth / 2)
        val topOffset = (yMid - innerHeight / 2)
        viewfinderFrame.set(
            leftOffset,
            topOffset,
            leftOffset + innerWidth,
            topOffset + innerHeight
        )
    }

    // 在 onDraw() 方法内创建绘制对象会显著降低性能并使界面显得卡顿。
    public override fun onDraw(canvas: Canvas) {
        canvas.drawRect(0f, 
            0f, 
            viewWidth, 
            viewfinderFrame.top, 
            paint
        )
        canvas.drawRect(
            0f,
            viewfinderFrame.top,
            viewfinderFrame.left,
            viewfinderFrame.bottom,
            paint
        )
        canvas.drawRect(
            viewfinderFrame.right,
            viewfinderFrame.top,
            viewWidth,
            viewfinderFrame.bottom,
            paint
        )
        canvas.drawRect(
            0f,
            viewfinderFrame.bottom,
            viewWidth,
            viewHeight,
            paint
        )
        
        ... //省略了一些无关的代码
    }

然后去找了canvas.drawRect() 源码:(从Canvas >> BaseCanvas >> BaseCanvas_Delegate)

public class BaseCanvas_Delegate {

    @LayoutlibDelegate
    /*package*/ static void nDrawRect(long nativeCanvas,
            final float left, final float top, final float right, final float bottom, long paint) {

        draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                (graphics, paintDelegate) -> {
                    int style = paintDelegate.getStyle();

                    // draw
                    if (style == Paint.Style.FILL.nativeInt ||
                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
                        graphics.fillRect((int)left, (int)top,
                                (int)(right-left), (int)(bottom-top));
                    }

                    if (style == Paint.Style.STROKE.nativeInt ||
                            style == Paint.Style.FILL_AND_STROKE.nativeInt) {
                        graphics.drawRect((int)left, (int)top,
                                (int)(right-left), (int)(bottom-top));
                    }
                });
    }
}

然后在BaseCanvas_Delegate.java 文件中发现了猫腻:竟然直接强转Int,是够直接的。

顺带打印下log:

com.android.demo D/ViewfinderView: onSizeChanged - viewfinderFrame w: 683, h: 420, oldw: 0, oldh: 0, measuredWidth: 683 , measuredHeight: 420

com.android.demo D/ViewfinderView: onDraw - viewWidth: 683.0, viewHeight: 420.0 , viewfinderFrame top: 116.8125, left: 180.0625, right: 502.9375, bottom: 303.1875

根据log里的数值,我们替换到上面到代码再根据源码计算:

// onDraw - viewWidth: 683.0, viewHeight: 420.0 , viewfinderFrame top: 116.8125, left: 180.0625, right: 502.9375, bottom: 303.1875
canvas.drawRect(0f, 0f, 683.0, 116.8125, paint)  // graphics.fillRect(0, 0,  683, 116)

canvas.drawRect(0f, 116.8125, 180.0625, 303.1875, paint) // graphics.fillRect(0, 116, 180, 186)

canvas.drawRect(502.9375, 116.8125, 683.0, 303.1875, paint) // graphics.fillRect(502, 116, 180, 186)

canvas.drawRect(0f, 303.1875, 683.0, 420.0, paint) // graphics.fillRect(0, 303, 683, 98)

然后在根据四块 Rect 来拼接核实一下:(用ABCD来表示,绘制的View) 

可以看到:A>B是无缝连接,但是B>D不是,因为116+186 = 302 < 303 (这里丢失了1px,直接强转Int带来的“收益”啊!)

 

所以:这里需要提前去处理下类型强转问题,这里只需要在onSizeChanged()做处理即可,贴上优化代码:

    // 视图不需要对其大小进行特殊控制,您只需替换一个方法,即 onSizeChanged()
    override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
        super.onSizeChanged(width, height, oldWidth, oldHeight)
        Log.d(
            "ViewfinderView",
            "onSizeChanged - viewfinderFrame w: ${width}, h: ${height}, oldw: ${oldWidth}, oldh: ${oldHeight}, measuredWidth: $measuredWidth , measuredHeight: $measuredHeight"
        )
        viewWidth = width.toFloat()
        viewHeight = height.toFloat()

        val xMid = (viewWidth / 2).toInt()
        val yMid = (viewHeight / 2).toInt()
        val leftOffset = xMid - (innerWidth / 2).toInt()
        val topOffset = yMid - (innerHeight / 2).toInt()
        viewfinderFrame.set(
            leftOffset.toFloat(),
            topOffset.toFloat(),
            (leftOffset + innerWidth),
            (topOffset + innerHeight)
        )
    }

⚠️ 上面的处理,使原本奇数宽高的布局,虽然不至于会在中间丢失了1px,但是也会让居中的样式会整理偏移1px。

在绘制View背景块拼接时,需要注意下这点!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值