Introduction

You might wonder at some time in a project how you could reorder a list efficiently ?
Using a Drag & Drop list is one of them ! It’s a « fun » and smart way to achieve this goal.
You might also use a « select position in list » popup or some  up/down arrows but honestly it’s not ergonomic
especially when you scroll looooong lists.

KayNote Drag & Drop list inside a view pager indicator

KayNote Drag & Drop list inside a view pager indicator

To achieve this feature, we’ll use a very good library developed by Carl Bauer and its contributors.
To figure out what could be done with this lib, you might want to check out :

https://play.google.com/store/apps/details?id=com.mobeta.android.demodslv

There are 2 approaches:

  1. using standard Android library build process
  2. using standard Maven library build system

I’m fine with both, if you aren’t used to build maven projects ( and especially Android one),
you might prefer to go for the first one ! However if you’re going to manage a serious project for your company… you should take a look at it 🙂

The requirements ( downloads )

You’ll find sources of the library here :
https://github.com/bauerca/drag-sort-listview

You can use the project / lib directly although as usual, I have my own version of the lib premade for Eclipse to be import and setup easily.
It contains just the library and nothing else. (Don’t worry I just rewrote the pom.xml to work out of the box and included .project etc)

my version of the lib ( based on 0.6.0 )

I personally also used ViewPagerIndicator which quite handy to show the view pager to users and setup a « tab style » interface.
This library is from Jake Wharton and is available here :

https://github.com/JakeWharton/Android-ViewPagerIndicator

just as for drag and drop list library, you can get my version :

my version of the lib ( based on 2.4.1 )

if you want to use Maven in Eclipse with Android you might want to read :

https://kayrnt.wordpress.com/2012/11/02/520/

IDE & Project Setup

Importing the project :

  • If you don’t want Maven :
  1. File -> Import -> Existing Android Code Into Workspace
  2. select the folder containing the extracted project, and Eclipse should detect it. Just import it ( and if required copy to workspace )
  3. Everything should compile fine, else try to clean & rebuild
  • If you have maven :
  1. I guess you already have installed M2E & M2E-Android, so just  File -> Import -> Existing Maven project Into Workspace
  2. select the folder containing the extracted project and the pom.xml should be detected.  Just import it ( and if required copy to workspace )
  3. Everything should compile fine, else try to make a run with “clean install” as goal
  4. Remember to keep the project open into your workspace if you want to use feature relying on « R » from your libs ( like style, strings… )
  5. Here is the pom.xml, I use to make an APK with Maven :  KayNote-example-pom.xml

Add the project as a library to your project

Add the drag & drop library project as an Android library ( right click on project -> properties -> Android -> Add… )

Just do the exact same thing for the View Pager Indicator Library if you need it

Full Source of the example :

https://github.com/Kayrnt/DragAndDropPagerDemo/

 

« Coding time »

First, we’ll use a FragmentActivity as your « ViewPagerActivity », in this activity, you’ll be focusing on view pager setup :

public class MyViewPagerActivity extends FragmentActivity {
NotesFragmentAdapter mAdapter;
TitlePageIndicator indicator;
ViewPager mPager;
@Override
  protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager);
mPager = (ViewPager)findViewById(R.id.pager);
indicator = (TitlePageIndicator)findViewById(R.id.indicator);
mAdapter = new MyFragmentAdapter(this, mPager, getSupportFragmentManager());
indicator.setTextColor(Color.BLACK);
indicator.setSelectedColor(getResources().getColor(R.color.ics_blue));
indicator.setViewPager(mPager);
indicator.setOnPageChangeListener(mAdapter);
  }
}

You’ll notice that I changed some color to match my theme but you’re free to customize yours 😉

You might have noticed the « MyFragmentAdapter » which is the adapter that will hold the fragments which contains the drag and drop lists.

I use titles as « ids » for fragments so that I know what my drag and drop lists will show but you can use whatever you want as long as you know how to use it.
It can be quite surprising but remember that I used this method for « KayNote » and « titles » are in fact « types » of notes so I use them to query the correct notes.

The fragment pager adapter, I use :

public class MyFragmentAdapter extends FragmentPagerAdapter {
public ArrayList<String> titles;
private final Context mContext;
private final ViewPager mViewPager;
public MyFragmentAdapter(Activity activity, ViewPager pager, FragmentManager fm) {
super(fm);
mContext = activity;
mViewPager = pager;
mViewPager.setAdapter(this);
}

public void setupTitles() {
types = DBRequests.getTitles(mContext); //This a DB request to retrieve the name of fragment titles if you use view pager indicator
}

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

@Override
public Fragment getItem(int position) {
return new MyFragment(titles.get(position));
}

@Override
public CharSequence getPageTitle(int position) {
return titles.get(position % titles.size()).toUpperCase();
}

}

