Aug 23, 2014

Android: Contextual Action Bar for ListView Item Deletion using ActionBarSherlock

What is CAB?

The selection CAB is a temporary action bar that overlays your app’s current action bar while data is selected. It appears after the user long presses on a selectable data item. Select additional data items by touching them. It is recommended to use when you perform actions with selected data like plain text or data items from ListView or GridView components. The action mode is disabled and the contextual action bar disappears when the user deselects all items, presses the BACK button, or selects the checkmark button on the left side of the bar.

Implementing CAB for ListView item selection

  • If you’re using Android 3.0 or higher there is a detailed description of CAB setup on each selection method – whether contextual action should be performed on a single selected item or on a group of selected items.
  • Although it is available from Android 3.0 it is a good practice to use it in apps working on earlier API versions. If your app works on android version 2.x and higher the best approach is to use ActionBarSherlock library. It has its own contextual action bar implementation, which is easy to set up. But it doesn’t support the native ListView integration, so you need to control CAB’s lifecycle by yourself.
You can invoke contextual action mode based on one of the two events or both.
  1. The user selects a checkbox or similar UI component within the view.
  2. The user performs a long-click on the view.
This tutorial explains the second method.

In this tutorial we’ll see how to use Contextual Action Bar for ListView using ActionBarSherlock library where list items can be selected on long press gesture. Here, contextual action bar has delete menu item, which deletes the selected items from ListView.

Output


Android Project

Create an Android project and name it as CABDemo. Include ActionBarSherlock as a library project.

strings.xml

Open res/values/strings.xml and replace it with following content.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">CABDemo</string>
    <string name="hello_world">Hello world!</string>
    <string-array name="laptops">
        <item><span style="font-weight: bold; height: 13px;" id="z20986_4" class="z20986">Acer</span></item>
        <item><span style="font-weight: bold; height: 13px;" id="z20986_3" class="z20986">Asus</span></item>
        <item>Dell</item>
        <item>HCL</item>
        <item>HP</item>
        <item>IBM</item>
        <item>LG</item>
        <item>Sony</item>
        <item>Samsung</item>
    </string-array>
</resources>

strings_menu.xml

Create a new strings_menu.xml in res/values/ folder and copy the following content.
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="menu_settings">Settings</string>
    <string name="menu_add">Add</string>
    <string name="menu_rate">Rate</string>
    <string name="menu_share">Share</string>   
     
    <string name="menu_delete">Delete</string
</resources>

styles.xml

Open res/values/styles.xml and replace it with following content. Here, we use Action bar Sherlock theme.
1
2
3
4
5
6
7
<resources>
    <style name="AppBaseTheme" parent="Theme.Sherlock">
    </style>
    <style name="AppTheme" parent="AppBaseTheme">
    </style>
</resources>

Menu XML files

activity_main.xml

Create a new activity_main.xml in res/menu folder and copy the following content. This file defines action bar menu items.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    <item
        android:id="@+id/menu_add"
        android:orderInCategory="100"
        android:showAsAction="ifRoom|withText"
        android:title="@string/menu_add"
        android:icon="@android:drawable/ic_input_add"/>
     
      <item
        android:id="@+id/menu_rate"
        android:orderInCategory="200"
        android:showAsAction="ifRoom|withText"
        android:title="@string/menu_rate"
        android:icon="@android:drawable/btn_star_big_on"/>
       
         <item
        android:id="@+id/menu_share"
        android:orderInCategory="300"
        android:showAsAction="ifRoom|withText"
        android:title="@string/menu_share"
        android:icon="@android:drawable/ic_menu_share"/>
</menu>

context_menu.xml

Create a new context_main.xml in res/menu folder and copy the following content. This file defines menu items for contextual action bar. We define one menu item which is used to delete items from ListView.
1
2
3
4
5
6
7
8
9
    <item
        android:id="@+id/menu_delete"
        android:orderInCategory="100"
        android:showAsAction="ifRoom|withText"
        android:title="@string/menu_delete"
        android:icon="@android:drawable/ic_menu_delete"/>
</menu>

XML layout files

This Android contextual action bar example uses two layout files; one for displaying the main layout containing the ListView and other for defining the row layout for ListView item.

activity_main.xml

This application uses XML layout file (activity_main.xml) to display ListView.
Open activity_main.xml file in res/layout and copy the following content.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <ListView
        android:id="@+id/laptopList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="#323232"
        android:dividerHeight="1dp"
        android:scrollbars="none" >
    </ListView>
</LinearLayout>

list_item.xml

Create a new layout file list_item.xml in res/layout and copy the following content. For simplicity, this file contains only a TextView.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp">
     
    <TextView
        android:id="@+id/laptop_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000000"/>
</LinearLayout>

Create Bean class

Laptop.java

