5. 创建上下文动作

5.1 问题

需要为用户在用户界面中的选择提供多个可供执行的动作。

5.2 解决方案

(API Level 11)
对于与单个项相关的上下文动作,使用PopupMenu显示固定到相关视图的这些动作,在应该影响多个项的示例中,可启用ActionMode来响应用户的动作。

注意:
该例使用AppCompat支持库来实现最佳的版本兼容性。如果应用程序单纯地支持较早的平台版本,可以使用原生的API实现相同的结果。关于在项目中包括支持库的更多信息,请参阅http://developer.android.com/tools/support-library/index.html

5.3 实现机制

对于本例,我们将构造如图所示的Activity。


具有上下文动作

ActionMode的列表视图

在点击项视图右侧的溢出按钮时,列表中的每个项通过弹出列表提供上下文动作。长按任何项时,Activity将激活ActionMode,从而可以将同一个动作应用于多个已选择的项。

1.上下文弹出菜单

PopupMenu用于获得一个选项菜单资源并将其作为小型弹出窗口轻松附加到任何视图。首先,我们需要在res/menu中创建一个XML文件以定义菜单自身;将此文件称为contextmenu.xml(参见以下代码)。
res/menu/contextmenu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:id="@+id/menu_delete"
    android:icon="@android:drawable/ic_menu_delete"
    android:title="Delete Item" />
  <item
    android:id="@+id/menu_edit"
    android:icon="@android:drawable/ic_menu_edit"
    android:title="Edit Item" />
</menu>

我们为驻留在此列表中的每一项将此资源扩展到PopupMenu实例中。为更好地封装此逻辑,以下两端代码为列表行布局自定义了ContextListItem类。
自定义行项列表

