Android:如何在创建列表时禁用列表项

21

我是一名新手Android开发者,仍在学习很多东西。

我使用以下代码显示了主菜单,但是无法弄清如何禁用菜单中的选定项。 有人可以帮我提供一些示例代码吗?

public class listTest extends ListActivity {

    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        setListAdapter(ArrayAdapter.createFromResource(this, R.array.mainMenu,
                android.R.layout.simple_list_item_1)); 
        //not sure how to disable list items here
    }

    protected void onListItemClick(ListView list, View view, int position, long id) {
        // can disable items when they are clicked on
        view.setEnabled(false);
    }   

}

我在我的strings.xml文件中有一个string-array

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="mainMenu">
        <item>Item 1</item>
        <item>Item 2</item>
        <item>Item 3</item>
    </string-array> 
</resources>
谢谢您。

几个快速澄清:如果您想要一个菜单,在用户按下菜单键时弹出,则请查看您的“Activity”中的onCreateOptionsMenu方法。此外,您不应该需要像那样保留应用程序上下文;一个“Activity”已经扩展了“Context”(正如您在调用createFromResource(this, ...)中所看到的)。 - Christopher Orr
我想要一个主菜单,当应用程序首次打开时会显示出来,所以optionsMenu和contextMenu都不太好用。我已经成功显示了列表,并且可以在点击后禁用项目:protected void onListItemClick(ListView list, View view, int position, long id) { view.setEnabled(false); }但是我不知道如何在onCreate中将所选的列表项设置为禁用状态。谢谢您对应用程序上下文的关注。 - Martyn
4个回答

45
为了在创建列表时禁用列表项,您需要从ArrayAdapter进行子类化。您需要覆盖以下方法:isEnabled(int position)areAllItemsEnabled()。在前者中,根据给定位置的列表项是否启用,返回truefalse。在后者中,返回false
如果您想使用createFromResource(),则还必须实现该方法,因为ArrayAdapter.createFromResource()仍然会实例化ArrayAdapter而不是您自己的适配器。
最后,代码将如下所示:
class MenuAdapter extends ArrayAdapter<CharSequence> {

    public MenuAdapter(
            Context context, int textViewResId, CharSequence[] strings) {
        super(context, textViewResId, strings);
    }

    public static MenuAdapter createFromResource(
            Context context, int textArrayResId, int textViewResId) {

        Resources      resources = context.getResources();
        CharSequence[] strings   = resources.getTextArray(textArrayResId);

        return new MenuAdapter(context, textViewResId, strings);
    }

    public boolean areAllItemsEnabled() {
        return false;
    }

    public boolean isEnabled(int position) {
        // return false if position == position you want to disable
    }
}

15
使用此解决方案时请注意。BaseAdapter的文档说明如下:“如果指定位置处的项目不是分隔符,则返回true。”这意味着如果返回false,则该项是分隔符项。一些手机可能不会在普通项和分隔符项之间绘制android:divider。 - Janusz
@Janusz 是的,我遇到了分割线的问题。因为当我禁用某个索引处的项时,分割线就会消失。所以我将该索引处的视图变成了浅灰色,看起来像是被禁用了 :) - Ali Imran
@AliImran:能否请您详细说明一下您是如何解决无法绘制分隔符的问题的?谢谢。 - dentex

11

我认为列表项是否可用是该项状态的一部分,因此您需要在ListAdapter中管理它。当子类化一个适配器时,您可以重写isEnabled(position)方法。对于任何位置,如果在这里返回true,则ListView将标记此项为禁用。

因此,您要做的是像这样:

class MenuAdapter extends ArrayAdapter<String> {

    public boolean isEnabled(int position) {
       // return false if position == positionYouWantToDisable
    }

}

如果你想使用setter方法来启用/禁用某个项目,那么可能需要使用例如Map来管理每个项目的启用状态。

然后将自定义适配器设置到你的ListView上。


谢谢您的回复,看起来很不错,但是在实现时遇到了问题。 setListAdapter(MenuAdapter.createFromResource(this, R.array.mainMenu,android.R.layout.simple_list_item_1)); MenuAdapter mainMenuList = (MenuAdapter) getListAdapter(); mainMenuList.isEnabled(2); //目前只设置为返回false 您有什么想法吗?它目前只是崩溃了。 - Martyn
你得到了什么异常? - mxk
02-03 13:51:21.971: ERROR/AndroidRuntime(1365): java.lang.RuntimeException: 无法启动组件活动 ComponentInfo{com.martyn.test/com.martyn.test.test}: java.lang.ClassCastException: android.widget.ArrayAdapter - Martyn
1
在哪一行?setListAdapter(MenuAdapter.createFromResource(this, R.array.mainMenu,android.R.layout.simple_list_item_1));?这很奇怪,因为createFromResource返回的是一个ArrayAdapter,而ArrayAdapter是一个ListAdapter。尝试手动实例化它,而不是使用资源助手。 - mxk
1
你应该返回“FALSE”以表示任何位置都不符合条件吗? - superjos

6
您可以通过调用以下两个方法来禁用列表项(使其不响应触摸操作):
setClickable(false)

并且

setFocusable(false)

例如,在您的适配器中。

默认情况下,这不会自动反映在图形上。

我目前正在使用它来创建一个列表,其中的列表项不能点击,但其中大部分包含可点击的小部件。效果很好。

这样,列表项将正常绘制,包括分隔符(请参见上面已接受答案的Janusz的回复)。


1
我实现了BaseAdapter的子类,它在getView方法中填充列表单元格布局。在布局XML中,根元素是(在我的情况下)一个LinearLayout,既不可聚焦也不可点击:android:focusable="false"和android:clickable="false"。 - Raginmari
6
我尝试了你的解决方案,它奏效了,但是奇怪的是,它反过来了!我的代码如下: public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; if (row == null) { row = inflater.inflate(R.layout.row_style, parent, false); } row.setFocusable(false); row.setClickable(false); return row; }这会使所有行都能被点击。但是如果我使用 row.setFocusable(true); row.setClickable(true); 则这些行不可点击。非常奇怪... - Pooks
5
对于那些认为行为会反向的人,事实并非如此。当您将列表项设置为可点击(true)时,后续的点击事件将传递到列表项本身,因此该事件不会传播到父级(列表视图)。如果列表项不对点击做出反应,则什么也不会发生。 - Jamol
这是一种禁用项目上的点击事件的hack-ish方法。你找到了正确的方法吗?我正在努力寻找。我认为这是一个非常普遍的问题,不能只有这些不太好的解决方案。 - doplumi
哇,这真有道理!尽管应该在getView()的描述中进行澄清,因为它非常不直观。 - Pooks
显示剩余3条评论

-3

或者简单的方法是取消注册并注册 OnItemClickListener 可能是一个更好的想法。

注册:

lstDevice.setOnItemClickListener(context);

取消注册:

lstDevice.setOnItemClickListener(null);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接