0

I have a view(Calendar) that contains multiple Rects(Events) drawn on it, now I am trying to implement drag/drop as another layer on top of that view. Example- I long-press on an event, it passes me the exact coordinates of the Rect(Event), no I have created a custom view which will draw the same Rect(because I have coordinates)

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)

    }
}

Now the listener will pass me the coordinates when long-press on an event.

Current situation: I put the above view in the XML(On Top of the calendar View) and just made it visible when I get the coordinates, but don't know how to draw Rect on it because it is already initialized.

If I miss something to provide as information, please let me know in the comments I'll update the question

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Lakhwinder Singh
  • 6,799
  • 4
  • 25
  • 42
  • Do you want to change the date of the event via drag and drop? If your only consideration is to draw a rect to a specified position you can create a method to pass coordinates fo the rect then you can call invalidate on the view it redraws itself. – M.ekici Aug 21 '19 at 12:57
  • yes, I want to achieve that, I almost completed that, but with some issues – Lakhwinder Singh Aug 22 '19 at 10:33
  • I think that adding a view to the top of the calendar view. If your calendar view is a custom view then you can customize for that feature. – M.ekici Aug 22 '19 at 10:45
  • Yes that will be a good option, but I just want an isolated component which only receives the coordinates and draw event on it self – Lakhwinder Singh Aug 22 '19 at 10:46

2 Answers2

0

I assume that your calendar view and the view you will implement in the same size you can achieve it the xml file. To draw a rect each position change you can create a method in the your custom view class.

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

Then in you onDraw() callback:

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

In you initialisation phase you can create the mRect object as Rect.Then you apply changes on this rect then invalidates the view so the system calls onDraw again for your view. Additionally, you have to create a paint object for the drawing.

M.ekici
  • 765
  • 4
  • 10
  • The event creation, I already achieved that, but now my issue is when the long press of event is called from the calendar, I pass the coordinates to my custom view to draw that event on it, and it is working perfect – Lakhwinder Singh Aug 22 '19 at 10:56
  • Now come to issue, the long press event called from the calendar event, and my view draws the event, and pass the event to my view which is generated, but when I move my finger the onTuch of my Custom view is not gets called – Lakhwinder Singh Aug 22 '19 at 10:58
  • because the touch event generated from the calendar's event not mine – Lakhwinder Singh Aug 22 '19 at 10:59
  • As I understood, your calendar view consumes touch events. If it is true, you can call onRectTranslated to notify the custom view about the touchEvents. If what you want is to change consumer at the middle of the touch events, as far as I know, you cannot do that. Events have to continue on a view on which the touch started so you must transfer all events from a view to another. – M.ekici Aug 22 '19 at 11:09
  • i also tried that, but not worked, transfer ontouch event from calendar to my custom view – Lakhwinder Singh Aug 22 '19 at 11:18
0

I have created a custom view which will draw the event on the same coordinates and can move that event

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?)
    }
}
Lakhwinder Singh
  • 6,799
  • 4
  • 25
  • 42
  • I think wrapping both of the views in a viewGroup provides better encapsulation and so you can add other functionalities to them without affecting any code in unrelated classes like activities. – M.ekici Aug 23 '19 at 15:27