Gmail like – Flip animated Multi selection list view with action mode
Android Gmail App has a very beautiful and smooth multi selection list, where you can select multiple items and perform an action over the selected items.
Here in this tutorial we are going to create a similar effect on custom list where we have a image and a title and on-click of the image it gets checked with a flip animation.
Also if you are new for creating a custom list please go through my previous tutorials:
http://techiedreams.com/android-simple-custom-list-views-with-examples/
Note : In this tutorial i used ABS(Actionbar Sherlock) for the Actionbar and Actionmode to support older versions also. Please find the library in the download package import it and add it to the project.
Here’s how the final result looks like:
First thing prior jumping in to this tutorial you should know how this works.
1. We flip the image half way (to middle).
2. End of the animation we now have image view rotated 90° we can imagine it as a thin line.
3. Now its time to change the image to inverse of the previous (Ex: if unchecked image is showing we replace it with checked image).
4. Now we run the second animation to rotate it from middle to normal. Done!
Hope you gone thorough my custom listview tutorial and have the custom list running, now
1. Create two anim files in the ‘anim’ folder of resources.
a) to_middle.xml :
1
2
3
4
5
6
7
8
9
|
<?xml version=“1.0” encoding=“utf-8”?>
<scale xmlns:android=“http://schemas.android.com/apk/res/android”
android:duration=“200”
android:fromXScale=“1.0”
android:fromYScale=“1.0”
android:pivotX=“50%”
android:pivotY=“50%”
android:toXScale=“0.0”
android:toYScale=“1.0” />
|
b) from_middle.xml :
1
2
3
4
5
6
7
8
9
|
<?xml version=“1.0” encoding=“utf-8”?>
<scale xmlns:android=“http://schemas.android.com/apk/res/android”
android:duration=“200”
android:fromXScale=“1.0”
android:fromYScale=“1.0”
android:pivotX=“50%”
android:pivotY=“50%”
android:toXScale=“0.0”
android:toYScale=“1.0” />
|
Now Lets jump in to the Main list activity where we initialize the Adapter and set our list view. Here is how the MainActivity looks like:
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
import java.util.ArrayList;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
public class MainActivity extends SherlockActivity implements OnItemClickListener {
ListView lvMyList;
ArrayList<MyListItem> listItems = new ArrayList<MyListItem>();
ActionBar actionBar;
MyListAdapter myListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvMyList = (ListView) findViewById(R.id.lvMyList);
lvMyList.setOnItemClickListener(this);
for (int i = 0; i < 20; i++) {
MyListItem item = new MyListItem();
item.setTitle(“Sample Title “ + (i + 1));
listItems.add(item);
}
myListAdapter = new MyListAdapter(this, listItems);
lvMyList.setAdapter(myListAdapter);
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// show description
}
public static final class AnActionModeOfEpicProportions implements ActionMode.Callback {
Context ctx;
public AnActionModeOfEpicProportions(Context ctx) {
this.ctx = ctx;
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.add(“Delete”).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(“Archive”).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(“Mark unread”).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(“Move”).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(“Remove star”).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Toast toast = null;
ArrayList<MyListItem> selectedListItems = new ArrayList<MyListItem>();
StringBuilder selectedItems = new StringBuilder();
// get items selected
for (MyListItem i : ((MainActivity) ctx).myListAdapter.mailList) {
if (i.isChecked()) {
selectedListItems.add(i);
selectedItems.append(i.getTitle()).append(“, “);
}
}
if (item.getTitle().equals(“Delete”)) {
// Delete
toast = Toast.makeText(ctx, “Delete: “ + selectedItems.toString(), Toast.LENGTH_SHORT);
} else if (item.getTitle().equals(“Archive”)) {
// Archive
toast = Toast.makeText(ctx, “Archive: “ + selectedItems.toString(), Toast.LENGTH_SHORT);
} else if (item.getTitle().equals(“Mark unread”)) {
// Mark unread
toast = Toast.makeText(ctx, “Mark unread: “ + selectedItems.toString(), Toast.LENGTH_SHORT);
} else if (item.getTitle().equals(“Move”)) {
// Move
toast = Toast.makeText(ctx, “Move: “ + selectedItems.toString(), Toast.LENGTH_SHORT);
} else if (item.getTitle().equals(“Remove star”)) {
// Remove star
toast = Toast.makeText(ctx, “Remove star: “ + selectedItems.toString(), Toast.LENGTH_SHORT);
}
if (toast != null) {
toast.show();
}
mode.finish();
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// Action mode is finished reset the list and ‘checked count’ also
// set all the list items checked states to false
((MainActivity) ctx).myListAdapter.checkedCount = 0;
((MainActivity) ctx).myListAdapter.isActionModeShowing = false;
// set list items states to false
for (MyListItem item : ((MainActivity) ctx).listItems) {
item.setIsChecked(false);
}
((MainActivity) ctx).myListAdapter.notifyDataSetChanged();
Toast.makeText(ctx, “Action mode closed”, Toast.LENGTH_SHORT).show();
}
}
}
|
Now, Lets jump in to the meat of the Application, The Adapter class:
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.actionbarsherlock.view.ActionMode;
public class MyListAdapter extends BaseAdapter {
Context context;
ArrayList<MyListItem> mailList;
Animation animation1;
Animation animation2;
ImageView ivFlip;
int checkedCount = 0;
ActionMode mMode;
boolean isActionModeShowing;
public MyListAdapter(Context context, ArrayList<MyListItem> mailList) {
this.context = context;
this.mailList = mailList;
animation1 = AnimationUtils.loadAnimation(context, R.anim.to_middle);
animation2 = AnimationUtils.loadAnimation(context, R.anim.from_middle);
isActionModeShowing = false;
}
@Override
public int getCount() {
return mailList.size();
}
@Override
public MyListItem getItem(int position) {
return mailList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@SuppressWarnings(“deprecation”)
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
LayoutInflater inflater = (LayoutInflater) context.getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.selectBox = (ImageView) convertView.findViewById(R.id.selectBox);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
holder.title.setText(getItem(position).getTitle());
holder.selectBox.setTag(“” + position);
holder.selectBox.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ivFlip = (ImageView) v;
ivFlip.clearAnimation();
ivFlip.setAnimation(animation1);
ivFlip.startAnimation(animation1);
setAnimListners(mailList.get(Integer.parseInt(v.getTag().toString())));
}
});
if (mailList.get(position).isChecked()) {
holder.selectBox.setImageResource(R.drawable.cb_checked);
convertView.setBackgroundColor(context.getResources().getColor(R.color.list_highlight));
} else {
holder.selectBox.setImageResource(R.drawable.cb_unchecked);
convertView.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.list_selector));
}
return convertView;
}
private void setAnimListners(final MyListItem curMail) {
AnimationListener animListner;
animListner = new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
if (animation == animation1) {
if (curMail.isChecked()) {
ivFlip.setImageResource(R.drawable.cb_unchecked);
} else {
ivFlip.setImageResource(R.drawable.cb_checked);
}
ivFlip.clearAnimation();
ivFlip.setAnimation(animation2);
ivFlip.startAnimation(animation2);
} else {
curMail.setIsChecked(!curMail.isChecked());
setCount();
setActionMode();
}
}
// Set selected count
private void setCount() {
if (curMail.isChecked()) {
checkedCount++;
} else {
if (checkedCount != 0) {
checkedCount—;
}
}
}
// Show/Hide action mode
private void setActionMode() {
if (checkedCount > 0) {
if (!isActionModeShowing) {
mMode = ((MainActivity) context).startActionMode(new MainActivity.AnActionModeOfEpicProportions(context));
isActionModeShowing = true;
}
} else if (mMode != null) {
mMode.finish();
isActionModeShowing = false;
}
// Set action mode title
if (mMode != null)
mMode.setTitle(String.valueOf(checkedCount));
notifyDataSetChanged();
}
@Override
public void onAnimationRepeat(Animation arg0) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animation arg0) {
// TODO Auto-generated method stub
}
};
animation1.setAnimationListener(animListner);
animation2.setAnimationListener(animListner);
}
static class ViewHolder {
TextView title;
ImageView selectBox;
}
}
|
MyListItem is an Entity where it holds the list items Name and Checked state. Here how it looks:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class MyListItem {
private String name;
private boolean isChecked = false;
public boolean isChecked() {
return isChecked;
}
public void setIsChecked(boolean isChecked) {
this.isChecked = isChecked;
}
public String getTitle() {
return name;
}
public void setTitle(String name) {
this.name = name;
}
}
|