Android:自定义可选ListView项

时间:2012-01-22 20:17:10

标签: android listview checkbox

我几天来一直在寻找这个问题的解决方案。

我正在尝试创建一个自定义ListView列表项,可以选择进行批量编辑。这类似于列出收件箱中的电子邮件的Gmail应用程序,允许您为操作选择一组电子邮件(删除,移动等)。

我的要求是:

  • 必须具有自定义外观。 android.R.layout.simple_list_item_ [checked | multiple_choice]布局不适合我正在寻找的内容
  • 我希望它能够使用给定的ListView#setChoiceMode和ListView#getCheckedItemPositions。 CheckedTextView也不是。 (另一种方法是没问题)
  • 我想在用户选择“编辑模式”时显示复选框
  • 我想修改类似于Android's Selection Mode
  • 的菜单选项

我尝试将自己的复选框添加到我的视图中,并为其设置一个OnClickListener,将列表项标记为选中/未选中,但这对我来说并不适用。

感谢任何帮助;即使它只让我90%的路程。

请记住,我正在努力让它在Android 2.1及更高版本上运行。

谢谢

3 个答案:

答案 0 :(得分:6)

查看this link

这是我为实现自己的自定义列表视图而阅读的资源。这对我真的很有帮助。我认为您需要在list_item.xml文件中添加一个复选框并更改布局属性。

您还需要在自定义适配器内的CheckBox对象onCheckedChanged上添加一个侦听器。

以下是自定义适配器的示例代码;

public class EntryAdapter extends ArrayAdapter<Entry> {

private Activity context;
private LayoutInflater inflater;
private ArrayList<Entry> entries;

public EntryAdapter(Activity context, ArrayList<Entry> objects) {
    super(context, R.layout.entry_item, objects);
    // TODO Auto-generated constructor stub
    this.context = context;
    this.entries = objects;
}

public View getView (int position,  View convertView, ViewGroup parent)
{
    View viewRow = convertView;
    if(viewRow == null)
    {                   
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);          
        viewRow = inflater.inflate(R.layout.entry_item , null, true);                       
    }

    Entry e = entries.get(position);
    if(e != null)
    {
        TextView aka = (TextView) viewRow.findViewById(R.id.authorTextView);
        TextView content = (TextView) viewRow.findViewById(R.id.entryContentTextView);

        if(aka != null && content != null){

            aka.setText(e.getAka());
            content.setText(e.getContent());
        }

    }


    return viewRow;

}

}

在此代码中,没有事件侦听器。你首先应该做的是添加定义一个类似于布尔的arraylist

ArrayList<boolean> selectedItems;

之后,在getView方法中你应该添加以下内容;

CheckBox cb = (CheckBox) findViewById(R.id.checkBoxId);

之后,你应该在getView()方法中添加onCheckStateChanged监听器

cb.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
    {
        if ( isChecked )
        {
          // find out the item is selected or not and add to selected arraylist by using the position of the element
        }

}
});

最后,你应该有getSelectedMethod,它返回所选项目的arraylist相对于他们的位置。

希望我理解这个问题,对你有所帮助。

答案 1 :(得分:3)

所以我相信我已经解决了我面临的主要问题。

我相信我遇到的问题是我的视图并不代表ListView的状态。在拉门里;我认为需要一个复选框来设置列表项是否被选中(这是我的Web开发人员)。但是,无论您是否有一个视图(CheckBox)来表示,ListView都会保持自己检查的状态,而不是什么。

布局/ list_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearLayout1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ProgressBar
        android:id="@+id/progressBar1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="5dip" />

    <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Line 1"
            android:textAppearance="?android:attr/textAppearanceMedium" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Line 2" />
    </LinearLayout>

    <CheckBox
        android:id="@+id/checkBox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="false"
        android:focusable="false"
        android:visibility="gone" />

</LinearLayout>

菜单/ menu.xml文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/edit_menu_item"
        android:title="KAMEHAMEHA!!!!!"/>

</menu>

菜单/ edit.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/commit_menu_item"
        android:title="Commit"/>
    <item
        android:id="@+id/cancel_menu_item"
        android:title="Cancel"/>

</menu>

ListViewTestActivity.java

