安卓上自定义ListView中的项点击问题

112

我有一个自定义ListView对象。列表项上有两个垂直叠放的TextView,以及水平进度条。我希望进度条一开始是隐藏的,直到用户执行某些操作后才显示。在最右边是一个复选框,只有当用户需要下载数据库更新时才显示。当我通过将可见性设置为Visibility.GONE来禁用复选框时,我可以点击列表项。但当复选框可见时,除了复选框之外,我不能点击列表中的任何东西。我已经搜索过了,但没有找到与我当前情况相关的内容。我发现了这个问题,但我正在使用重写的ArrayAdapter,因为我在内部使用ArrayLists来包含数据库列表。我是否只需要获取LinearLayout视图,并像Tom那样添加一个onClickListeners?我不确定。

以下是ListView行布局XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="6dip">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dip"
        android:layout_weight="1"
        android:layout_height="fill_parent">
        <TextView
            android:id="@+id/UpdateNameText"
            android:layout_width="wrap_content"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:textSize="18sp"
            android:gravity="center_vertical"
            />
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="0dip"
            android:layout_weight="1"
            android:id="@+id/UpdateStatusText"
            android:singleLine="true"
            android:ellipsize="marquee"
            />
        <ProgressBar android:id="@+id/UpdateProgress" 
                     android:layout_width="fill_parent" 
                     android:layout_height="wrap_content"
                     android:indeterminateOnly="false" 
                     android:progressDrawable="@android:drawable/progress_horizontal" 
                     android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal" 
                     android:minHeight="10dip" 
                     android:maxHeight="10dip"                    
                     />
    </LinearLayout>
    <CheckBox android:text="" 
              android:id="@+id/UpdateCheckBox" 
              android:layout_width="wrap_content" 
              android:layout_height="wrap_content" 
              />
</LinearLayout>

这里是扩展ListActivity的类。显然,它仍在开发中,所以请原谅还缺少或留下的东西:

public class UpdateActivity extends ListActivity {

    AccountManager lookupDb;
    boolean allSelected;
    UpdateListAdapter list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lookupDb = new AccountManager(this);
        lookupDb.loadUpdates();

        setContentView(R.layout.update);
        allSelected = false;

        list = new UpdateListAdapter(this, R.layout.update_row, lookupDb.getUpdateItems());
        setListAdapter(list);

        Button btnEnterRegCode = (Button)findViewById(R.id.btnUpdateRegister);
        btnEnterRegCode.setVisibility(View.GONE);

        Button btnSelectAll = (Button)findViewById(R.id.btnSelectAll);
        btnSelectAll.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                allSelected = !allSelected;

                for(int i=0; i < lookupDb.getUpdateItems().size(); i++) {
                    lookupDb.getUpdateItem(i).setSelected(!lookupDb.getUpdateItem(i).isSelected());
                }

                list.notifyDataSetChanged();
                // loop through each UpdateItem and set the selected attribute to the inverse 

            } // end onClick
        }); // end setOnClickListener

        Button btnUpdate = (Button)findViewById(R.id.btnUpdate);
        btnUpdate.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
            } // end onClick
        }); // end setOnClickListener

        lookupDb.close();
    } // end onCreate


    @Override
    protected void onDestroy() {
        super.onDestroy();

        for (UpdateItem item : lookupDb.getUpdateItems()) {
            item.getDatabase().close();        
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        UpdateItem item = lookupDb.getUpdateItem(position);

        if (item != null) {
            item.setSelected(!item.isSelected());
            list.notifyDataSetChanged();
        }
    }

    private class UpdateListAdapter extends ArrayAdapter<UpdateItem> {
        private List<UpdateItem> items;

        public UpdateListAdapter(Context context, int textViewResourceId, List<UpdateItem> items) {
            super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = null;

            if (convertView == null) {
                LayoutInflater li = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = li.inflate(R.layout.update_row, null);
            } else {
                row = convertView;
            }

            UpdateItem item = items.get(position);

            if (item != null) {
                TextView upper = (TextView)row.findViewById(R.id.UpdateNameText);
                TextView lower = (TextView)row.findViewById(R.id.UpdateStatusText);
                CheckBox cb = (CheckBox)row.findViewById(R.id.UpdateCheckBox);

                upper.setText(item.getName());
                lower.setText(item.getStatusText());

                if (item.getStatusCode() == UpdateItem.UP_TO_DATE) {
                    cb.setVisibility(View.GONE);
                } else {
                    cb.setVisibility(View.VISIBLE);
                    cb.setChecked(item.isSelected());
                }

                ProgressBar pb = (ProgressBar)row.findViewById(R.id.UpdateProgress);
                pb.setVisibility(View.GONE);
            }
            return row;
        }

    } // end inner class UpdateListAdapter
}

编辑:我仍然存在这个问题。我在文本视图中添加了 onClick 处理程序,但似乎非常愚蠢,当我没有点击复选框时,我的 onListItemClick() 函数根本不会被调用。

4个回答

244
问题在于Android不允许您选择具有可聚焦元素的列表项。我修改了列表项上的复选框,添加了一个属性,如下所示:

问题在于Android不允许您选择具有可聚焦元素的列表项。我修改了列表项上的复选框,添加了一个属性,如下所示:

android:focusable="false"

现在我的列表项包含复选框(按钮也适用),它们在传统意义上是“可选择的”(它们会发光,您可以在列表项中的任何位置点击,“onListItemClick”处理程序将触发等)。

编辑:作为更新,评论者提到:“只是一个注意事项,在更改按钮的可见性后,我必须以编程方式再次禁用焦点。”


24
看起来当在列表项中使用ImageButton时,这个解决方案不起作用。 :( - Julian A.
1
好的,但是如果我希望我的复选框和列表视图点击是互斥的呢?如果我选择了列表视图项目,我不一定想要选中复选框。这个解决方案可以实现吗? - Mike6679
2
非常好的答案,谢谢。这也适用于 ImageButtons。只是需要注意,在更改按钮的可见性之后,我必须以编程方式再次禁用聚焦。 - Ljdawson
1
你可能还需要设置android:clickable="false"。 - Mina Wissa
@OscarS 我还没有尝试过,但是阅读API文档让我觉得那也可能行。只有一种方法可以找出来;) - MattC
显示剩余3条评论

44

如果列表项内部有ImageButton,你应该在根列表项元素中设置descendantFocusability值为"blocksDescendants"。

android:descendantFocusability="blocksDescendants"

ImageButton视图中的focusableInTouchMode标志设置为true

android:focusableInTouchMode="true"

1
这对于具有图像按钮的列表项完美地工作。但是除了ImageButton,这可能不起作用 - raksja

13

我曾经遇到过类似的问题,发现在ListView中,复选框CheckBox比较难搞。它会对整个ListItem施加影响,并且有点覆盖了onListItemClick事件。你可能需要为此实现一个点击处理程序,并将复选框的文本属性设置好,而不是使用TextViews。

我建议你也可以研究一下这个View对象,它可能比CheckBox更有效:

CheckedTextView


6
我发现,如果你将列表中可获得焦点的项目的“focusable”设置为false,那么你就可以再次使用你的onListItemClick函数。 - MattC
2
然而,复选框会像列表项一样变成橙色。有人知道解决方案吗? - erdomester

10

在列表项的根视图中使用以下代码:

android:descendantFocusability="blocksDescendants"


该代码可阻止子视图抢占焦点,从而确保用户可以正常操作列表项。

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