Search code examples
androiddrag-and-drop

How to drag the TextView to the correct target other TextView


I have giving the full code that I have.

The code bellow can drag and drop very smoothly but it is not working like what I need like bellow description.

  1. Drag Data Is (Apple, Orage, Ball)
  2. Drop target location is (A for, O for, B for)

So, if drag "Apple" drop to "A for" it will be CORRECT and update text to "A for Apple" and Text "Apple" at drag will be hide... but if we drag "Apple" drop to "O for" or "B for" it will be failed because "O for Orange" and "B for Ball".

Based on the below code, what should I add.

Layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:paddingLeft="50dp"
android:paddingRight="50dp" >


<TextView
    android:id="@+id/option_1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/option"
    android:gravity="center"
    android:text="Apple"
    android:textStyle="bold" />

<TextView
    android:id="@+id/option_2"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/option"
    android:gravity="center"
    android:text="Orange"
    android:textStyle="bold" />

<TextView
    android:id="@+id/option_3"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/option"
    android:gravity="center"
    android:text="Ball"
    android:textStyle="bold" />

<TextView
    android:id="@+id/choice_1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/choice"
    android:gravity="center"
    android:text="A for " />

<TextView
    android:id="@+id/choice_2"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/choice"
    android:gravity="center"
    android:text="O for" />

<TextView
    android:id="@+id/choice_3"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/choice"
    android:gravity="center"
    android:text="B for " />

</LinearLayout>

Acvitity:

public class picture_to_word_24_color extends Activity {

//text views being dragged and dropped onto
private TextView option1, option2, option3, choice1, choice2, choice3;
public CharSequence dragData;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.picture_to_word_24_color);

    //get both sets of text views

    //views to drag
    option1 = (TextView)findViewById(R.id.option_1);
    option2 = (TextView)findViewById(R.id.option_2);
    option3 = (TextView)findViewById(R.id.option_3);

    //views to drop onto
    choice1 = (TextView)findViewById(R.id.choice_1);
    choice2 = (TextView)findViewById(R.id.choice_2);
    choice3 = (TextView)findViewById(R.id.choice_3);

    //set touch listeners
    option1.setOnTouchListener(new ChoiceTouchListener());
    option2.setOnTouchListener(new ChoiceTouchListener());
    option3.setOnTouchListener(new ChoiceTouchListener());

    //set drag listeners
    choice1.setOnDragListener(new ChoiceDragListener());
    choice2.setOnDragListener(new ChoiceDragListener());
    choice3.setOnDragListener(new ChoiceDragListener());
}

/**
 * ChoiceTouchListener will handle touch events on draggable views
 *
 */
private final class ChoiceTouchListener implements OnTouchListener {
    @SuppressLint("NewApi")
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
            /*
             * Drag details: we only need default behavior
             * - clip data could be set to pass data as part of drag
             * - shadow can be tailored
             */
            ClipData data = ClipData.newPlainText("", "");
            DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
            //start dragging the item touched
            view.startDrag(data, shadowBuilder, view, 0);
            return true;
        } else {
            return false;
        }
    }
} 

/**
 * DragListener will handle dragged views being dropped on the drop area
 * - only the drop action will have processing added to it as we are not
 * - amending the default behavior for other parts of the drag process
 *
 */
@SuppressLint("NewApi")
private class ChoiceDragListener implements OnDragListener {

