You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin:如何让Canvas绘制的Bitmap可点击并跳转新Activity

问题描述

我有一个可缩放的ImageView,当前已在该视图上添加了2个作为定位标记的Bitmap视图,希望用户点击这些Bitmap时能打开新的Activity,但无法编写正确的代码实现每个Bitmap的点击功能,请问该如何达成需求?

PinView代码

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.widget.Toast
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.davemorrissey.labs.subscaleview.sample.R.drawable


class PinView @JvmOverloads constructor(context: Context, attr: AttributeSet? = null) : SubsamplingScaleImageView(context, attr) {

    private val paint = Paint()
    private val vPin = PointF()
    private var sPin: PointF? = null

    private val paint2 = Paint()
    private val vPin2 = PointF()
    private var sPin2: PointF? = null

    private var pin: Bitmap
    private var pin2: Bitmap

    init {
        val density = resources.displayMetrics.densityDpi.toFloat()
        val bitmap = BitmapFactory.decodeResource(this.resources, drawable.pushpin_green)
        val w = density / 420f * bitmap.width
        val h = density / 420f * bitmap.height
        pin = Bitmap.createScaledBitmap(bitmap, w.toInt(), h.toInt(), true)
        pin2 = Bitmap.createScaledBitmap(bitmap, w.toInt(), h.toInt(), true)
    }

    fun setPin(sPin: PointF) {
        this.sPin = sPin
        invalidate()
    }

    fun setPin2(sPin2: PointF) {
        this.sPin2 = sPin2
        invalidate()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (sPin != null) {
            sourceToViewCoord(sPin, vPin)
            val vX = vPin.x - pin.width / 2
            val vY = vPin.y - pin.height
            canvas.drawBitmap(pin, vX, vY, paint)
        }
        if (sPin2 != null) {
            sourceToViewCoord(sPin2, vPin2)
            val vX = vPin2.x - pin2.width / 2
            val vY = vPin2.y - pin2.height
            canvas.drawBitmap(pin2, vX, vY, paint2)
        }
    }
}

ExtensionPinFragment代码

import android.app.Fragment
import android.graphics.PointF
import android.opengl.ETC1.getHeight
import android.opengl.ETC1.getWidth
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.sample.R.layout
import kotlinx.android.synthetic.main.extension_pin_fragment.*


class ExtensionPinFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater!!.inflate(layout.extension_pin_fragment, container, false)

    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        imageView.setImage(ImageSource.asset("swissroad.jpg"))
        imageView.setPin(PointF(565f, 1065f))
        imageView.setPin2(PointF(165f, 1465f))
    }
}

MainActivity代码

import android.os.Bundle
import kotlinx.android.synthetic.main.main.*
import android.support.v7.app.AppCompatActivity
import com.davemorrissey.labs.subscaleview.sample.extension.ExtensionPinFragment

class MainActivity : AppCompatActivity() {

    private var fragmentId: Int = R.id.extension

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        setFragment(fragmentId)

    }

    private fun setFragment(id: Int) {
        fragmentId = id
        val fragmentClass = when(id) {
            R.id.extension -> ExtensionPinFragment::class.java
            else -> ExtensionPinFragment::class.java
        }
        val existingFragment = fragmentManager.findFragmentByTag("ROOT")
        if (existingFragment?.javaClass != fragmentClass) {
            fragmentManager.beginTransaction()
                    .replace(R.id.content, fragmentClass.newInstance(), "ROOT")
                    .commit()
        }
    }
}
解决方案

要给两个定位标记添加点击跳转功能,需要在PinView中处理触摸事件,判断点击区域是否落在Bitmap范围内,再通过回调通知Fragment启动对应Activity,具体步骤如下:

1. 在PinView中添加点击回调接口

在PinView类中定义回调接口,用于向外部传递点击事件:

// 定义点击回调接口
interface OnPinClickListener {
    fun onPin1Clicked()
    fun onPin2Clicked()
}

private var onPinClickListener: OnPinClickListener? = null

// 设置回调的方法
fun setOnPinClickListener(listener: OnPinClickListener) {
    this.onPinClickListener = listener
}

2. 重写onTouchEvent判断点击区域

在PinView中重写触摸事件方法,将触摸坐标转换为视图坐标后,判断是否点击在两个Pin的范围内:

