5

To understand it better read this :

First game :

Skate QuestionMark
Archery QuestionMark
Swim QuestionMark

---------------------------
Water Bow Wheel

If user drags Water to Skate or Archery QuestionMark it will animate to the list (because it is not correct)

If user drags twice incorrect (it will mark the one that is correct from the answer list)

If user still fail in the third try it will drag to the incorrect one and then it will highlight or just change the color doesn't matter to red.

If user drags to the correct one it will highlight green and replace QuestionMark with the correct one (I do not want to be draggable anymore that image)

------

Game 2 (is more or less the same...)

There's no QuestionMark column, there's only :

Skate
Swim
Archery
--------------
(Lot of answers)

Now the way to play is the same (about the fails and etc) the thing is now when I drag any answer to the correct one it won't replace the correct one, it will just disappear and if it fails instead of highlighting all the corrects one it will highlight the correct answer (for instance; if I drag wheel to Swim once, it doesn't happen anything just animate to the place where it was, if I do it twice it will highlight the Skate one, and if it fails at third one it just drag wherever he did and highlight with red)

I'm planning to build an app that does a simple check, I'm calling an endpoint and I'll get some params, and then I'll know how many ImageView are going to be displayed in the screen. This is like a puzzle, and it would look like this :

enter image description here

So I have different options, which contains only one correct answer, I'm planing the way to achieve this, could be able to drag "Bow" to the questionmark infront of "skateboarding" and then says that is not correct, then drag it to the "archery" one and replace the questionmark for the ImageView from the bottom that contains the word "Arrow".

Layout should contain one column for Question (this should be the sports) then another one in front of the Question one and should be the Answer one, then below them should contain the Options one.

Was it clear? Otherwise let me know and I'll try to explain it a little bit with more details.

EDIT

What I thought is having like a class that contains a list of Answers or just create like :

RightList : (id:1,id:2,id:3)

LeftList : (id:1, id:2, id:3)

DownList : (Bow = id:2),(Skate = id:1), (Ball = id:3)

Then doing the drag and drop thing when the DragEvent.ACTION_DROP or DragEvent.ACTION_DRAG_ENDEDI do not know which one, check (Pseudocode below)

if(imageDragged.id==location.id) then replace the question mark image for imageDragged
else animate the image to the place where it comes

I do not know if creating a class that implements onDragListener() or something like that, I'd like to have it generic so I can use it on different games like for instance :

SKATE(id:1) ARCHERY(id:2) FOOTBALL(id:3)

Answers : TABLE(C.A. id:1) BOW(C.A. id:2) GRASS(C.A. id:3) GOAL(C.A. id:3) BALL(C.A. id:3) ARROW(C.A. id:2) AXES(C.A. id:1) WHEELS(C.A. id:1)

So if I drag and drop for instance BOW to FOOTBALL then it should display that is bad, otherwise say that it's good.

Community
  • 1
  • 1
Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148
  • 1
    Is your question whether what you want to do makes sense to the uninitiated user, or do you want us to suggest how to achieve what you described above? – Nikos Hidalgo Oct 14 '19 at 11:52
  • A suggestion, the game is like this. Please. – Skizo-ozᴉʞS ツ Oct 14 '19 at 11:52
  • As a totally different way to do it, I would suggest [bubbles-for-android](https://github.com/txusballesteros/bubbles-for-android). (They are based on`Facebook Chatheads` ) See [my answer](https://stackoverflow.com/questions/47244548/changing-layoutparams-of-always-visible-chat-heads-to-not-always-visible/47623784#47623784) – Jon Goodwin Oct 21 '19 at 23:52

2 Answers2

6

EXAMPLE 1/3

Just for reference and summarize everything. Here is one 100 lines code, within single Activity and imports, representing all this behavior even with simple animation.

enter image description here

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bind()
    }

    private fun bind() {
        addQuestions()
        addAnswers()
    }

    @SuppressLint("InflateParams")
    private fun addQuestions() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..8) {
            val view = inflater.inflate(R.layout.item_question, null)
            view.setOnDragListener(DragListener())
            questionContainer.addView(view)
        }
    }


    @SuppressLint("InflateParams")
    private fun addAnswers() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..8) {
            val view = inflater.inflate(R.layout.item_answer, null)
            view.setOnTouchListener(DragItemTouchListener())
            answerContainer.addView(view)
        }
    }

    private inner class DragItemTouchListener : OnTouchListener {

        override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
            return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
                dragMultiple(view)
                true
            } else {
                false
            }
        }

        private fun dragMultiple(view : View) {
            val data = ClipData.newPlainText("", "")
            val shadowBuilder = DragShadowBuilder(
                view
            )
            val parent = view.parent as ViewGroup

            view.startDragAndDrop(data, shadowBuilder, view, 0)
            parent.removeView(view)
        }
    }


    private inner class DragListener : OnDragListener {

        override fun onDrag(v: View, event: DragEvent): Boolean {
            when (event.action) {
                DragEvent.ACTION_DRAG_STARTED -> {

                }
                DragEvent.ACTION_DRAG_ENTERED -> {

                }
                DragEvent.ACTION_DRAG_EXITED -> {

                }
                DragEvent.ACTION_DROP -> {
                    animateDropEffect(v as ViewGroup, event.localState as View)
                }
                DragEvent.ACTION_DRAG_ENDED -> {

                }
                else -> {
                }
            }
            return true
        }

        private fun animateDropEffect(into: ViewGroup, view: View) {
            into.addView(view)
            val params = (view.layoutParams as FrameLayout.LayoutParams)
                .apply {
                    gravity = Gravity.END
                }
            view.layoutParams = params
        }
    }
}

