kotlin - 自定义圆形、文字在圆中心,拖动不能超出上下左右边界、添加点击事件

自定义圆形、文字在圆中心,拖动不能超出上下左右边界、添加点击事件

/**
 * Author : wn
 * Email : maoning20080809@163.com
 * Date : 2025/3/24 20:39
 * Description :  自定义圆形、文字在圆中心,拖动不能超出上下左右边界、添加点击事件
 */
class DraggableCircleViewActivity :AppCompatActivity() {

    private var activity : AppCompatActivity? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.custom_draggable_circle_view_main)

        activity = this

        val draggableCircleView = findViewById<DraggableCircleView>(R.id.customview5_draggable_circle_view)
        draggableCircleView.setCustomTextColor(Color.BLUE)

        draggableCircleView.setCustomViewOnClickListener {
            LogUtils.i("AAA", "Circle clicked custom Tag")
            Toast.makeText(activity , "Circle clicked custom", Toast.LENGTH_LONG).show()
        }

    }

}

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.text.TextPaint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import com.example.androidkotlindemo2.R

/**
 * Author : wn
 * Email : maoning20080809@163.com
 * Date : 2025/3/24 20:44
 * Description :
 */
class DraggableCircleView : View {

    constructor(context: Context) :this(context, null)
    constructor(context: Context, attributeSet: AttributeSet?) :this(context, attributeSet, 0)
    constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr :Int) : super(context, attributeSet, defStyleAttr){
        initBase(context, attributeSet)
    }

    //圆的半径
    private var circleRadius = 100f

    //圆的中心坐标
    private var circleX = 0f
    private var circleY = 0f

    //圆形画笔
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.BLUE
        style = Paint.Style.FILL
    }

    //文字画笔
    private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.WHITE
        textSize = 40f
        textAlign = Paint.Align.CENTER
    }

    //是否正在拖动
    private var isDragging = false

    //上次触摸事件的X坐标
    private var lastTouchX = 0f
    //上次触摸事件的Y坐标
    private var lastTouchY = 0f

    //判断是否为点击阈值(像素)
    private val clickThreshold = 10f

    //触摸开始时间(用于判断点击)
    private var touchStartTime = 0L

    //初始化触摸位置(用于判断是否移动)
    private var initialTouchX = 0f
    private var initialTouchY = 0f

    //点击事件
    private var onCustomViewClickListener : OnClickListener? = null

    //要显示的文字
    private var circleText = "点击、拖动"



    private fun initBase(context: Context, attributeSet: AttributeSet?) {
        context.theme.obtainStyledAttributes(attributeSet, R.styleable.DraggableCircleView, 0, 0)
            .apply {
                circleRadius = getDimension(R.styleable.DraggableCircleView_circleRadius, 100f)
                paint.color = getColor(R.styleable.DraggableCircleView_circleColor, Color.BLUE)
            }
    }

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

        //初始化圆的位置为view中心
        circleX = w / 2f
        circleY = h / 2f

    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        //绘制圆形
        canvas.drawCircle(circleX, circleY, circleRadius, paint)

        //计算文字基线位置,使文字垂直居中
        val textBaseY = circleY - (textPaint.descent() + textPaint.ascent()) / 2
        //绘制文字
        canvas.drawText(circleText, circleX, textBaseY, textPaint)

    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                //检查触摸点是否在圆内
                val touchX = event.x
                val touchY = event.y

                //计算触摸点与圆心的距离
                val distance = Math.sqrt(
                    Math.pow((touchX - circleX).toDouble(), 2.0) +
                    Math.pow((touchY - circleY).toDouble(), 2.0)
                )

                //如果接触点在圆内,则开始拖动
                if(distance <= circleRadius){
                    isDragging = true
                    lastTouchX = touchX
                    lastTouchY = touchY

                    touchStartTime = System.currentTimeMillis()
                    initialTouchX = touchX
                    initialTouchY = touchY
                    return true
                }
            }

            MotionEvent.ACTION_MOVE -> {
                if(isDragging){
                    val touchX = event.x
                    val touchY = event.y

                    //计算移动距离
                    val dx = touchX - lastTouchX
                    val dy = touchY - lastTouchY

                    //更新圆的坐标
                    var newX = circleX + dx
                    var newY = circleY + dy

                    //确保圆不会超出左边界
                    newX = newX.coerceAtLeast(circleRadius)

                    //确保圆不会超出右边界
                    newX = newX.coerceAtMost(width - circleRadius)

                    //确保圆不会超出上边距
                    newY = newY.coerceAtLeast(circleRadius)

                    //确保圆不会超出下边界
                    newY = newY.coerceAtMost(height - circleRadius)

                    //应用新的坐标
                    circleX = newX
                    circleY = newY

                    //更新最后触摸的位置
                    lastTouchX = touchX
                    lastTouchY = touchY

                    //重新绘制
                    invalidate()
                    return true
                }
            }

            MotionEvent.ACTION_UP -> {
                if(isDragging){
                    isDragging = false

                    val touchX = event.x
                    val touchY = event.y

                    //计算从按下到抬起的移动距离
                    val moveDistance = Math.sqrt(
                        Math.pow((touchX - initialTouchX).toDouble(), 2.0) +
                        Math.pow((touchY - initialTouchY).toDouble(), 2.0)
                    )

                    //如果移动距离小于阈值并且时间很短,认为是点击
                    if(moveDistance <= clickThreshold &&
                        System.currentTimeMillis() - touchStartTime < ViewConfiguration.getTapTimeout()){
                        performClick()
                    }
                }
            }

            MotionEvent.ACTION_CANCEL -> {
                isDragging = false
            }
        }
        return super.onTouchEvent(event)
    }



    override fun performClick(): Boolean {
        super.performClick()
        onCustomViewClickListener?.onClick(this)
        return true
    }

    fun setCustomViewOnClickListener(clickListener: OnClickListener) {
        onCustomViewClickListener = clickListener
    }

    //可以设置颜色、文字大小、文字内容
    fun setCustomTextColor(color : Int){
        textPaint.color = color
        invalidate()
    }

}

custom_draggable_circle_view_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/gradient_item_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">


    <com.example.androidkotlindemo2.customview5.DraggableCircleView
        android:id="@+id/customview5_draggable_circle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:circleRadius="50dp"
        app:circleColor="@color/red"
        />

</LinearLayout>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六毛六66

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值