Making Swipeable ListView in Android

     Sometimes, we find out that sliding ListView (swipeable ListView) is available in many apps like Gmail, Contact,...This design is helpful in quickly actions with each ListView element (edit, delete, archive,...).
    By searching on Internet, we found that there are a lot of libraries use for making this design style. In this tutorial post, I will present AndroidSwipeLayout, it is powerful library by making swipe view, not only ListView like another. This is a project's DEMO VIDEO :


    Okey, let's start a new Android Project with min-sdk 15.

Building swipeable layouts

    First, open your app/build.gradle file and adding this line to use library:
compile "com.daimajia.swipelayout:library:1.2.0@aar"
    In each ListView item layout, setting SwipeLayout as root:
item_listview.xml
<?xml version="1.0" encoding="utf-8"?>
<com.daimajia.swipe.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <!-- Bottom View Start-->
    <LinearLayout
        android:id="@+id/bottom_wrapper"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:orientation="horizontal">
 
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="#66ddff00">
 
            <ImageView
                android:id="@+id/edit_query"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/edit" />
        </RelativeLayout>
 
        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="#66FF3300">
 
            <ImageView
                android:id="@+id/delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@mipmap/delete" />
        </RelativeLayout>
 
 
    </LinearLayout>
    <!-- Bottom View End-->
 
    <!-- Surface View Start -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:padding="10dp">
 
        <TextView
            android:id="@+id/name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:minHeight="?android:attr/listPreferredItemHeightSmall"
            android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
            android:paddingRight="?android:attr/listPreferredItemPaddingRight"
            android:textAppearance="?android:attr/textAppearanceListItemSmall" />
    </LinearLayout>
    <!-- Surface View End -->
</com.daimajia.swipe.SwipeLayout>
    As you can see, SwipeLayout has 2 children ViewGroups, the first one is "Surface View (1)", which always visible and display view content, which be hidden and only visible when user swipe. The second one is "Bottom View (2)", which be hidden and only visible when user swipe.
    Creating a ListView header, also include SwipeLayout as root, too. It will display total number of ListView elements:
header_listview.xml
<?xml version="1.0" encoding="utf-8"?>
<com.daimajia.swipe.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
 
    <LinearLayout
        android:id="@+id/bottom_wrapper"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:background="#FF3300"
        android:orientation="horizontal">
 
        <TextView
            android:id="@+id/total"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:minHeight="?android:attr/listPreferredItemHeightSmall"
            android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
            android:paddingRight="?android:attr/listPreferredItemPaddingRight"
            android:textAppearance="?android:attr/textAppearanceListItemSmall" />
 
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#0000CC"
        android:padding="10dp">
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="@string/classmates"
            android:textAllCaps="true"
            android:textColor="@android:color/white"
            android:textStyle="bold" />
 
    </LinearLayout>
</com.daimajia.swipe.SwipeLayout>

Creating running Activity

    Defining a simple layout which include only a ListView for our activity like this:
activity_main.xml
<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=".MainActivity">
 
    <ListView
        android:id="@+id/list_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
 
</RelativeLayout>
    In programmatically code, with SwipeLayout, the first important option is "set show mode" for it. This line do this work:
swipeLayout.setShowMode(SwipeLayout.ShowMode.PullOut);
In this libary, showMode can be PullOut or LayDown.
   Moreover, like the library docs say, we can set "listener" for swiping action like this:
swipeLayout.addSwipeListener(new SwipeLayout.SwipeListener() {
            @Override
            public void onClose(SwipeLayout layout) {
                Log.i(TAG, "onClose");
            }

            @Override
            public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) {
                Log.i(TAG, "on swiping");
            }

            @Override
            public void onStartOpen(SwipeLayout layout) {
                Log.i(TAG, "on start open");
            }

            @Override
            public void onOpen(SwipeLayout layout) {
                Log.i(TAG, "the BottomView totally show");
            }

            @Override
            public void onStartClose(SwipeLayout layout) {
                Log.i(TAG, "the BottomView totally close");
            }

            @Override
            public void onHandRelease(SwipeLayout layout, float xvel, float yvel) {
                //when user's hand released.
            }
        });