The next step is to create the Fragment that will be used in the View Pager.
In this tutorial, I’m using a Cursor adapter because I find it convient for using SQLite data.
However, you’ll find other pre-made adapters in the library if you want to use a simple ArrayList to populate your list !

public class DragFragment extends Fragment {
DragDropNoteAdapter adapter;
private DragSortListView mDslv;
private DragSortController mController;
CursorLoader loader;
public int dragStartMode = DragSortController.ON_DOWN;
public boolean sortEnabled = true;
public boolean dragEnabled = true;
public String title;
public DragFragment(String title) {
this.title = title;
}
public DragFragment() {}
/** Called when the activity is first created. */
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(container == null) {
return null;
}
return mDslv = (DragSortListView) getActivity().getLayoutInflater().inflate(R.layout.dslv_fragment_main, null);
}
@Override
public void onResume() {
super.onResume();
setupUI();
}
private void setupUI() {
mController = new DragSortController(mDslv);
mController.setDragHandleId(R.id.drag_handle);
mController.setSortEnabled(sortEnabled);
mController.setDragInitMode(dragStartMode);
mDslv.setFloatViewManager(mController);
mDslv.setOnTouchListener(mController);
mDslv.setDragEnabled(dragEnabled);
if(title != null) {
loader = new CursorLoader(getActivity(),MyProvider.CONTENT_URI, null, DBHelper.TYPE + " =  ?" , new String[] {title},  null);
}
adapter = new DragDropAdapter(getActivity(), R.layout.list_item_handle_left, loader, null);
mDslv.setAdapter(adapter);
}
}

Fine ! One of the last thing to do is to deal with the adapter for the drag & drop list.
Note that we use bindView to setup views instead of usual « getView ».

public class DragDropNoteAdapter extends ResourceDragSortCursorAdapter {
int contentColumn;
int idColumn;
CursorLoader loader;
Activity activity;

public DragDropNoteAdapter(Activity activity, int layout, CursorLoader loader, int flags) {
super(activity, layout, loader.loadInBackground(), flags);
this.loader = loader;
contentColumn = mCursor.getColumnIndex(DBHelper.CONTENT); //the column in my DB table that refers to the content of my list item
this.activity = activity;
}

@Override
public void bindView(View view, final Context context, Cursor cursor) {
final TextView tContent = (TextView) view.findViewById(R.id.text);
final String content = cursor.getString(contentColumn);
tContent.setText(content);
}

}

Finally the layouts in XML, be careful and don’t forget to add your application package identifier in this one :

dslv_fragment_main.xml

<?xml version="1.0" encoding="utf-8"?>
<com.mobeta.android.dslv.DragSortListView
   xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:dslv="http://schemas.android.com/apk/res/com.myapp.example"
    android:id="@+id/noteList"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="3dp"
    android:layout_margin="3dp"
    android:dividerHeight="2dp"
    dslv:drag_enabled="true"
    dslv:collapsed_height="2dp"
    dslv:drag_scroll_start="0.33"
    dslv:max_drag_scroll_speed="0.5"
    dslv:float_alpha="0.6"
    dslv:slide_shuffle_speed="0.3"
    dslv:track_drag_sort="false"
    dslv:use_default_controller="false" />

For the  drag & drop item, I used a similar layout to the examples of the library :

list_item_handle_left.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="50dp"
  android:orientation="horizontal">
  <ImageView
    android:id="@id/drag_handle"
    android:background="@drawable/drag"
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_weight="0" />
  <TextView
    android:id="@+id/text"
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_weight="1"
    android:ellipsize="end"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:gravity="center_vertical"
    android:paddingLeft="8dp" />
</LinearLayout>

Note that I used a drawable « drag » that is the representation of a « handle » which is the layout you drag & drop to move the item.
You can find one in the library sample ( or simply make your own ).

Finally I used the standard the View Pager Indicator  layout provided by Jake Wharton under Apache 2.0 :

viewpager.xml

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

    <com.viewpagerindicator.TitlePageIndicator
        android:id="@+id/indicator"
        android:padding="10dip"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        />
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        /> 
</LinearLayout>

Of course, don’t forget your Activity declaration in the Android Manifest :

<activity
            android:name=".MyViewPagerActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

</activity>

As a reminder, you can find a full example of this concept here :
https://github.com/Kayrnt/DragAndDropPagerDemo/

Hopefully… you’re done with a first « simple » version of this !

However if you plan to do use it in a real app, you’ll probably want to save position or use some listeners on page selections !
I’ll come back on the topic in a second part.

If you are waiting « eagerly » on that subject don’t miss to drop a comment so that I feel like it’s worthy 😉
( you might also report mistakes, since I’m pretty sure there are some 🙂 )

Publicités