Sunday, January 11, 2015

Improved Drag and Drop items between ListView

Last post show my first try "Drag and Drop items between ListView". The source and destination of the Drag and Drop operation is hard coded and fixed. It's too un-flexible! It is improved here to provide more flexible approach.


In order to get the associated ListView from any LinearLayout to be drop, I create a new class LinearLayoutListView extends LinearLayout, associated with a ListView.

LinearLayoutListView.java
package com.example.androidimageviewlist;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.ListView;

public class LinearLayoutListView extends LinearLayout {
 
 ListView listView;

 public LinearLayoutListView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutListView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutListView(Context context, AttributeSet attrs,
   int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  // TODO Auto-generated constructor stub
 }

 public void setListView(ListView lv){
  listView = lv;
 }

}

Modify /res/layout/activity_main.xml, to use <com.example.androidimageviewlist.LinearLayoutListView>. And also display three to show the flexibility of the new approach.
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="4dp"
    tools:context="com.example.androidimageviewlist.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@android:color/background_dark"
        android:orientation="horizontal" >

        <com.example.androidimageviewlist.LinearLayoutListView
            android:id="@+id/pane1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutListView>

        <com.example.androidimageviewlist.LinearLayoutListView
            android:id="@+id/pane2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutListView>
        
        <com.example.androidimageviewlist.LinearLayoutListView
            android:id="@+id/pane3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutListView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/prompt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            android:gravity="bottom"
            android:textColor="@android:color/white" />
    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidimageviewlist;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 //items stored in ListView
 public class Item {
  Drawable ItemDrawable;
  String ItemString;
  Item(Drawable drawable, String t){
   ItemDrawable = drawable;
   ItemString = t;
  }
 }
 
 //objects passed in Drag and Drop operation
 class PassObject{
  View view;
  Item item;
  List<Item> srcList;
  
  PassObject(View v, Item i, List<Item> s){
   view = v;
   item = i;
   srcList = s;
  }
 }
 
 static class ViewHolder {
  ImageView icon;
  TextView text; 
 }

 public class ItemsListAdapter extends BaseAdapter {
  
  private Context context;
  private List<Item> list;

  ItemsListAdapter(Context c, List<Item> l){
   context = c;
   list = l;
  }

  @Override
  public int getCount() {
   return list.size();
  }

  @Override
  public Object getItem(int position) {
   return list.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View rowView = convertView;
   
      // reuse views
      if (rowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       rowView = inflater.inflate(R.layout.row, null);

       ViewHolder viewHolder = new ViewHolder();
       viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
       viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
       rowView.setTag(viewHolder); 
      }

      ViewHolder holder = (ViewHolder) rowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);
      holder.text.setText(list.get(position).ItemString);

      return rowView;
  }
  
  public List<Item> getList(){
   return list;
  }
 }

 List<Item> items1, items2, items3;
 ListView listView1, listView2, listView3;
 ItemsListAdapter myItemsListAdapter1, myItemsListAdapter2, myItemsListAdapter3;
 LinearLayoutListView area1, area2, area3;
 TextView prompt;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  listView1 = (ListView)findViewById(R.id.listview1);
  listView2 = (ListView)findViewById(R.id.listview2);
  listView3 = (ListView)findViewById(R.id.listview3);
  
  area1 = (LinearLayoutListView)findViewById(R.id.pane1);
  area2 = (LinearLayoutListView)findViewById(R.id.pane2);
  area3 = (LinearLayoutListView)findViewById(R.id.pane3);
  area1.setOnDragListener(myOnDragListener);
  area2.setOnDragListener(myOnDragListener);
  area3.setOnDragListener(myOnDragListener);
  area1.setListView(listView1);
  area2.setListView(listView2);
  area3.setListView(listView3);
  
  initItems();
  myItemsListAdapter1 = new ItemsListAdapter(this, items1);
  myItemsListAdapter2 = new ItemsListAdapter(this, items2);
  myItemsListAdapter3 = new ItemsListAdapter(this, items3);
  listView1.setAdapter(myItemsListAdapter1);
  listView2.setAdapter(myItemsListAdapter2);
  listView3.setAdapter(myItemsListAdapter3);
  
  //Auto scroll to end of ListView
  listView1.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView2.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView3.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

  listView1.setOnItemClickListener(listOnItemClickListener);
  listView2.setOnItemClickListener(listOnItemClickListener);
  listView3.setOnItemClickListener(listOnItemClickListener);
  
  listView1.setOnItemLongClickListener(myOnItemLongClickListener);
  listView2.setOnItemLongClickListener(myOnItemLongClickListener);
  listView3.setOnItemLongClickListener(myOnItemLongClickListener);
  
  prompt = (TextView) findViewById(R.id.prompt);
  // make TextView scrollable
  prompt.setMovementMethod(new ScrollingMovementMethod());
  //clear prompt area if LongClick
  prompt.setOnLongClickListener(new OnLongClickListener(){
   
   @Override
   public boolean onLongClick(View v) {
    prompt.setText("");
    return true; 
   }});

 }
 
 OnItemLongClickListener myOnItemLongClickListener = new OnItemLongClickListener(){

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   Item selectedItem = (Item)(parent.getItemAtPosition(position));
   
   ItemsListAdapter associatedAdapter = (ItemsListAdapter)(parent.getAdapter());
      List<Item> associatedList = associatedAdapter.getList();
   
   PassObject passObj = new PassObject(view, selectedItem, associatedList);
   
   
   ClipData data = ClipData.newPlainText("", "");
   DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
   view.startDrag(data, shadowBuilder, passObj, 0);
   
   return true;
  }
  
 };
 
 OnDragListener myOnDragListener = new OnDragListener() {

  @Override
  public boolean onDrag(View v, DragEvent event) {
   String area;
   if(v == area1){
    area = "area1"; 
   }else if(v == area2){
    area = "area2"; 
   }else if(v == area3){
    area = "area3"; 
   }else{
    area = "unknown"; 
   }
   
   switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
     prompt.append("ACTION_DRAG_STARTED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_ENTERED:
     prompt.append("ACTION_DRAG_ENTERED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_EXITED:
     prompt.append("ACTION_DRAG_EXITED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DROP:
     prompt.append("ACTION_DROP: " + area  + "\n");

     PassObject passObj = (PassObject)event.getLocalState();
     View view = passObj.view;
     Item passedItem = passObj.item;
     List<Item> srcList = passObj.srcList;
     ListView oldParent = (ListView)view.getParent();
     ItemsListAdapter srcAdapter = (ItemsListAdapter)(oldParent.getAdapter());
     
     LinearLayoutListView newParent = (LinearLayoutListView)v;
     ItemsListAdapter destAdapter = (ItemsListAdapter)(newParent.listView.getAdapter());
        List<Item> destList = destAdapter.getList();
     
     if(removeItemToList(srcList, passedItem)){
      addItemToList(destList, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
     
     break;
      case DragEvent.ACTION_DRAG_ENDED:
       prompt.append("ACTION_DRAG_ENDED: " + area  + "\n");   
      default:
       break;    
   }
      
   return true;
  }
  
 };
 
 OnItemClickListener listOnItemClickListener = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   Toast.makeText(MainActivity.this, 
     ((Item)(parent.getItemAtPosition(position))).ItemString, 
     Toast.LENGTH_SHORT).show();
  }
  
 };

 private void initItems(){
  items1 = new ArrayList<Item>();
  items2 = new ArrayList<Item>();
  items3 = new ArrayList<Item>();
  
  TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
  TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);
  
  for(int i=0; i<arrayDrawable.length(); i++){
   Drawable d = arrayDrawable.getDrawable(i);
   String s = arrayText.getString(i);
   Item item = new Item(d, s);
   items1.add(item);
  }
  
  arrayDrawable.recycle();
  arrayText.recycle();
 }
 
 private boolean removeItemToList(List<Item> l, Item it){
  boolean result = l.remove(it);
  return result;
 }
 
 private boolean addItemToList(List<Item> l, Item it){
  boolean result = l.add(it);
  return result;
 }

}

/res/layout/row.xml and /res/values/arrays.xml, refer to previous post of "Custom ListView with ImageView".

To use Drag and Drop on your app, you have to modify AndroidManifest.xml to set android:minSdkVersion="11".

download filesDownload the files.


In this exercise, it drag a ListView item, drop on a LinearLayout, then add the item to the associated ListView, always on bottom. Not drop on ListView actually.

In this exercise, "Drag and Drop items between ListView, with OnDragListener on ListView cells", another OnDragListener (ItemOnDragListener) implemented for rowView of ListView items, such that we can actually drop on ListView items, and insert in the previous position.

2 comments:

Unknown said...

hi

Just i want to know how we can resize the listview upto where data is
please help me out

Erik said...

hello Atul Dhanuka,

Do you means visually resize the items? I think you have to change the view returned by getView() of ItemsListAdapter.

You can try to change something in /res/layout/row.xml to make it bigger.