    @Override
    public boolean onDrag(View v, DragEvent event) {
        switch (event.getAction()) {
        case DragEvent.ACTION_DRAG_STARTED:
            //no action necessary
            break;
        case DragEvent.ACTION_DRAG_ENTERED:
            //no action necessary
            Toast.makeText(picture_to_word_24_color.this, "ACTION_DRAG_ENTERED", Toast.LENGTH_SHORT).show();
            break;
        case DragEvent.ACTION_DRAG_EXITED:        
            //no action necessary
            Toast.makeText(picture_to_word_24_color.this, "ACTION_DRAG_EXITED", Toast.LENGTH_SHORT).show();
            break;
        case DragEvent.ACTION_DROP:
            
            Toast.makeText(picture_to_word_24_color.this, "ACTION_DROP", Toast.LENGTH_SHORT).show();
            //handle the dragged view being dropped over a drop view
            View view = (View) event.getLocalState();
            //stop displaying the view where it was before it was dragged
            view.setVisibility(View.INVISIBLE);
            //view dragged item is being dropped on
            TextView dropTarget = (TextView) v;
            //view being dragged and dropped
            TextView dropped = (TextView) view;
            //update the text in the target view to reflect the data being dropped
            dropTarget.setText(dropped.getText());
            //make it bold to highlight the fact that an item has been dropped
            dropTarget.setTypeface(Typeface.DEFAULT_BOLD);
            //if an item has already been dropped here, there will be a tag
            Object tag = dropTarget.getTag();
            //if there is already an item here, set it back visible in its original place
            if(tag!=null)
            {
                //the tag is the view id already dropped here
                int existingID = (Integer)tag;
                //set the original view visible again
                findViewById(existingID).setVisibility(View.VISIBLE);
            }
            //set the tag in the target view being dropped on - to the ID of the view being dropped
            dropTarget.setTag(dropped.getId());
            break;
        case DragEvent.ACTION_DRAG_ENDED:
            //no action necessary
            Toast.makeText(picture_to_word_24_color.this, "ACTION_DRAG_ENDED", Toast.LENGTH_SHORT).show();
            break;
        default:
            break;
        }
        return true;
    }
} 
}