All Xmls used. Below xml for all examples below.

/* activity_main.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:id="@+id/mainContainer"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">


        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="500dp"
            android:animateLayoutChanges="true">

            <LinearLayout
                android:id="@+id/questionContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                android:orientation="vertical">

            </LinearLayout>
        </ScrollView>

        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:animateLayoutChanges="true">

            <LinearLayout
                android:id="@+id/answerContainer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                android:orientation="horizontal">

            </LinearLayout>
        </HorizontalScrollView>

    </LinearLayout>
</FrameLayout>


/* item_question.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:animateLayoutChanges="true"
    android:padding="5dp">

    <View
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="start"
        android:background="@android:color/holo_blue_bright">

    </View>

    <View
        android:id="@+id/questionView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="end"
        android:background="@android:color/holo_orange_light">

    </View>

</FrameLayout>


/* item_answer.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="5dp"
    android:tag="Test">


    <LinearLayout
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:background="@android:color/darker_gray">

    </LinearLayout>

</FrameLayout>

EXAMPLE 2/3

It's not a problem to make dragging for few elements following with a the same approach. Here is a little crappy, but simple example.

enter image description here

Modified code for second example. Xml stay the same.

class MainActivity : AppCompatActivity() {

    var activeOneDrag : Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bind()
    }

    private fun bind() {
        addQuestions()
        addAnswers()
    }

    fun getRandomColor(): Int {
        return Color.argb(255, Random.nextInt(255),
            Random.nextInt(255), Random.nextInt(255))
    }

    @SuppressLint("InflateParams")
    private fun addQuestions() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..8) {
            val view = inflater.inflate(R.layout.item_question, null)
            view.setOnDragListener(DragListener())
            questionContainer.addView(view)
        }
    }


    @SuppressLint("InflateParams")
    private fun addAnswers() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..8) {
            val view = inflater.inflate(R.layout.item_answer, null)
            (view as ViewGroup).getChildAt(0).setBackgroundColor(getRandomColor())
            view.setOnTouchListener(DragItemTouchListener())
            answerContainer.addView(view)
        }
    }


    private inner class DragItemTouchListener : OnTouchListener {

        override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
            return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
                dragMultiple(view)
                true
            } else {
                false
            }
        }

        private fun dragMultiple(view : View) {
            val parent = view.parent as ViewGroup
            parent.removeView(view)
            /**
             * Some other logic with selective multiple View.
             * Just getting neighbor in our case
             */

            var anotherView : View? = null
            if (!activeOneDrag) {
                anotherView = parent.getChildAt(
                    parent.indexOfChild(view) + 1)
                parent.removeView(anotherView)
            }
            activeOneDrag = !activeOneDrag

            /**
             * As you can see, there is postDelay here.
             * But only for our case with animateLayoutChanges,
             * with delays removing View! In your samples, you could remove it
             * with listener on your own animation, if any!
             */
            parent.postDelayed({

                val layout = LinearLayout(this@MainActivity)
                val params = FrameLayout.LayoutParams(
                    FrameLayout.LayoutParams.WRAP_CONTENT,
                    FrameLayout.LayoutParams.WRAP_CONTENT)
                params.gravity = Gravity.BOTTOM
                layout.layoutParams = params
                layout.orientation = LinearLayout.HORIZONTAL


                layout.addView(view)
                if (anotherView != null) {
                    layout.addView(anotherView)
                }
                layout.visibility = INVISIBLE
                mainContainer.addView(layout)

                parent.post {
                    layout.startDragAndDrop(
                        ClipData.newPlainText("", ""),
                        DragShadowBuilder(layout), layout, 0)
                }

            }, 400)

        }
    }


    private inner class DragListener : OnDragListener {

        override fun onDrag(v: View, event: DragEvent): Boolean {
            when (event.action) {
                DragEvent.ACTION_DRAG_STARTED -> {

                }
                DragEvent.ACTION_DRAG_ENTERED -> {

                }
                DragEvent.ACTION_DRAG_EXITED -> {

                }
                DragEvent.ACTION_DROP -> {
                    val view = event.localState as View
                    (view.parent as ViewGroup).removeView(view)
                    view.visibility = VISIBLE
                    animateDropEffect(v as ViewGroup, event.localState as View)
                }
                DragEvent.ACTION_DRAG_ENDED -> {

                }
                else -> {
                }
            }
            return true
        }

        private fun animateDropEffect(into: ViewGroup, view: View) {
            into.addView(view)
            val params = (view.layoutParams as FrameLayout.LayoutParams)
                .apply {
                    gravity = Gravity.END
                }
            view.layoutParams = params
        }
    }
}

