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

标签 android android-imageview android-canvas android-custom-view android-paint

我根据 this 中学到的经验教训创建了一个自定义 View 项目。和 this codelab 。 在我的项目中,我尝试不仅在 View 上绘制,还尝试在自定义 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()
    }
}

这就是我的 MainActivity 的 XML 布局的样子 (在 Codelab 中没有人,他们只是以编程方式使用自定义 View ):

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <com.celik.abdullah.drawingonimageview.MyImageView
            android:id="@+id/myImageView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

这是我的 MainActivity.kt 类:

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)
    }
}

我的问题是:没有显示我想要绘制的图像资源。 绘图部分的工作方式与 Codelab 中类似。但是我想仅用于测试目的的 R.drawable.ic_launcher_foreground 可绘制对象并未显示在屏幕上。为什么?

希望有人能帮忙。

最佳答案

在自定义 View 中, Canvas 仅用于绘制由 extraCanvas 更新的 extraBitmap,因此 MyImageView 仅处理extraBitmap 的绘制。

setImageResource 属于 ImageView 类,该类在内部处理资源到可绘制对象的转换,并将其绘制在其 onDraw() 内部的 Canvas 上但是 onDraw 被自定义 View 覆盖,该 View 不处理接收到的位图的绘制,因此解决方案是调用 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)
}

或者,您可以覆盖 setImageResource 并添加代码以通过 setImageResource 方法在 canvas 上绘制接收到的资源> 在 MyImageViewonDraw 内。

关于android - 无法在Android中设置自定义imageview的图像资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60760848/

相关文章:

java - 使用 OkHttp 进行时间戳请求

java - Android如何为ImageView的自定义部分设置透明度

java - ImageView 上的 TranslateAnimation 不起作用

Android ImageView动态宽高

Android View.onDraw() 总是有一个干净的 Canvas

java - Android:用 `MPAndroidChart`绘制水平条形图

java - 在回收者 View 中显示评级栏的平均评级

android - 获取 GPS 位置 消耗电池

java - 如何在触摸事件上绘制矩形

android - 以编程方式绘制气泡