Solution

  • Try the following code, it should work according to your description in the question. I have added an extra thing i.e. reset button as I felt it necessary.

    picture_to_word_24_color.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="10dp"
    android:paddingLeft="50dp"
    android:paddingRight="50dp" >
    
    
    <TextView
    android:id="@+id/option_1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/option"
    android:gravity="center"
    android:text="Apple"
    android:textStyle="bold" />
    
    <TextView
    android:id="@+id/option_2"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/option"
    android:gravity="center"
    android:text="Orange"
    android:textStyle="bold" />
    
    <TextView
    android:id="@+id/option_3"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/option"
    android:gravity="center"
    android:text="Ball"
    android:textStyle="bold" />
    
    <TextView
    android:id="@+id/choice_1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/choice"
    android:gravity="center"
    android:text="A for " />
    
    <TextView
    android:id="@+id/choice_2"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/choice"
    android:gravity="center"
    android:text="O for " />
    
    <TextView
    android:id="@+id/choice_3"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="@drawable/choice"
    android:gravity="center"
    android:text="B for " />
    
    <Button 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Reset"
    android:onClick="reset"/>
    
    </LinearLayout>
    

    Picture_to_word_24_color.java

    package com.example.touchanddrag;
    
    import android.os.Build;
    import android.os.Bundle;
    import android.annotation.SuppressLint;
    import android.annotation.TargetApi;
    import android.app.Activity;
    import android.content.ClipData;
    import android.graphics.Typeface;
    import android.view.DragEvent;
    import android.view.Menu;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.View.DragShadowBuilder;
    import android.view.View.OnDragListener;
    import android.view.View.OnTouchListener;
    import android.widget.TextView;
    import android.widget.Toast;
    
    @SuppressLint("NewApi")
    public class Picture_to_word_24_color extends Activity {
    
    private TextView option1, option2, option3, choice1, choice2, choice3;
    public CharSequence dragData;
    
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.picture_to_word_24_color);
    
        //get both sets of text views
    
        //views to drag
        option1 = (TextView)findViewById(R.id.option_1);
        option2 = (TextView)findViewById(R.id.option_2);
        option3 = (TextView)findViewById(R.id.option_3);
    
        //views to drop onto
        choice1 = (TextView)findViewById(R.id.choice_1);
        choice2 = (TextView)findViewById(R.id.choice_2);
        choice3 = (TextView)findViewById(R.id.choice_3);
    
        //set touch listeners
        option1.setOnTouchListener(new ChoiceTouchListener());
        option2.setOnTouchListener(new ChoiceTouchListener());
        option3.setOnTouchListener(new ChoiceTouchListener());
    
        //set drag listeners
        choice1.setOnDragListener(new ChoiceDragListener());
        choice2.setOnDragListener(new ChoiceDragListener());
        choice3.setOnDragListener(new ChoiceDragListener());
    }
    
    /**
     * ChoiceTouchListener will handle touch events on draggable views
     *
     */
    private final class ChoiceTouchListener implements OnTouchListener {
        @SuppressLint("NewApi")
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                /*
                 * Drag details: we only need default behavior
                 * - clip data could be set to pass data as part of drag
                 * - shadow can be tailored
                 */
                ClipData data = ClipData.newPlainText("", "");
                DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
                //start dragging the item touched
                view.startDrag(data, shadowBuilder, view, 0);
                return true;
            } else {
                return false;
            }
        }
    } 
    
    /**
     * DragListener will handle dragged views being dropped on the drop area
     * - only the drop action will have processing added to it as we are not
     * - amending the default behavior for other parts of the drag process
     *
     */
    @SuppressLint("NewApi")
    private class ChoiceDragListener implements OnDragListener {
    
        @Override
        public boolean onDrag(View v, DragEvent event) {
            switch (event.getAction()) {
            case DragEvent.ACTION_DRAG_STARTED:
                //no action necessary
                break;
            case DragEvent.ACTION_DRAG_ENTERED:
                //no action necessary
                break;
            case DragEvent.ACTION_DRAG_EXITED:        
                //no action necessary
                break;
            case DragEvent.ACTION_DROP:
    
                //handle the dragged view being dropped over a drop view
                View view = (View) event.getLocalState();
                //view dragged item is being dropped on
                TextView dropTarget = (TextView) v;
                //view being dragged and dropped
                TextView dropped = (TextView) view;
                //checking whether first character of dropTarget equals first character of dropped
                if(dropTarget.getText().toString().charAt(0) == dropped.getText().toString().charAt(0))
                {
                    //stop displaying the view where it was before it was dragged
                    view.setVisibility(View.INVISIBLE);
                    //update the text in the target view to reflect the data being dropped
                    dropTarget.setText(dropTarget.getText().toString() + dropped.getText().toString());
                    //make it bold to highlight the fact that an item has been dropped
                    dropTarget.setTypeface(Typeface.DEFAULT_BOLD);
                    //if an item has already been dropped here, there will be a tag
                    Object tag = dropTarget.getTag();
                    //if there is already an item here, set it back visible in its original place
                    if(tag!=null)
                    {
                        //the tag is the view id already dropped here
                        int existingID = (Integer)tag;
                        //set the original view visible again
                        findViewById(existingID).setVisibility(View.VISIBLE);
                    }
                    //set the tag in the target view being dropped on - to the ID of the view being dropped
                    dropTarget.setTag(dropped.getId());
                    //remove setOnDragListener by setting OnDragListener to null, so that no further drag & dropping on this TextView can be done
                    dropTarget.setOnDragListener(null);
                }
                else
                    //displays message if first character of dropTarget is not equal to first character of dropped
                    Toast.makeText(Picture_to_word_24_color.this, dropTarget.getText().toString() + "is not " + dropped.getText().toString(), Toast.LENGTH_LONG).show();
                break;
            case DragEvent.ACTION_DRAG_ENDED:
                //no action necessary
                break;
            default:
                break;
            }
            return true;
        }
    } 
    
    public void reset(View view)
    {
        option1.setVisibility(TextView.VISIBLE);
        option2.setVisibility(TextView.VISIBLE);
        option3.setVisibility(TextView.VISIBLE);
    
        choice1.setText("A for ");
        choice2.setText("O for ");
        choice3.setText("B for ");
    
        choice1.setTag(null);
        choice2.setTag(null);
        choice3.setTag(null);
    
        choice1.setTypeface(Typeface.DEFAULT);
        choice2.setTypeface(Typeface.DEFAULT);
        choice3.setTypeface(Typeface.DEFAULT);
    
        choice1.setOnDragListener(new ChoiceDragListener());
        choice2.setOnDragListener(new ChoiceDragListener());
        choice3.setOnDragListener(new ChoiceDragListener());
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.picture_to_word_24_color, menu);
        return true;
    }
    
    }
    

    Don't forget to check for target API level. As I had to add @SuppressLint("NewApi")