Android meets Drag and Drop by chance

Posted by bgbs on Sun, 07 Jul 2019 21:29:40 +0200

Recently, during the iteration of the project, I encountered some knowledge points about Drag And Drop, so I made some simple explorations on this knowledge point and recorded my experience in using it.

Design sketch


GIF.gif

Requirements introduction

Drag the pictures provided in the question stem to the answer box for sorting. The pictures in the question stem area can not be switched between each other. The pictures in the answer box can be replaced each other. Drag the pictures in the answer box to the question stem to delete the pictures.

Ideas based on official documents

When the user makes a gesture that you recognize as starting to drag data, the drag-and-drop operation begins. In response, your application will inform the system that dragging is starting. The system callbacks the application to obtain a representation of the data being dragged. In the process of moving this representation ("drag shadow") with the user's finger on the current layout, the system sends drag events to the drag event listener object associated with the View object in the layout and the drag event callback method. After the user releases the drag shadow, the system immediately terminates the drag operation.
When you start dragging, include both the data you want to move and the metadata describing it in the system call. During the drag, the system sends the drag event to the drag event listener or callback method for each view in the layout. These listeners or callback methods can use metadata to determine whether they want to accept data when it is dropped. If the user places the data on a view object and the listener or callback method of the view object has told the system that it wants to accept the dropped data before, the system sends the data to the listener or callback method in the drag event.

Implementation Method

From Realization View.OnDragListener The class created the drag event listener object ("listeners"). Using View Objects setOnDragListener() Method Sets the drag event listener object for the view. Each view object also has one onDragEvent() Callback method.


DragEvent Operational Type. png

Drag Shadows

During the drag-and-drop operation, the system displays the user's dragged image. For data movement, this image represents data being dragged. For other operations, this image represents an aspect of the drag-and-drop operation. This image is called drag shadow. You use View.DragShadowBuilder The object declaration method creates drag shadows and then uses them startDrag() Pass it to the system when you start dragging. As a system pair startDrag() As part of the response, the system calls you to View.DragShadowBuilder The callback method defined in this section captures the drag shadow.

Code presentation

activity_main.xml layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="com.ooowin.itemtouchhelperdemo.MainActivity">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

Title title.xml layout

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:gravity="center_vertical">
</TextView>

Topic content.xml layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="#E1E1E1"
        android:layout_centerInParent="true"
        android:layout_margin="8dp">
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:src="@mipmap/ic_launcher"/>
    <TextView
        android:id="@+id/num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
    </FrameLayout>
</RelativeLayout>

MainAcitivity

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    //Find the address of several pictures on the Internet
    private String[] images = {"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1492574781205&di=2a8fb3d3916f88a21ec33d08e5795e61&imgtype=0&src=http%3A%2F%2Ffile.cbda.cn%2Fuploadfile%2F2015%2F0330%2F20150330041852447.jpg",
    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1492574781204&di=1259124b260d828df88602e849d27bed&imgtype=0&src=http%3A%2F%2Fpic24.nipic.com%2F20121023%2F5692504_105430688142_2.jpg",
    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1492574781204&di=c0bbe94467bfb35fd30d76c0e9638a4d&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fw%253D580%2Fsign%3Deb4ecc6249fbfbeddc59367748f1f78e%2F36cd4b2309f7905229c31a630ef3d7ca7acbd57c.jpg",
    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1492574781204&di=7c1c2ae590318f9d1b1473b54ef97f12&imgtype=0&src=http%3A%2F%2Fpic2.ooopic.com%2F11%2F44%2F22%2F15b1OOOPIC2a.jpg",
    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1492574781203&di=7466a35b511204029646e534a88e5297&imgtype=0&src=http%3A%2F%2Fimg01.taopic.com%2F141127%2F240494-14112FQ23021.jpg",
    "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1492574781202&di=a24bb915d997bae104a7b98a1311682a&imgtype=0&src=http%3A%2F%2Feasyread.ph.126.net%2FJUvXxLCFCK7w8tDEvOaEYg%3D%3D%2F8796093022405405071.jpg"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = ((RecyclerView) findViewById(R.id.recyclerView));
        List<String> questions = new ArrayList<>();
        List<String> answers = new ArrayList<>();
        //Add content to the stem and answer
        for (int i = 0; i < 6; i++) {
            questions.add(images[i]);
            answers.add("");
        }


        //Create a Layout Manager
        GridLayoutManager manager = new GridLayoutManager(this,3);
        recyclerView.setLayoutManager(manager);

        final RecyclerViewAdapter adapter = new RecyclerViewAdapter(this,questions,answers);
        recyclerView.setAdapter(adapter);

        //By default, each item accounts for 1. You can change it by providing a custom GridLayout Manager. Through the setSpanSizeLookup SpanSizeLookup instance (SpanSizeLookup).
        manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                //Specify their span by determining the layout type
                int viewType = adapter.getItemViewType(position);
                return viewType == RecyclerViewAdapter.QUESTION_CONTENT || viewType == RecyclerViewAdapter.ANSWER_CONTENT?1:3;
            }
        });
    }
}

RecyclerViewAdapter