override fun onTouchEvent(event: MotionEvent): Boolean {
    if (event.action == MotionEvent.ACTION_UP) {
        val touchPoint = PointF(event.x, event.y)
        
        // 检查第一个Pin的点击区域
        sPin?.let {
            sourceToViewCoord(it, vPin)
            val pinLeft = vPin.x - pin.width / 2
            val pinTop = vPin.y - pin.height
            val pinRight = pinLeft + pin.width
            val pinBottom = pinTop + pin.height
            
            if (touchPoint.x >= pinLeft && touchPoint.x <= pinRight && touchPoint.y >= pinTop && touchPoint.y <= pinBottom) {
                onPinClickListener?.onPin1Clicked()
                return true
            }
        }
        
        // 检查第二个Pin的点击区域
        sPin2?.let {
            sourceToViewCoord(it, vPin2)
            val pin2Left = vPin2.x - pin2.width / 2
            val pin2Top = vPin2.y - pin2.height
            val pin2Right = pin2Left + pin2.width
            val pin2Bottom = pin2Top + pin2.height
            
            if (touchPoint.x >= pin2Left && touchPoint.x <= pin2Right && touchPoint.y >= pin2Top && touchPoint.y <= pin2Bottom) {
                onPinClickListener?.onPin2Clicked()
                return true
            }
        }
    }
    return super.onTouchEvent(event)
}

3. 在Fragment中设置回调并启动Activity

修改ExtensionPinFragment,给imageView设置回调,在回调中启动对应的Activity(注意替换为你实际的Activity类名):

import android.content.Intent
// ...其他导入保持不变

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    imageView.setImage(ImageSource.asset("swissroad.jpg"))
    imageView.setPin(PointF(565f, 1065f))
    imageView.setPin2(PointF(165f, 1465f))
    
    // 设置Pin点击回调
    imageView.setOnPinClickListener(object : PinView.OnPinClickListener {
        override fun onPin1Clicked() {
            // 打开第一个Pin对应的Activity
            startActivity(Intent(context, FirstTargetActivity::class.java))
        }

        override fun onPin2Clicked() {
            // 打开第二个Pin对应的Activity
            startActivity(Intent(context, SecondTargetActivity::class.java))
        }
    })
}

4. 完整PinView代码

整合后的PinView代码如下:

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.davemorrissey.labs.subscaleview.sample.R.drawable

class PinView @JvmOverloads constructor(context: Context, attr: AttributeSet? = null) : SubsamplingScaleImageView(context, attr) {

    private val paint = Paint()
    private val vPin = PointF()
    private var sPin: PointF? = null

    private val paint2 = Paint()
    private val vPin2 = PointF()
    private var sPin2: PointF? = null

    private var pin: Bitmap
    private var pin2: Bitmap

    // 点击回调接口
    interface OnPinClickListener {
        fun onPin1Clicked()
        fun onPin2Clicked()
    }

    private var onPinClickListener: OnPinClickListener? = null

    fun setOnPinClickListener(listener: OnPinClickListener) {
        this.onPinClickListener = listener
    }

    init {
        val density = resources.displayMetrics.densityDpi.toFloat()
        val bitmap = BitmapFactory.decodeResource(this.resources, drawable.pushpin_green)
        val w = density / 420f * bitmap.width
        val h = density / 420f * bitmap.height
        pin = Bitmap.createScaledBitmap(bitmap, w.toInt(), h.toInt(), true)
        pin2 = Bitmap.createScaledBitmap(bitmap, w.toInt(), h.toInt(), true)
    }

    fun setPin(sPin: PointF) {
        this.sPin = sPin
        invalidate()
    }

    fun setPin2(sPin2: PointF) {
        this.sPin2 = sPin2
        invalidate()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (sPin != null) {
            sourceToViewCoord(sPin, vPin)
            val vX = vPin.x - pin.width / 2
            val vY = vPin.y - pin.height
            canvas.drawBitmap(pin, vX, vY, paint)
        }
        if (sPin2 != null) {
            sourceToViewCoord(sPin2, vPin2)
            val vX = vPin2.x - pin2.width / 2
            val vY = vPin2.y - pin2.height
            canvas.drawBitmap(pin2, vX, vY, paint2)
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.action == MotionEvent.ACTION_UP) {
            val touchPoint = PointF(event.x, event.y)
            
            // 检查第一个Pin的点击区域
            sPin?.let {
                sourceToViewCoord(it, vPin)
                val pinLeft = vPin.x - pin.width / 2
                val pinTop = vPin.y - pin.height
                val pinRight = pinLeft + pin.width
                val pinBottom = pinTop + pin.height
                
                if (touchPoint.x >= pinLeft && touchPoint.x <= pinRight && touchPoint.y >= pinTop && touchPoint.y <= pinBottom) {
                    onPinClickListener?.onPin1Clicked()
                    return true
                }
            }
            
            // 检查第二个Pin的点击区域
            sPin2?.let {
                sourceToViewCoord(it, vPin2)
                val pin2Left = vPin2.x - pin2.width / 2
                val pin2Top = vPin2.y - pin2.height
                val pin2Right = pin2Left + pin2.width
                val pin2Bottom = pin2Top + pin2.height
                
                if (touchPoint.x >= pin2Left && touchPoint.x <= pin2Right && touchPoint.y >= pin2Top && touchPoint.y <= pin2Bottom) {
                    onPinClickListener?.onPin2Clicked()
                    return true
                }
            }
        }
        return super.onTouchEvent(event)
    }
}

内容的提问来源于stack exchange,提问作者GamerBlind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:40:29