【问题标题】:Draw RectF on canvas在画布上绘制 RectF
【发布时间】:2019-08-23 17:37:45
【问题描述】:

我有一个视图(日历),其中包含多个绘制的矩形(事件),现在我正在尝试将拖放作为该视图顶部的另一层来实现。示例-我长按一个事件,它传递给我 Rect(Event) 的确切坐标,不,我创建了一个自定义视图,它将绘制相同的 Rect(因为我有坐标)

class DraggerView: View {


    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)


    override fun isInEditMode(): Boolean {
        return true
    }

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

    }
}

现在,当长按事件时,侦听器会将坐标传递给我。

现状:我把上面的视图放在 XML 中(在日历视图的顶部),当我得到坐标时让它可见,但不知道如何在上面绘制 Rect因为它已经被初始化了。

如果我错过了作为信息提供的信息,请在 cmets 中告诉我,我会更新问题

【问题讨论】:

  • 您想通过拖放更改活动日期吗?如果您唯一的考虑是将矩形绘制到指定位置,您可以创建一个方法来传递矩形的坐标,然后您可以在它自己重绘的视图上调用 invalidate。
  • 是的,我想实现,我几乎完成了,但有一些问题
  • 我认为在日历视图的顶部添加一个视图。如果您的日历视图是自定义视图,那么您可以针对该功能进行自定义。
  • 是的,这将是一个不错的选择,但我只想要一个孤立的组件,它只接收坐标并在其上绘制事件

标签: android android-canvas android-custom-view android-rect


【解决方案1】:

我假设您的日历视图和您将实现的视图大小相同,您可以通过 xml 文件实现它。要在每个位置变化时绘制一个矩形,您可以在自定义视图类中创建一个方法。

fun onRectTranslated(dx:Int, y:Int){
    mRect.offset(dx,dy)
    postInvalidateOnAnimation()
}

然后在你onDraw()回调:

 override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    canvas?.drawRect(mRect,mRectPaint)
}

在初始化阶段,您可以将 mRect 对象创建为 Rect。然后在此矩形上应用更改,然后使视图无效,以便系统再次为您的视图调用 onDraw。此外,您必须为绘图创建一个绘画对象。

【讨论】:

  • 事件创建,我已经实现了,但现在我的问题是当从日历中调用事件的长按时,我将坐标传递给我的自定义视图以在其上绘制该事件,并且它运行良好
  • 现在来发出,从日历事件中调用的长按事件,我的视图绘制事件,并将事件传递给生成的我的视图,但是当我移动我的手指时我的自定义视图没有被调用
  • 因为日历事件生成的触摸事件不是我的
  • 据我了解,您的日历视图会消耗触摸事件。如果是真的,您可以调用 onRectTranslated 来通知自定义视图有关 touchEvents。如果你想要的是在触摸事件的中间改变消费者,据我所知,你不能这样做。事件必须在触摸开始的视图上继续,因此您必须将所有事件从一个视图转移到另一个视图。
  • 我也尝试过,但没有成功,将 ontouch 事件从日历转移到我的自定义视图
【解决方案2】:

我创建了一个自定义视图,它将在相同坐标上绘制事件并可以移动该事件

class DraggerView : View {


    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    private var availableWidth: Int = 0
    private var availableHeight: Int = 0
    private var title: String = ""
    private var rect: RectF? = null
    private val paintEvent = Paint()
    private val paintText: TextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG or Paint.LINEAR_TEXT_FLAG).apply {
        isAntiAlias=true
        style = Paint.Style.FILL
        color = Color.WHITE
        textSize = 30f
        typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
    }
    private var data: Event? = null
    private var draw = false
    var eventListener: EventListener? = null

    override fun isInEditMode(): Boolean {
        return true
    }

    @SuppressLint("NewApi")
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (draw) {
            canvas.save()
            canvas.drawColor(ContextCompat.getColor(context, R.color.shadow))
            rect?.let { rectF ->
                canvas.drawRoundRect(rectF, 10f, 10f, paintEvent)
                val x = rectF.left + 10
                val y = rectF.top + 10

                val layout = StaticLayout.Builder.obtain(title, 0, title.length, paintText, availableWidth)
                        .setAlignment(Layout.Alignment.ALIGN_NORMAL)
                        .setLineSpacing(0.0f, 1.0f)
                        .setIncludePad(false)
                        .build()
                canvas.translate(x, y)
                layout.draw(canvas)

            }
            canvas.restore()
        }

    }

    fun drawEvent(rectF: RectF, data: Event) {
        draw = true
        rect = rectF
        this.data = data
        title = data.title
        availableWidth = rectF.right.minus(rectF.left).toInt()
        availableHeight = rectF.bottom.minus(rectF.top).toInt()
        paintEvent.color = data.color
        invalidate()
    }


    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        val xMove = event?.x ?: 0f
        val yMove = event?.y ?: 0f
        when (event?.action) {
            MotionEvent.ACTION_UP -> {
                draw = false
                eventListener?.onEventDrop(rect, data)
            }
            MotionEvent.ACTION_MOVE -> {
                val newLeft = xMove - (availableWidth / 2)
                val newTop = yMove - (availableHeight / 2)
                val newRight = xMove + (availableWidth / 2)
                val newBottom = yMove + (availableHeight / 2)

                rect?.let {
                    it.left = newLeft
                    it.top = newTop
                    it.right = newRight
                    it.bottom = newBottom
                }
                // we might needed to scroll weekview when event
                // dragged to right side of the screen
                if (xMove > (width * 0.90)) {
                    eventListener?.onEventScrollRight(rect, data)
                }
                if (xMove < (width * 0.10)) {
                    eventListener?.onEventScrollLeft(rect, data)
                }
            }
        }
        invalidate()
        return draw
    }

    interface EventListener {
        fun onEventDrop(rectF: RectF?, data: Event?)
        fun onEventScrollRight(rectF: RectF?, data: Event?)
        fun onEventScrollLeft(rectF: RectF?, data: Event?)
    }
}

【讨论】:

  • 我认为将两个视图包装在一个 viewGroup 中可以提供更好的封装,因此您可以向它们添加其他功能,而不会影响活动等不相关类中的任何代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
  • 2020-05-25
  • 2013-01-07
  • 2018-01-14
相关资源
最近更新 更多