public class ContextListItem extends LinearLayout implements
        PopupMenu.OnMenuItemClickListener,
        View.OnClickListener {

    private PopupMenu mPopupMenu;
    private TextView mTextView;

    public ContextListItem(Context context) {
        super(context);
    }

    public ContextListItem(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ContextListItem(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTextView = (TextView) findViewById(R.id.text);

        //附加单击处理程序
        View contextButton = findViewById(R.id.context);
        contextButton.setOnClickListener(this);

        //创建上下文菜单
        mPopupMenu = new PopupMenu(getContext(), contextButton);
        mPopupMenu.setOnMenuItemClickListener(this);
        mPopupMenu.inflate(R.menu.contextmenu);
    }

    @Override
    public void onClick(View v) {
        //处理上下文按钮单击以显示菜单
        mPopupMenu.show();
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        String itemText = mTextView.getText().toString();

        switch (item.getItemId()) {
            case R.id.menu_edit:
                Toast.makeText(getContext(), "Edit "+itemText, Toast.LENGTH_SHORT).show();
                break;
            case R.id.menu_delete:
                Toast.makeText(getContext(), "Delete "+itemText, Toast.LENGTH_SHORT).show();
                break;
        }
        return true;
    }
}

res/layout/list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<com.examples.popupmenus.ContextListItem
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeightSmall"
    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
    android:background="?android:attr/activatedBackgroundIndicator" >
    <TextView
        android:id="@+id/text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:layout_gravity="center_vertical" />

    <ImageView
        android:id="@+id/context"
        style="@style/Widget.AppCompat.Light.ActionButton.Overflow"
        android:layout_width="?android:attr/listPreferredItemHeightSmall"
        android:layout_height="match_parent"
        android:clickable="true"
        android:focusable="false"/>
</com.examples.popupmenus.ContextListItem>

ContextListItem是包含上下文菜单的文本项和图片按钮的LinearLayout。为实现平台一致性,我们对按钮应用Widget.AppCompat.Light.ActionButton.Overflow样式,使其外观和行为类似于标准的溢出菜单按钮。创建视图时,我们构建一个PopupMenu来显示R.menu.contextmenu,并且关联该菜单,使其在按钮溢出按钮时始终会显示。行视图也设置为OnMenuItemClickListener,以便处理从弹出菜单中选择适当的选项。

注意:
ListView内的可单击项需要将android:focusable设置为false,否则就不能够同时单击顶层列表项。

为了将此视图与列表中的数据绑定,我们需要创建引用列表项布局的基本适配器:

ArrayAdapter<String> adapter = new ArrayAdapter<String> (this,R.layout.list_item,R.id.text,ITEMS);

我们将很快看到将此功能与其他功能联系在一起的完整Activity代码,但首先让我们查看一下如何实现多选逻辑。

2.ActionMode

ActionMode API解决了类似的问题,即允许用户对UI中的某个条目执行某种动作,但它的实现方式稍微有点不同。激活ActionMode后会在窗口的ActionBar上出现一个包含所提供的菜单选项的叠层,并出现一个可以退出ActionMode的额外选项。它还允许同时选择多个条目来执行同一个动作。以下代码演示了这一功能。

使用了上下文ActionMode的Activity

public class ActionActivity extends ActionBarActivity implements AbsListView.MultiChoiceModeListener {

    private static final String[] ITEMS = {
                "Mom", "Dad", "Brother", "Sister", "Uncle", "Aunt",
                "Cousin", "Grandfather", "Grandmother"};
    
    private ListView mList;
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //为上下文事件注册一个按钮
        mList = new ListView(this);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                R.layout.list_item, R.id.text, ITEMS);
        mList.setAdapter(adapter);
        mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        mList.setMultiChoiceModeListener(this);

        setContentView(mList, new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
    }
    
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        // 如果ActionMode一直是无效的,可以在这里做些工作来更新菜单
        return true;
    }
    
    @Override
    public void onDestroyActionMode(ActionMode mode) {
        //退出ActionMode时会调用这个方法
    }
    
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        MenuInflater inflater = mode.getMenuInflater();
        inflater.inflate(R.menu.contextmenu, menu);
        return true;
    }
    
    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        SparseBooleanArray items = mList.getCheckedItemPositions();
        //通过条目的ID得到用户选择的动作
        switch(item.getItemId()) {
        case R.id.menu_delete:
            //执行删除动作
            break;
        case R.id.menu_edit:
            //执行编辑动作
            break;
        default:
            return false;
        }
        return true;
    }
    
    @Override
    public void onItemCheckedStateChanged(ActionMode mode, int position,
            long id, boolean checked) {
        int count = mList.getCheckedItemCount();
        mode.setTitle(String.format("%d Selected", count));
    }
}

要使用我们的ListView来激活一个多选择项的ActionMode,需要将choiceMode属性设置为CHOICE_MODE_MULTIPLE_MODAL。它和传统的CHOICE_MODE_MULTIPLE不同,CHOICE_MODE_MULTIPLE是在每个列表条目上都提供一个可选择的小部件。在ActionMode处于激活状态时,它的模式标识只应用这个选择模式。
对于ActionMode,有很多的回调方法需要实现它,因为它不像ContextMenu一样是直接内置在Activity中的。我们需要实现ActionMode.Callback接口来响应创建菜单和选择选项的事件。ListView有一个名为MultiChoiceModeListener的特殊接口,它是ActionMode.Callback的子接口,本例中就实现了这个特殊的接口。
我们在onCreateActionMode()中的响应和onCreateContextMenu()类似,只是构建了叠层中要显示的菜单选项。这个菜单不需要包含图标;这时,ActionMode会显示条目名。选中每个条目后,我们会在onItemCheckedStateChanged()方法中得到通知。这里,我们会更新ActionMode的标题来显示当前选中条目的个数。
当用户结束选择并单击一个菜单选项时会调用onActionItemClicked()方法。因为会同时选择多个条目,所以我通过getCheckedItemPositions()得到所有选择的条目,这样就可以对所有选择的条目进行操作。

Demo下载地址:
[2.5 创建上下文动作]
https://download.csdn.net/download/qq_41121204/10764629

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,406评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,976评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,302评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,366评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,372评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,457评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,872评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,521评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,717评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,523评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,590评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,299评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,859评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,883评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,127评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,760评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,290评论 2 342

推荐阅读更多精彩内容