Creating ListView adapter

     Like normal ListView, building an adapter based on ArrayAdapter or BaseAdapter. In this ListView, I added 2 buttons (Edit and Delete) on each row. In addition to the familiar methods, we must handling their events (edit or delete selected item):
private View.OnClickListener onEditListener(final int position, final ViewHolder holder) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               showEditDialog(position, holder);
            }
        };
    }

    /**
     * Editting confirm dialog
     * @param position
     * @param holder
     */
    private void showEditDialog(final int position, final ViewHolder holder) {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);

        alertDialogBuilder.setTitle("EDIT ELEMENT");
        final EditText input = new EditText(activity);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT);
        input.setText(friends.get(position));
        input.setLayoutParams(lp);
        alertDialogBuilder.setView(input);

        alertDialogBuilder
                .setCancelable(false)
                .setPositiveButton("OK",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                // get user input and set it to result edit text
                                friends.set(position, input.getText().toString().trim());

                                //notify data set changed
                                activity.updateAdapter();
                                holder.swipeLayout.close();
                            }
                        })
                .setNegativeButton("Cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                dialog.cancel();
                            }
                        });

        // create alert dialog and show it
        AlertDialog alertDialog = alertDialogBuilder.create();
        alertDialog.show();
    }

    private View.OnClickListener onDeleteListener(final int position, final ViewHolder holder) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                friends.remove(position);
                holder.swipeLayout.close();
                activity.updateAdapter();
            }
        };
    }
    By adding getView() method and use a ViewHolder class to get scrolling smoother, we have full code for this ListView adapter:
ListViewAdapter.java
package info.devexchanges.swipeableview;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.daimajia.swipe.SwipeLayout;

import java.util.List;

public class ListViewAdapter extends ArrayAdapter<String> {

    private MainActivity activity;
    private List<String> friends;


    public ListViewAdapter(MainActivity context, int resource, List<String> objects) {
        super(context, resource, objects);
        this.activity = context;
        this.friends = objects;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        LayoutInflater inflater = (LayoutInflater) activity
                .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        // If holder not exist then locate all view from UI file.
        if (convertView == null) {
            // inflate UI from XML file
            convertView = inflater.inflate(R.layout.item_listview, parent, false);
            // get all UI view
            holder = new ViewHolder(convertView);
            // set tag for holder
            convertView.setTag(holder);
        } else {
            // if holder created, get tag from view
            holder = (ViewHolder) convertView.getTag();
        }

        holder.name.setText(getItem(position));

        //handling buttons event
        holder.btnEdit.setOnClickListener(onEditListener(position, holder));
        holder.btnDelete.setOnClickListener(onDeleteListener(position, holder));

        return convertView;
    }

    private View.OnClickListener onEditListener(final int position, final ViewHolder holder) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               showEditDialog(position, holder);
            }
        };
    }

    /**
     * Editting confirm dialog
     * @param position
     * @param holder
     */
    private void showEditDialog(final int position, final ViewHolder holder) {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);

        alertDialogBuilder.setTitle("EDIT ELEMENT");
        final EditText input = new EditText(activity);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT);
        input.setText(friends.get(position));
        input.setLayoutParams(lp);
        alertDialogBuilder.setView(input);

        alertDialogBuilder
                .setCancelable(false)
                .setPositiveButton("OK",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                // get user input and set it to result edit text
                                friends.set(position, input.getText().toString().trim());

                                //notify data set changed
                                activity.updateAdapter();
                                holder.swipeLayout.close();
                            }
                        })
                .setNegativeButton("Cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                dialog.cancel();
                            }
                        });

        // create alert dialog and show it
        AlertDialog alertDialog = alertDialogBuilder.create();
        alertDialog.show();
    }

    private View.OnClickListener onDeleteListener(final int position, final ViewHolder holder) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                friends.remove(position);
                holder.swipeLayout.close();
                activity.updateAdapter();
            }
        };
    }

    private class ViewHolder {
        private TextView name;
        private View btnDelete;
        private View btnEdit;
        private SwipeLayout swipeLayout;

        public ViewHolder(View v) {
            swipeLayout = (SwipeLayout)v.findViewById(R.id.swipe_layout);
            btnDelete = v.findViewById(R.id.delete);
            btnEdit = v.findViewById(R.id.edit_query);
            name = (TextView) v.findViewById(R.id.name);

            swipeLayout.setShowMode(SwipeLayout.ShowMode.LayDown);
        }
    }
}
    In line 92 and 114, when finish editing or deleting process, we will call back to main activity and update adapter and views by updateAdapter() method. Put these line in activity code :