package com.loesak.listviewtest;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ListViewTestActivity extends ListActivity {
    private static final String[] GENRES = new String[] {
        "Action", "Adventure", "Animation", "Children", "Comedy", "Documentary", "Drama",
        "Foreign", "History", "Independent", "Romance", "Sci-Fi", "Television", "Thriller"
    };

    private ListView listView = null;
    private MyArrayAdapter myArrayAdapter = null;
    private boolean editMode = false;

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

        this.listView = getListView();
        this.myArrayAdapter = new MyArrayAdapter(this, GENRES);
        this.setListAdapter(this.myArrayAdapter);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        menu.clear();

        MenuInflater inflater = this.getMenuInflater();

        if(!this.editMode) {
            inflater.inflate(R.menu.menu, menu);
        } else {
            inflater.inflate(R.menu.edit, menu);
        }

        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(item.getItemId() == R.id.edit_menu_item) {
            Toast.makeText(this, "entering edit mode", Toast.LENGTH_SHORT).show();

            this.editMode = true;
            this.listView.setItemsCanFocus(false);
            this.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

            this.myArrayAdapter.setEditMode(editMode);
        } else if(item.getItemId() == R.id.commit_menu_item 
                || item.getItemId() == R.id.cancel_menu_item) {
            if(item.getItemId() == R.id.commit_menu_item) {
                Toast.makeText(this, "committing changes", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "leaving edit mode", Toast.LENGTH_SHORT).show();
            }

            this.editMode = false;
            this.listView.setItemsCanFocus(true);
            this.listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
            this.listView.clearChoices();

            this.myArrayAdapter.setEditMode(editMode);
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        if(!this.editMode) {
            Toast.makeText(this, "list item selected at position " + position, Toast.LENGTH_SHORT).show();
        } else {
            String str = "";
            SparseBooleanArray wtfit = l.getCheckedItemPositions();
            for(int i = 0; i < GENRES.length; i++) {
                if(wtfit.get(i)) {
                    str += i + ", ";
                }
            }

            Toast.makeText(this, "Selected item positions: " + str, Toast.LENGTH_SHORT).show();
        }

        super.onListItemClick(l, v, position, id);
    }

    class MyArrayAdapter extends ArrayAdapter<String> {
        private Context context;
        private String[] entires;

        private boolean editMode = false;

        public MyArrayAdapter(Context context, String[] entries) {
            super(context, R.layout.list_item, entries);

            this.context = context;
            this.entires = entries;
        }

        @Override
        public View getView(final int position, View view, ViewGroup parent) {
            final ListView listView = (ListView) parent;
            final ViewHolder viewHolder;

            if(view == null) {
                LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

                view = layoutInflater.inflate(R.layout.list_item, null, true);

                viewHolder = new ViewHolder();
                viewHolder.text1 = (TextView) view.findViewById(R.id.textView1);
                viewHolder.text2 = (TextView) view.findViewById(R.id.textView2);
                viewHolder.checkbox1 = (CheckBox) view.findViewById(R.id.checkBox1);

                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }

            viewHolder.text1.setText(this.entires[position] + "_1");
            viewHolder.text2.setText(this.entires[position] + "_2");
            viewHolder.checkbox1.setChecked(listView.isItemChecked(position));

            if(this.editMode) {
                viewHolder.checkbox1.setVisibility(View.VISIBLE);
            } else {
                viewHolder.checkbox1.setVisibility(View.GONE);
            }

            return view;
        }

        public void setEditMode(boolean editMode) {
            this.editMode = editMode;
            this.notifyDataSetChanged();
        }

        class ViewHolder {
            TextView text1;
            TextView text2;
            CheckBox checkbox1;
        }
    }
}

要使复选框反映列表项的实际选定模式:

viewHolder.checkbox1.setChecked(listView.isItemChecked(position));

就是这样。一旦你知道这一切是如何运作的,那就很明显了。太糟糕了我花了很长时间才弄明白。有时我觉得android编程有时候太复杂了。

我现在唯一的问题是,当单击列表项时,ListView项总是被重绘。这对包含来自文件的异步加载图像的列表项目造成了严重破坏。希望当我加强我的代码部分的性能时,这将消失。

答案 2 :(得分:0)

如同之前的帖子一样:

  

每当你想用ListView中的视图进行处理时   需要创建一个可以处理逻辑的自定义适配器   实现并在必要时将该信息传递给视图。

这是您需要的起点。在构建视图时,必须明确定义每一行。单击特定视图时,将处理该特定行。您可以做的一件事是在单击编辑按钮时,您将获取该行中的所有数据并将其转换为另一个看似可编辑的视图。然后在确认中,您将更新该行并将其转换回原始视图。