/**
 * Created by xiaolong on 2017/4/18.
 */
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    //Title of the question
    public static final int QUESTION_HEADER = 1;
    //Answer Title Tips
    public static final int ANSWER_HEADER = 2;
    //Topic content
    public static final int QUESTION_CONTENT = 3;
    //Answer Content
    public static final int ANSWER_CONTENT = 4;
    //Number of titles
    public static final int HEADER_COUNT = 2;
    //context
    private Context context;
    //Question stem and answer data resources
    private List<String> questions,answers;
    //Layout loader
    private LayoutInflater inflater;

    public RecyclerViewAdapter(Context context, List<String> questions, List<String> answers) {
        this.context = context;
        this.questions = questions;
        this.answers = answers;
        inflater = LayoutInflater.from(context);
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return QUESTION_HEADER;
        }else if(position == questions.size() + 1){
            return ANSWER_HEADER;
        }else if(position > 0 && position < questions.size() + 1){
            return QUESTION_CONTENT;
        }else {
            return ANSWER_CONTENT;
        }
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        switch (viewType) {
            case QUESTION_HEADER:
            case ANSWER_HEADER:
                view = inflater.inflate(R.layout.title,parent,false);
                return new TitleViewHolder(view);
            case QUESTION_CONTENT:
            case ANSWER_CONTENT:
                view = inflater.inflate(R.layout.answer,parent,false);
                return new ThumViewHolder(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof TitleViewHolder){
            TitleViewHolder titleViewHolder = (TitleViewHolder) holder;
            switch (getItemViewType(position)) {
                case QUESTION_HEADER:
                    titleViewHolder.title.setText("1.According to the observation results, 6 pictures were arranged in order.");//Title I
                    break;
                case ANSWER_HEADER:
                    titleViewHolder.title.setText("Drag and drop in order to the corresponding box below.");//Title II
                    break;
            }
        }else if (holder instanceof ThumViewHolder){
            final ThumViewHolder thumViewHolder = (ThumViewHolder) holder;
            switch (getItemViewType(position)) {
                case QUESTION_CONTENT:
                    //Load the picture in the stem of the question
                    Glide.with(context).load(questions.get(position - 1)).into(thumViewHolder.answerImage);
                    break;
                case ANSWER_CONTENT:
                    //Display the ranking number of the answers
                    thumViewHolder.orderId.setText(position - questions.size() - HEADER_COUNT +"");
                    //Load the picture in the answer
                    Glide.with(context).load(answers.get(position - questions.size() - HEADER_COUNT)).into(thumViewHolder.answerImage);
                    break;
            }
            thumViewHolder.answerImage.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    //Triggered in Mobile State
                    if (MotionEventCompat.getActionMasked(motionEvent) == MotionEvent.ACTION_MOVE) {
                        ClipData data = ClipData.newPlainText("value",position + "");
                        //This method is discarded at API level 24. startDragAndDrop() version is used for the new platform.
                        thumViewHolder.answerImage.startDrag(data,new View.DragShadowBuilder(view),null,0);
                    }
                    return false;
                }
            });
            thumViewHolder.answerImage.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //Get Subscripts
                    Toast.makeText(context,position+"",Toast.LENGTH_SHORT).show();
                }
            });
            thumViewHolder.answerImage.setOnDragListener(new View.OnDragListener() {
                @Override
                public boolean onDrag(View view, DragEvent dragEvent) {
                    switch (dragEvent.getAction()) {

                        case DragEvent.ACTION_DRAG_STARTED:
                            return true;

                        case DragEvent.ACTION_DRAG_ENTERED:
                            return true;

                        case DragEvent.ACTION_DRAG_LOCATION:
                            return true;

                        case DragEvent.ACTION_DRAG_EXITED:
                            return true;

                        case DragEvent.ACTION_DROP:
                            //Get the subscript of the drag picture
                            String dragVal = dragEvent.getClipData().getItemAt(0).getText().toString();
                            int index = Integer.parseInt(dragVal);
                            //Add and replace operations
                            replace(index,thumViewHolder.getAdapterPosition());
                            return true;

                        case DragEvent.ACTION_DRAG_ENDED:
                            return true;
                    }
                    return false;
                }
            });
        }
    }

    /**
     * Picture replacement
     * @param index
     * @param adapterPosition
     */
    private void replace(int index, int adapterPosition) {
        if (index > 0 && index < questions.size() + 1 && adapterPosition < questions.size() + 1) {//If the subscript is in the title, do not deal with it.
            return;
        }else if (index > 0 && index < questions.size() + 1 && adapterPosition > questions.size() + 1) {//Drag from the Dry to the Answer Interface
            answers.set(adapterPosition - questions.size() - 2,questions.get(index - 1));
        }else if (index > questions.size() + 1 && adapterPosition > questions.size() + 1){//Exchange in Answer Interface
            String url = answers.get(index - questions.size() - HEADER_COUNT);
            answers.set(index - questions.size() - HEADER_COUNT,answers.get(adapterPosition - questions.size() - HEADER_COUNT));
            answers.set(adapterPosition - questions.size() - HEADER_COUNT,url);
        }else{
            answers.set(index - questions.size() - HEADER_COUNT,"");
        }
        notifyDataSetChanged();
    }

    @Override
    public int getItemCount() {
        return questions.size() + answers.size() + HEADER_COUNT;//Total quantity
    }

    //Title ViewHolder
    class TitleViewHolder extends RecyclerView.ViewHolder{
        private TextView title;
        public TitleViewHolder(View itemView) {
            super(itemView);
            title = (TextView) itemView;
        }
    }
    //Stem and Answer ViewHolder
    class ThumViewHolder extends RecyclerView.ViewHolder{
        private ImageView answerImage;
        private TextView orderId;
        public ThumViewHolder(View itemView) {
            super(itemView);
            answerImage = (ImageView) itemView.findViewById(R.id.image);
            orderId = (TextView) itemView.findViewById(R.id.num);
        }
    }
}

Concluding remarks

At this point, the project requirements have been realized, in fact, it is very simple, you will understand the principle of it at a glance. This is my second article. I hope the quality of my article will be improved. Here's a reminder: read more books, read more newspapers, eat less snacks and sleep more.

Topics: Android xml encoding Mobile