public void updateAdapter() {
        adapter.notifyDataSetChanged(); //update adapter
        totalClassmates.setText("(" + friendsList.size() + ")"); //update total friends in list
    }

Final activity code

    Getting data from text file in Asset folder and put it to an ArrayList, set ListView header (a SwipeLayout, too), set adapter then, we have full code:
MainActivity.java
package info.devexchanges.swipeableview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.daimajia.swipe.SwipeLayout;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private ArrayAdapter<String> adapter;
    private ArrayList<String> friendsList;
    private TextView totalClassmates;
    private SwipeLayout swipeLayout;

    private final static String TAG = MainActivity.class.getSimpleName();

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

        listView = (ListView)findViewById(R.id.list_item);

        friendsList = new ArrayList<>();
        getDataFromFile();
        setListViewHeader();
        setListViewAdapter();
    }

    private void getDataFromFile() {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(getAssets().open("classmates.txt"), "UTF-8"));

            // do reading, usually loop until end of file reading
            String line = reader.readLine();
            while (line != null && !line.equals("")) {
                line = reader.readLine();
                friendsList.add(line); // add line to array list
            }
        } catch (IOException e) {
            //log the exception
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void setListViewHeader() {
        LayoutInflater inflater = getLayoutInflater();
        View header = inflater.inflate(R.layout.header_listview, listView, false);
        totalClassmates = (TextView) header.findViewById(R.id.total);
        swipeLayout = (SwipeLayout)header.findViewById(R.id.swipe_layout);
        setSwipeViewFeatures();
        listView.addHeaderView(header);
    }

    private void setSwipeViewFeatures() {
        //set show mode.
        swipeLayout.setShowMode(SwipeLayout.ShowMode.PullOut);

        //add drag edge.(If the BottomView has 'layout_gravity' attribute, this line is unnecessary)
        swipeLayout.addDrag(SwipeLayout.DragEdge.Left, findViewById(R.id.bottom_wrapper));

        swipeLayout.addSwipeListener(new SwipeLayout.SwipeListener() {
            @Override
            public void onClose(SwipeLayout layout) {
                Log.i(TAG, "onClose");
            }

            @Override
            public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) {
                Log.i(TAG, "on swiping");
            }

            @Override
            public void onStartOpen(SwipeLayout layout) {
                Log.i(TAG, "on start open");
            }

            @Override
            public void onOpen(SwipeLayout layout) {
                Log.i(TAG, "the BottomView totally show");
            }

            @Override
            public void onStartClose(SwipeLayout layout) {
                Log.i(TAG, "the BottomView totally close");
            }

            @Override
            public void onHandRelease(SwipeLayout layout, float xvel, float yvel) {
                //when user's hand released.
            }
        });
    }

    private void setListViewAdapter() {
        adapter = new ListViewAdapter(this, R.layout.item_listview, friendsList);
        listView.setAdapter(adapter);

        totalClassmates.setText("(" + friendsList.size() + ")");
    }

    public void updateAdapter() {
        adapter.notifyDataSetChanged(); //update adapter
        totalClassmates.setText("(" + friendsList.size() + ")"); //update total friends in list
    }
}
     When app running, we have this output:

Conclusions

    By using an external lib, we can make a swipe view/list view easily.
    References:
    By searching on Internet, there are some similar libraries:

Share


Previous post
« Prev Post
Next post
Next Post »