自定义圆形、文字在圆中心,拖动不能超出上下左右边界、添加点击事件
/** * 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>