Create a new Java class “Laptop.java” in package “com.theopentutorials.cabdemo.beans” and copy the following code.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.theopentutorials.cabdemo.beans;
<span style="font-weight: bold; height: 13px;" id="z20986_11" class="z20986">public class</span> Laptop {
    private String brand;
     
    public Laptop() {
        super();
    }
    public Laptop(String brand) {
        super();
        this.brand = brand;
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    @Override
    public String toString() {
        return  brand;
    }
}

Create Android Custom Adapter class for ListView

Create a new Java class “LaptopListAdapter.java” in package “com.theopentutorials.cabdemo.adapters” and copy the following code.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
package com.theopentutorials.cabdemo.adapters;
import java.util.List;
import android.app.Activity;
import android.graphics.Color;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.theopentutorials.cabdemo.R;
import com.theopentutorials.cabdemo.beans.Laptop;
public class LaptopListAdapter extends ArrayAdapter<Laptop> {
    Activity context;
    List<Laptop> laptops;
    private SparseBooleanArray mSelectedItemsIds;
    public LaptopListAdapter(Activity context, int resId, List<Laptop> laptops) {
        super(context, resId, laptops);
        mSelectedItemsIds = new SparseBooleanArray();
        this.context = context;
        this.laptops = laptops;
    }
    private class ViewHolder {
        TextView laptopTxt;
    }
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item, null);
            holder = new ViewHolder();
            holder.laptopTxt = (TextView) convertView
                    .findViewById(R.id.laptop_name);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        Laptop laptop = getItem(position);
        holder.laptopTxt.setText(laptop.toString());
        convertView
                .setBackgroundColor(mSelectedItemsIds.get(position) ? 0x9934B5E4
                        : Color.TRANSPARENT);
        return convertView;
    }
    @Override
    public void add(Laptop laptop) {
        laptops.add(laptop);
        notifyDataSetChanged();
        Toast.makeText(context, laptops.toString(), Toast.LENGTH_LONG).show();
    }
    @Override
    public void remove(Laptop object) {
        // super.remove(object);
        laptops.remove(object);
        notifyDataSetChanged();
    }
    public List<Laptop> getLaptops() {
        return laptops;
    }
    public void toggleSelection(int position) {
        selectView(position, !mSelectedItemsIds.get(position));
    }
    public void removeSelection() {
        mSelectedItemsIds = new SparseBooleanArray();
        notifyDataSetChanged();
    }
    public void selectView(int position, boolean value) {
        if (value)
            mSelectedItemsIds.put(position, value);
        else
            mSelectedItemsIds.delete(position);
        notifyDataSetChanged();
    }
    public int getSelectedCount() {
        return mSelectedItemsIds.size();
    }
    public SparseBooleanArray getSelectedIds() {
        return mSelectedItemsIds;
    }
}

Activity

Open “MainActivity” from the package “com.theopentutorials.cabdemo” and copy the following code.
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
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
package com.theopentutorials.cabdemo;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.util.SparseBooleanArray;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.theopentutorials.cabdemo.adapters.LaptopListAdapter;
import com.theopentutorials.cabdemo.beans.Laptop;
public class MainActivity extends SherlockFragmentActivity implements
        OnItemClickListener {
    ListView laptopListView;
    LaptopListAdapter laptopListAdapter;
    List<Laptop> laptops = new ArrayList<Laptop>();
    Activity activity = this;
    private ActionMode mActionMode;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        laptopListView = (ListView) findViewById(R.id.laptopList);
        setLaptopList();
        laptopListAdapter = new LaptopListAdapter(this, R.layout.list_item,
                laptops);
        laptopListView.setAdapter(laptopListAdapter);
        laptopListView.setOnItemClickListener(this);
        laptopListView.setOnItemLongClickListener(new OnItemLongClickListener() {
                    public boolean onItemLongClick(AdapterView<?> parent,
                            View view, int position, long id) {
                        onListItemSelect(position);
                        return true;
                    }
                });
    }
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view,
            int position, long id) {
        if (mActionMode == null) {
            /*no items selected, so perform item click actions
             * like moving to next activity */
            Toast toast = Toast.makeText(getApplicationContext(), "Item "
                    + (position + 1) + ": " + laptops.get(position),
                    Toast.LENGTH_SHORT);
            toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);
            toast.show();
        } else
            // add or remove selection for current list item
            onListItemSelect(position);
    }
    private void onListItemSelect(int position) {
        laptopListAdapter.toggleSelection(position);
        boolean hasCheckedItems = laptopListAdapter.getSelectedCount() > 0;
        if (hasCheckedItems && mActionMode == null)
            // there are some selected items, start the actionMode
            mActionMode = startActionMode(new ActionModeCallback());
        else if (!hasCheckedItems && mActionMode != null)
            // there no selected items, finish the actionMode
            mActionMode.finish();
        if (mActionMode != null)
            mActionMode.setTitle(String.valueOf(laptopListAdapter
                    .getSelectedCount()) + " selected");
    }
    private void setLaptopList() {
        String[] blogs = getResources().getStringArray(R.array.laptops);
        for (String brand : blogs) {
            Laptop laptop = new Laptop(brand);
            laptops.add(laptop);
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.activity_main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_add) {
            // adds item to listview
        } else if (item.getItemId() == R.id.menu_share) {
            // share your application by using share intent
        } else if (item.getItemId() == R.id.menu_rate) {
            // add rate feature to your application by launching market
        }
        return true;
    }
    private class ActionModeCallback implements ActionMode.Callback {
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // inflate contextual menu
            mode.getMenuInflater().inflate(R.menu.context_menu, menu);
            return true;
        }
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return false;
        }
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            switch (item.getItemId()) {
            case R.id.menu_delete:
                // retrieve selected items and delete them out
                SparseBooleanArray selected = laptopListAdapter
                        .getSelectedIds();
                for (int i = (selected.size() - 1); i >= 0; i--) {
                    if (selected.valueAt(i)) {
                        Laptop selectedItem = laptopListAdapter
                                .getItem(selected.keyAt(i));
                        laptopListAdapter.remove(selectedItem);
                    }
                }
                mode.finish(); // Action picked, so close the CAB
                return true;
            default:
                return false;
            }
        }
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            // remove selection
            laptopListAdapter.removeSelection();
            mActionMode = null;
        }
    }
}
Here, we won’t use ListView’s built in selection handling. In the case of long click, actionMode activation required switching between different choice modes of ListView like multipleChoice and none, which works buggy. So we have manually added several methods in adapter class for handling item selection.

Output

Run your application and you will get the output as shown in the beginning of this tutorial.

Folder Structure

The complete folder structure of this project is shown below.

0 comments:

Post a Comment

Nam Le © 2014 - Designed by Templateism.com, Distributed By Templatelib