无法在Android中设置自定义imageview的图像资源

无法在Android中设置自定义imageview的图像资源,android,android-imageview,android-canvas,android-custom-view,android-paint,Android,Android Imageview,Android Canvas,Android Custom View,Android Paint,我根据在和中学到的经验创建了一个自定义视图项目。 在我的项目中,我不仅尝试在视图上绘制,还尝试在自定义的ImageView上绘制。因此,我创建了一个定制的ImageView,并完成了上述官方代码实验室中的所有步骤。 这是我的自定义ImageView类: // Stroke width for the the paint. private const val STROKE_WIDTH = 12f class MyImageView @JvmOverloads constructor( c

我根据在和中学到的经验创建了一个自定义视图项目。 在我的项目中,我不仅尝试在视图上绘制,还尝试在自定义的
ImageView
上绘制。因此,我创建了一个定制的
ImageView
,并完成了上述官方代码实验室中的所有步骤。 这是我的自定义
ImageView
类:

// Stroke width for the the paint.
private const val STROKE_WIDTH = 12f

class MyImageView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {

    private var path = Path()
    private val drawColor = ResourcesCompat.getColor(resources, R.color.colorPaint, null)

    private lateinit var extraCanvas : Canvas
    private lateinit var extraBitmap : Bitmap

    private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop

    private var currentX = 0f
    private var currentY = 0f

    private var motionTouchEventX = 0f
    private var motionTouchEventY = 0f

    // Set up the paint with which to draw.
    private val paint = Paint().apply {
        color = drawColor
        // Smooths out edges of what is drawn without affecting shape.
        isAntiAlias = true
        // Dithering affects how colors with higher-precision than the device are down-sampled.
        isDither = true
        style = Paint.Style.STROKE // default: FILL
        strokeJoin = Paint.Join.ROUND // default: MITER
        strokeCap = Paint.Cap.ROUND // default: BUTT
        strokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)
    }

    init{
        init()
    }

    private fun init(){


        setOnTouchListener(OnTouchListener { _, event ->
            event?.let {
                motionTouchEventX = it.x
                motionTouchEventY = it.y

                when(it.action){
                    MotionEvent.ACTION_DOWN -> touchStart()
                    MotionEvent.ACTION_MOVE -> touchMove()
                    MotionEvent.ACTION_UP -> touchUp()
                }
                return@OnTouchListener true
            }
            false
        })
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.drawBitmap(extraBitmap, 0f, 0f, null)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        if (::extraBitmap.isInitialized) extraBitmap.recycle()
        extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        extraCanvas = Canvas(extraBitmap)
    }


    private fun touchStart() {
        path.reset()
        path.moveTo(motionTouchEventX, motionTouchEventY)
        currentX = motionTouchEventX
        currentY = motionTouchEventY
    }

    private fun touchMove() {
        val dx = Math.abs(motionTouchEventX - currentX)
        val dy = Math.abs(motionTouchEventY - currentY)
        if (dx >= touchTolerance || dy >= touchTolerance) {
            // QuadTo() adds a quadratic bezier from the last point,
            // approaching control point (x1,y1), and ending at (x2,y2).
            path.quadTo(currentX, currentY, (motionTouchEventX + currentX) / 2, (motionTouchEventY + currentY) / 2)
            currentX = motionTouchEventX
            currentY = motionTouchEventY
            // Draw the path in the extra bitmap to save it.
            extraCanvas.drawPath(path, paint)
        }
        // Invalidate() is inside the touchMove() under ACTION_MOVE because there are many other
        // types of motion events passed into this listener, and we don't want to invalidate the
        // view for those.
        invalidate()
    }

    private fun touchUp() {
        // Reset the path so it doesn't get drawn again.
        path.reset()
    }
}
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.myImageView.setImageResource(R.drawable.ic_launcher_foreground)
    }
}
这就是我的
MainActivity
的XML布局 (在codelab中没有人,他们只是以编程方式使用自定义视图):

我的问题是:没有显示我要绘制的图像资源。 绘图部分的工作方式与codelabs中的类似。但是我只想用于测试的
R.drawable.ic_launcher_前台
drawable没有显示在屏幕上。为什么?


我希望有人能帮忙。

在自定义视图中,画布仅用于绘制
extraBitmap
,它由
extraCanvas
更新,因此
MyImageView
仅处理
extraBitmap
的绘制

setImageResource
属于
ImageView
类,该类在内部处理资源到可绘制的转换,并在其
onDraw()内的画布上绘制
但是
onDraw
被自定义视图覆盖,该视图不处理所接收位图的绘制,因此解决方案是调用
super.onDraw
,如下所示:

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    // ^^^^^^^^^^^^^^^^ add this to execute imageview's onDraw for image handling
    canvas?.drawBitmap(extraBitmap, 0f, 0f, null)
}

或者,您可以
覆盖
设置ImageResource
并通过
设置ImageResource
方法将代码添加到
画布
我的ImageView

的onDraw
中,以绘制接收到的资源。您也可以使用
setBackgroundResource(…)
<

因此,您的主要活动如下所示:

private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.myImageView.setBackgroundResource(R.drawable.ic_launcher_foreground)
    }
}
使用
purple
draw color在绘制一点并使用
ic_launcher_前台
作为背景图像后,结果如下:

别忘了,正如@Pavneet_Singh所建议的:

对重写的onDraw方法调用super(如 super.onDraw(帆布)


谢谢:)它解决了我的问题。顺便说一句,这是一个很好的解释。谢谢你,我很高兴能帮上忙。快乐编码!
binding.myImageView.setBackgroundResource(R.drawable.ic_launcher_foreground)
private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.myImageView.setBackgroundResource(R.drawable.ic_launcher_foreground)
    }
}