EXAMPLE 3/3

As I see, it's not clear, how to change simple actions with animation or Drag listening area. Here is another simple example of doing all actions

enter image description here

class MainActivity : AppCompatActivity() {

    @Volatile
    var state : State = State.INACTIVE

    enum class State {
        ACTIVE, INACTIVE, HANDLED
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bind()
    }

    private fun bind() {
        addQuestions()
        addAnswers()
    }

    private fun getRandomColor(): Int {
        return Color.argb(255, Random.nextInt(255),
            Random.nextInt(255), Random.nextInt(255))
    }

    @SuppressLint("InflateParams")
    private fun addQuestions() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..8) {
            val view = inflater.inflate(R.layout.item_question, null)
            view.findViewById<View>(R.id.questionView)
                .setOnDragListener(DragListener())
            questionContainer.addView(view)
        }
    }


    @SuppressLint("InflateParams")
    private fun addAnswers() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..8) {
            val view = inflater.inflate(R.layout.item_answer, null)
            (view as ViewGroup).getChildAt(0).setBackgroundColor(getRandomColor())
            view.setOnTouchListener(DragItemTouchListener())
            answerContainer.addView(view)
        }
    }

    private inner class DragItemTouchListener : OnTouchListener {
        val ITEM_INDEX_D = "Index-From"

        override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
            return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
                createDrag(view)
                true
            } else {
                false
            }
        }

        private fun createDrag(view : View) {
            val parent = view.parent as ViewGroup
            view.tag = Pair(ITEM_INDEX_D,
                parent.indexOfChild(view))

            view.startDragAndDrop(ClipData.newPlainText("", ""),
                DragShadowBuilder(view), view, 0)
            parent.removeView(view)
            parent.setBackgroundColor(Color.WHITE)
        }
    }

    private inner class DragListener : OnDragListener {

        override fun onDrag(parent: View, event: DragEvent): Boolean {
            val view = event.localState as View

            when (event.action) {
                DragEvent.ACTION_DRAG_STARTED -> {
                    state = State.ACTIVE
                }
                DragEvent.ACTION_DRAG_ENTERED -> {
                }
                DragEvent.ACTION_DRAG_EXITED -> {

                }
                DragEvent.ACTION_DROP -> {
                    state = State.HANDLED
                    animateDropEffect(parent, view)
                    return true
                }
                DragEvent.ACTION_DRAG_ENDED -> {
                    if (state == State.ACTIVE) {
                        state = State.INACTIVE
                        animateMoveBack(view,
                            (view.tag as Pair<*, *>).second as Int)
                    }
                    return true
                }
                else -> {
                }
            }
            return true
        }

        private fun animateMoveBack(view: View, index : Int) {
            answerContainer.addView(view, index)
        }

        private fun animateDropEffect(into: View, view: View) {
            val parent = (into.parent as ViewGroup)
            parent.addView(view)

            val params = (view.layoutParams as FrameLayout.LayoutParams)
                .apply {
                    gravity = Gravity.END
                }
            view.layoutParams = params
            checkIsCorrect(parent)
        }

        private fun checkIsCorrect(parent : ViewGroup) {
            val correct = Random.nextBoolean()

            val colorFrom = Color.WHITE
            val colorTo : Int = if (correct) 0x8000ff00.toInt() else 0x80ff0000.toInt()
            ObjectAnimator.ofObject(
                parent,
                "backgroundColor",
                ArgbEvaluator(),
                colorFrom,
                colorTo
            )
                .setDuration(1000)
                .start()
        }
    }
}

