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
相关产品推荐
相关产品推荐

