We can use swipe gestures within each ListView item to allow the user to perform a particular action or change the UI of the ListView row item.
This tutorial will help you to implement a custom ListView which initially display some details in TextViews and; when the user swipes from Right to Left on a list item; few ImageButtons appear from the right direction and the details slide out to the left.
I will try do this without the usage of any external libraries or APIs. Using the stock widgets(Views) of android and some simple animation using XMLs.
How the ListView will look and behave?
How the project is organized?
Create a new project and create a new xml file named list_row_item.xml in the /res/layout folder.
list_row_item.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeightLarge" > <ImageView android:id="@+id/userimage" android:layout_height="80dp" android:layout_width="60dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:layout_margin="10dp" android:src="@drawable/ic_launcher" android:contentDescription="@string/app_name" /> <LinearLayout android:id="@+id/layout_front" android:layout_height="match_parent" android:layout_width="match_parent" android:layout_toRightOf="@id/userimage" android:layout_centerVertical="true" android:gravity="center_vertical" android:orientation="vertical" > <TextView android:id="@+id/name" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="User Name" android:textAppearance="?android:attr/textAppearanceMedium" /> <TextView android:id="@+id/detail1" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="user detail 1" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView android:id="@+id/detail2" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="user detail 2" android:textAppearance="?android:attr/textAppearanceSmall" /> </LinearLayout> <RelativeLayout android:id="@+id/layout_back" android:layout_height="match_parent" android:layout_width="match_parent" android:layout_toRightOf="@id/userimage" android:paddingLeft="30dp" android:paddingRight="30dp" android:layout_centerVertical="true" android:visibility="gone" android:gravity="center_vertical" > <ImageView android:id="@+id/btn1" android:layout_height="wrap_content" android:layout_width="wrap_content" android:padding="5dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true" android:src="@drawable/phone_icon" android:contentDescription="@string/app_name" /> <ImageView android:id="@+id/btn2" android:layout_height="wrap_content" android:layout_width="wrap_content" android:padding="5dp" android:layout_centerInParent="true" android:layout_centerVertical="true" android:src="@drawable/message_icon" android:contentDescription="@string/app_name" /> <ImageView android:id="@+id/btn3" android:layout_height="wrap_content" android:layout_width="wrap_content" android:padding="5dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:src="@drawable/email_icon" android:contentDescription="@string/app_name" /> </RelativeLayout> </RelativeLayout> |
Now create a folder named anim in the /res folder of your project. As mentioned earlier this folder will be used to keep the animation files. After that you need to create the following four XML files in the /res/anim folder:
in_from_left.xml
1 2 3 4 5 | <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="-100%p" android:toXDelta="0" android:duration="800" /> |
in_from_right.xml
1 2 3 4 5 | <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="100%p" android:toXDelta="0" android:duration="800" /> |
out_to_left.xml
1 2 3 4 5 | <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromXDelta="0" android:toXDelta="-100%p" android:duration="800" /> |
out_to_right.xml
1 2 3 4 5 6 | <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="800" android:fromXDelta="0" android:interpolator="@android:anim/linear_interpolator" android:toXDelta="100%p" /> |
MyListAdapter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | package com.example.myswipelistview; import android.content.Context; import android.support.v4.view.GestureDetectorCompat; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.RelativeLayout; import android.widget.TextView; public class MyListAdapter extends BaseAdapter { private Context ctx; private String[] names; public MyListAdapter(Context ctx, String[] data) { this.ctx = ctx; this.names = data; } static class ViewHolder { RelativeLayout container; TextView userName; GestureDetectorCompat mDetector; } @Override public int getCount() { return names.length; } @Override public Object getItem(int arg0) { return names[arg0]; } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(ctx).inflate( R.layout.list_row_item, null); final ViewHolder holder = new ViewHolder(); holder.container = (RelativeLayout) convertView .findViewById(R.id.container); holder.userName = (TextView) convertView.findViewById(R.id.name); holder.mDetector = new GestureDetectorCompat(ctx, new MyGestureListener(ctx, convertView)); convertView.setTag(holder); } final ViewHolder holder = (ViewHolder) convertView.getTag(); holder.userName.setText(names[position]); holder.container.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { holder.mDetector.onTouchEvent(event); return true; } }); return convertView; } } |
MyGestureListener.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | package com.example.myswipelistview; import android.content.Context; import android.util.Log; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.LinearLayout; import android.widget.RelativeLayout; public class MyGestureListener extends SimpleOnGestureListener { private static final int MIN_DISTANCE = 50; private static final String TAG = "MyGestureListener"; private RelativeLayout backLayout; private LinearLayout frontLayout; private Animation inFromRight,outToRight,outToLeft,inFromLeft; public MyGestureListener(Context ctx,View convertView) { backLayout = (RelativeLayout) convertView.findViewById(R.id.layout_back); frontLayout = (LinearLayout) convertView.findViewById(R.id.layout_front); inFromRight = AnimationUtils.loadAnimation(ctx, R.anim.in_from_right); outToRight = AnimationUtils.loadAnimation(ctx, R.anim.out_to_right); outToLeft = AnimationUtils.loadAnimation(ctx, R.anim.out_to_left); inFromLeft = AnimationUtils.loadAnimation(ctx, R.anim.in_from_left); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { float diffX = e2.getX() - e1.getX(); float diffY = e2.getY() - e1.getY(); if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) > MIN_DISTANCE) { if(diffX<0){ Log.v(TAG, "Swipe Right to Left"); if(backLayout.getVisibility()==View.GONE){ frontLayout.startAnimation(outToLeft); backLayout.setVisibility(View.VISIBLE); backLayout.startAnimation(inFromRight); frontLayout.setVisibility(View.GONE); } }else{ Log.v(TAG, "Swipe Left to Right"); if(backLayout.getVisibility()!=View.GONE){ backLayout.startAnimation(outToRight); backLayout.setVisibility(View.GONE); frontLayout.setVisibility(View.VISIBLE); frontLayout.startAnimation(inFromLeft); } } } } return true; } } |
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.example.myswipelistview; import android.app.Activity; import android.os.Bundle; import android.widget.ListView; public class MainActivity extends Activity { ListView listView; String[] names={"User 1","User 2","User 3","User 4","User 5","User 6"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.listView1); MyListAdapter adapter = new MyListAdapter(this,names); listView.setAdapter(adapter); } } |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <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/listView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:divider="#cccccc" android:dividerHeight="2dp" > </ListView> </RelativeLayout> |
Feel free to post your comments, queries and suggestions. Cheers... ;)