UPDATE

The last update from the comments sections. I think it's enough, and of course you would need you changes. So just change two "if" statement to align with your requirements and animation.

enter image description here

enter image description here

class MainActivity : AppCompatActivity() {

    enum class State {
        ACTIVE, INACTIVE, HANDLED
    }

    var state : State = State.INACTIVE

    var failsCount = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bind()
    }

    private fun bind() {
        addQuestions()
        addAnswers()
    }

    private fun getRandomColor(): Int {
        return Color.argb(255, Random.nextInt(255),
            Random.nextInt(255), Random.nextInt(255))
    }

    @SuppressLint("InflateParams")
    private fun addQuestions() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..3) {
            val view = inflater.inflate(R.layout.item_question, null)
            view.findViewById<View>(R.id.questionView)
                .setOnDragListener(DragListener())
            questionContainer.addView(view)
        }
    }


    @SuppressLint("InflateParams")
    private fun addAnswers() {
        val inflater = getSystemService(
            Context.LAYOUT_INFLATER_SERVICE
        ) as LayoutInflater
        for (i in 1..3) {
            val view = inflater.inflate(R.layout.item_answer, null)
            (view as ViewGroup).getChildAt(0).setBackgroundColor(getRandomColor())
            view.setOnTouchListener(DragItemTouchListener())
            answerContainer.addView(view)
        }
    }

    private inner class DragItemTouchListener : OnTouchListener {
        val ITEM_INDEX_D = "Index-From"

        override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
            return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
                createDrag(view)
                true
            } else {
                false
            }
        }

        private fun createDrag(view : View) {
            val parent = view.parent as ViewGroup
            view.tag = Pair(ITEM_INDEX_D,
                parent.indexOfChild(view))

            view.startDragAndDrop(ClipData.newPlainText("", ""),
                DragShadowBuilder(view), view, 0)
            parent.removeView(view)
            parent.setBackgroundColor(Color.WHITE)
        }
    }

    private inner class DragListener : OnDragListener {

        val ANIM_DURATION_LONG = TimeUnit.SECONDS.toMillis(1)
        val ANIM_DURATION_SHORT = TimeUnit.MILLISECONDS.toMillis(500)

        val GREEN_ALPHA = 0x8000ff00.toInt()
        val RED_ALPHA = 0x80ff0000.toInt()
        val ANIM_COLOR = "backgroundColor"

            override fun onDrag(parent: View, event: DragEvent): Boolean {
            val view = event.localState as View

            when (event.action) {
                DragEvent.ACTION_DRAG_STARTED -> {
                    state = State.ACTIVE
                }
                DragEvent.ACTION_DRAG_ENTERED -> {
                }
                DragEvent.ACTION_DRAG_EXITED -> {

                }
                DragEvent.ACTION_DROP -> {
                    state = State.HANDLED
                    animateDropEffect(parent, view)
                    return true
                }
                DragEvent.ACTION_DRAG_ENDED -> {
                    if (state == State.ACTIVE) {
                        state = State.INACTIVE
                        animateMoveBack(view,
                            (view.tag as Pair<*, *>).second as Int)
                    }
                    return true
                }
                else -> {
                }
            }
            return true
        }

        private fun animateMoveBack(view: View, index : Int) {
            answerContainer.addView(view, index)
        }

        private fun animateDropEffect(into: View, view: View) {
            val parent = (into.parent as ViewGroup)
            parent.addView(view)

            val params = (view.layoutParams as FrameLayout.LayoutParams)
                .apply {
                    gravity = Gravity.END
                }
            view.layoutParams = params
            checkIsCorrect(parent)
        }

        private fun checkIsCorrect(parent : ViewGroup) {
            val correct = false
            if (correct) {
                animateColorChange(parent, true)
                return
            }
            if (++failsCount > Companion.MAX_FAIL_COUNT) {
                animateColorChange(parent, false)
                return
            }
            animateWrongAttempt(parent)
        }

        private fun animateWrongAttempt(parent: ViewGroup) {
            val questionMark = parent.findViewById<View>(R.id.questionView)
            questionMark.setBackgroundColor(Color.RED)

            val va = ValueAnimator.ofFloat(1f, 1.1f)
            va.interpolator = BounceInterpolator()

            va.duration = ANIM_DURATION_SHORT
            va.addUpdateListener { animation ->
                questionMark.scaleX = animation.animatedValue as Float
                questionMark.scaleY = animation.animatedValue as Float
            }
            va.start()

        }

        private fun animateColorChange(parent : ViewGroup, right : Boolean) {
            val colorFrom = Color.WHITE
            ObjectAnimator
                .ofObject(parent, ANIM_COLOR,
                    ArgbEvaluator(), colorFrom,
                    if (right) GREEN_ALPHA else RED_ALPHA)
                .setDuration(ANIM_DURATION_LONG)
                .start()
        }
    }

    companion object {
        const val MAX_FAIL_COUNT = 2
    }
}

And new xml.

/* activity_main.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    android:id="@+id/mainContainer"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">


        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="500dp"
            android:animateLayoutChanges="true">

            <LinearLayout
                android:id="@+id/questionContainer"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                android:orientation="vertical">

            </LinearLayout>
        </ScrollView>

        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:animateLayoutChanges="true">

            <LinearLayout
                android:id="@+id/answerContainer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                android:orientation="horizontal">

            </LinearLayout>
        </HorizontalScrollView>

    </LinearLayout>
</FrameLayout>



/* item_question.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:animateLayoutChanges="true"
    android:paddingTop="10dp">

    <View
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="start"
        android:layout_margin="5dp"
        android:background="@android:color/holo_blue_bright">

    </View>

    <View
        android:id="@+id/questionView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="end"
        android:layout_margin="5dp"
        android:background="@android:color/holo_orange_light">

    </View>

</FrameLayout>



/* item_answer.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="5dp"
    android:tag="Test">


    <LinearLayout
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:background="@android:color/darker_gray">

    </LinearLayout>

</FrameLayout>
GensaGames
  • 5,538
  • 4
  • 24
  • 53
  • In this case if I only want to drag one item I guess will work, but if I missing the xml, also I need something to multi-drag differents images to the same "Skateboarding" for instance. That's why I proposed something like having an id or a class that implements onDragListener and inside of it have the id of the correct one and its own id. – Skizo-ozᴉʞS ツ Oct 17 '19 at 06:34
  • @Skizo-ozᴉʞS What you mean by missing xml? All items built in runtime. I didn't get you. And dragging few items you could do in the same way. – GensaGames Oct 17 '19 at 15:46
  • What is questionContainer and what is answerContainer? Just share the xml and let me check if it's what I want or not please. – Skizo-ozᴉʞS ツ Oct 17 '19 at 20:28
  • Gansa I'm not meaning this, Let me edit the question but you are close.... – Skizo-ozᴉʞS ツ Oct 18 '19 at 15:02
  • @Skizo-ozᴉʞS From your question, I have created exactly, what do you need. You have `Questions` `Marks` and `Answers` rows. `Answer` tiles you can drag to the `Question` marks and animate wrong/correct behavior, with listeners on Drag (there are relevant callbacks with data passed from starting Drag). – GensaGames Oct 18 '19 at 17:07
  • Could you describe in simple actions, what else do you need? – GensaGames Oct 18 '19 at 17:07
  • Theres the simple game : Questions (SkateBoarding, Archery, Football) In front there's another column the question mark ones, then below there's the answer list (Skate, bow, ball) if I try to drag Skate to Archery it should notify that it's not correct, if I drag it to the Skateboarding it should replace questionmark witht he same image. – Skizo-ozᴉʞS ツ Oct 18 '19 at 17:34
  • Second one is that : Questions(Skateboarding, archery, football) and now there's no front column it's just answer column(skate, wheels, bow, arrow, ball, goal, etc..) and it's pretty the same I have to drag skate to Skateboarding not into the question mark column and if I fail is the same, just animate to the list and say that it's wrong. – Skizo-ozᴉʞS ツ Oct 18 '19 at 17:36
  • For your first point. Is not clear, that you could animate and check right/wrong answer on `ACTION_DROP` callback? With supplied data, from the dragged object. It's just one simple `if` in the code above... – GensaGames Oct 18 '19 at 17:48
  • @Skizo-ozᴉʞS For the second point, it's just to change on drag listening on another view. Is it really problem? – GensaGames Oct 18 '19 at 17:50
  • Thanks for the answer, tomorrow I'll check it out, the second game is instead of 3 lists one for the question and then another list with lot of answers but one question can contain more than one answer, I'll check it tomorrow and I'll let you know if I can costumize like the drag and drop and if its wrong just animate back to the answer list, thanks. – Skizo-ozᴉʞS ツ Oct 18 '19 at 22:06
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201148/discussion-between-skizo-oziks-and-gensagames). – Skizo-ozᴉʞS ツ Oct 19 '19 at 21:19
  • Did you check the chat? :( – Skizo-ozᴉʞS ツ Oct 21 '19 at 07:20
  • if anyone knows, in order to drag and drop, do we must have a second container that is like targetview in order to make everything work? I am trying to have only single constraintlayout and want to move around textview within that container. Is it possible? thanks – Ajay P. Prajapati Jun 23 '23 at 00:09
0

I think, there are various ways to do it. For me, seems nice, to split the screen into two main sections. a) To have single vertical list with Items and connections to "Question mark", refer to the image below.

enter image description here Single row in a vertical list

So it's easy to change size to the item list, which you are retrieving from Server, by dynamically adding and removing simple View. Once correct tile would be places on the top of "Question mark" you will change new View on the top of this View. b) To create bottom list with possible "Answers" there will be Horizontal list, with this items. As you mentioned in our question.

enter image description here

Bottom horizontal list


Description

a) I really do't like implementation of such kind of task with Android widgets like RecyclerView. With small list of items, we could bring a lot of customization. For the first list I would use VerticalScrollView with LinearLayout and View (with any your layout).

Note. Your last layout could be ConstraintLayout. Ex. 1. In this case your dragged items would stick on exact places, where you live it. And In case it's stick on specific row with more than half size, you could change view for it (to green or red). Ex 2. Maybe even better way would be animate moving from place where live the tiles, to the nearest "Question mark" with "Property Animation"

b) To create bottom list I would use the same structure with `HorizontalListView". This will help to use "Property Animation" and not just animate dragging, but also move an object with your grading trajectory. Using dynamically added items in the containers with simple views will reflect on changes in bottom, or top lists.


Implementation

1) Initial listeners for each your "Answer" tiles.

// Assign the touch listener to your view which you want to move
findViewById(R.id.myimage1).setOnTouchListener(new MyTouchListener());

// This defines your touch listener
private final class MyTouchListener implements OnTouchListener {
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            ClipData data = ClipData.newPlainText("", "");
            DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
                view);
            view.startDrag(data, shadowBuilder, view, 0);
            view.setVisibility(View.INVISIBLE);
            return true;
        } else {
        return false;
        }
    }
}

2) Define each of your target Views in the top list.

findViewById(R.id.bottomright).setOnDragListener(new MyDragListener());

class MyDragListener implements OnDragListener {
    Drawable enterShape = getResources().getDrawable(
            R.drawable.shape_droptarget);
    Drawable normalShape = getResources().getDrawable(R.drawable.shape);

    @Override
    public boolean onDrag(View v, DragEvent event) {
        int action = event.getAction();
        switch (event.getAction()) {
        case DragEvent.ACTION_DRAG_STARTED:
        // do nothing
            break;
        case DragEvent.ACTION_DRAG_ENTERED:
            v.setBackgroundDrawable(enterShape);
            break;
        case DragEvent.ACTION_DRAG_EXITED:
            v.setBackgroundDrawable(normalShape);
            break;
        case DragEvent.ACTION_DROP:
            // Dropped, reassign View to ViewGroup
            View view = (View) event.getLocalState();
            ViewGroup owner = (ViewGroup) view.getParent();
            owner.removeView(view);
            LinearLayout container = (LinearLayout) v;
            container.addView(view);
            view.setVisibility(View.VISIBLE);
            break;
        case DragEvent.ACTION_DRAG_ENDED:
            v.setBackgroundDrawable(normalShape);
            default:
            break;
        }
        return true;
    }
}

3) That is it you need to create dragging. Just to make another setup in the Activity and View changes.

However you could check few other examples of drag and drop implementation. But since you were using simple Views and Scrollable containers, you can align every sample to work with your lists.

GensaGames
  • 5,538
  • 4
  • 24
  • 53
  • I find the same problem as described in the other answer you posted. I should have a way to say ok, this is the id of correct one, and then assign to the images an id and the id correct or something like this. But also I'm looking for different approaches, but doing this what you said I can not figure it out how to say that more than one image is correct in one image. – Skizo-ozᴉʞS ツ Oct 17 '19 at 11:30