如何在Android的ListView中正确地更改特定行的背景颜色?

4

我花了几天的时间尝试解决我在Android上使用ListViews时遇到的问题。我想使用ListView实现单选列表框。因此,我想只有一行具有预定义的浅色背景颜色,其余行具有另一个预选颜色。我遇到的问题是,当我单击特定行时,突出显示的是另一行而不是我按下的那一行。我添加了几条消息以记录发生了什么,但所有情况似乎都正常。这是我的代码:

public class TryListViewActivity extends Activity {
    protected static final int NO_SELECTED_COLOR = 0xFF191919;
    protected static final int SELECTED_COLOR = 0xFF3366CC;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ListView listView = new ListView(this);
        ArrayList<String> list = new ArrayList<String>();
        list.add("Option 1");
        list.add("Option 2");
        list.add("Option 3");
        list.add("Option 4");
        list.add("Option 5");
        list.add("Option 6");
        list.add("Option 7");
        list.add("Option 8");
        list.add("Option 9");
        list.add("Option 10");
        list.add("Option 11");
        list.add("Option 12");
        list.add("Option 13");
        list.add("Option 14");
        list.add("Option 15");

        ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(
            this,
            R.layout.list_box_entry,
            list
        );
        listView.setAdapter(listAdapter);

        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

        // Set the listener
        listView.setOnItemClickListener(
            new AdapterView.OnItemClickListener() {
                public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] Item clicked: position="
                        + position + ";id=" + id
                    );

                    // First, set all rows to be unselected
                    int counter  = parent.getCount();
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] "
                        + counter + " items found inside the parent"
                    );
                    int children = parent.getChildCount();
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] "
                        + children + " views found inside the parent"
                    );
                    for(int i=0;i<children;i++) {
                        Log.i(
                            "Log",
                            "[SingleSelectionListBox] Child "
                            + i + " has message "
                            + ((TextView)parent.getChildAt(i)).getText()
                        );
                    }

                    // Too inefficient but for now is OK
                    for(int i=0;i<children;i++)
                        parent.getChildAt(i)
                            .setBackgroundColor(NO_SELECTED_COLOR);
                    Log.i("Log",
                        "[SingleSelectionListBox] First visible position: "
                        + parent.getFirstVisiblePosition()
                    );

                    // Set the background color
                    TextView textView = (TextView)(parent.getChildAt(
                        position-parent.getFirstVisiblePosition()));
                    textView.setBackgroundColor(SELECTED_COLOR);
                    Log.i(
                        "Log",
                        "[SingleSelectionListBox] Text inside the "
                        + " View changing the color " + textView.getText()
                    );
                }
            }
        );
        setContentView(listView);
    }
}

在资源(res/layout)中,我插入了一个名为list_text_entry.xml的文件,其内容如下:
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:gravity="center"
  android:textColor="#FFFFFF" android:padding="10dp" android:textSize="16sp">
</TextView>

例如,如果我在listView滚动到我看到的第一行是"Option 4"之后再点击"Option 11"条目,那么只有携带蓝色背景的"Option 7"行会被选中。有人能够向我解释这里发生了什么吗?下面是我从日志中获取的信息。
[SingleSelectionListBox] Item clicked: position=10;id=10
[SingleSelectionListBox] 15 items found inside the parent
[SingleSelectionListBox] 11 views found inside the parent
[SingleSelectionListBox] Child 0 has message Option 4
[SingleSelectionListBox] Child 1 has message Option 5
[SingleSelectionListBox] Child 2 has message Option 6
[SingleSelectionListBox] Child 3 has message Option 7
[SingleSelectionListBox] Child 4 has message Option 8
[SingleSelectionListBox] Child 5 has message Option 9
[SingleSelectionListBox] Child 6 has message Option 10
[SingleSelectionListBox] Child 7 has message Option 11
[SingleSelectionListBox] Child 8 has message Option 12
[SingleSelectionListBox] Child 9 has message Option 13
[SingleSelectionListBox] Child 10 has message Option 14
[SingleSelectionListBox] First visible position: 3
[SingleSelectionListBox] Text inside the View changing the color Option 11

我猜测所有子元素在ViewGroup中都是从上到下排序的,即使我在代码中这样做:

TextView textView = (TextView)(parent.getChildAt(
    position-parent.getFirstVisiblePosition()
));
textView.setBackgroundColor(SELECTED_COLOR);

出现消息 选项11,但实际上选择的是 选项7。这是Android的一个错误吗?

下次,請使用四個空格來標記代碼,而不是使用HTML標籤。詳情請參閱Markdown Editing Help頁面。 - Matthias
6个回答

7

我使用过这段代码,它的效果很好,你应该试试:

listView.getChildAt(0).setBackgroundColor(Color.BLUE);

5

感谢Dave和ChristianB的答复。我仍然不知道Android为什么这样做,所以问题仍未解决。但是,我找到了一种使用自定义适配器获得所需内容的方法。以下是代码,如果有人需要,可以参考。

public class CustomAdapter extends ArrayAdapter<String> {

    protected static final int NO_SELECTED_COLOR = 0xFF191919;
    protected static final int SELECTED_COLOR = 0xFF3366CC;

    private ArrayList<String> items;
    private LayoutInflater mInflater;
    private int viewResourceId;
    private int selectedPosition;

    public CustomAdapter(Activity activity,int resourceId,
        ArrayList<String> list) {
        super(activity,resourceId,list);

        // Sets the layout inflater
        mInflater = (LayoutInflater)activity
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        // Set a copy of the layout to inflate
        viewResourceId = resourceId;

        // Set a copy of the list
        items = list;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv = (TextView)convertView;
        if (tv == null) {
            tv = (TextView)mInflater.inflate(viewResourceId, null);
        }
        tv.setText(items.get(position));

        // Change the background color
        if (position==selectedPosition) tv.setBackgroundColor(SELECTED_COLOR);
        else tv.setBackgroundColor(NO_SELECTED_COLOR);

        return tv;
    }

    public void setSelected(int position) {
        selectedPosition = position;
    }
}

所以,在ListView初始化时,我只需要添加这个监听器:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View view,
        int position, long id) {
        ((CustomAdapter)listAdapter).setSelected(position);
        listView.invalidate();
    }
});

由于我正在扩展ArrayAdapter,而这个适配器应该具有提供的所有数据的副本,因此这不是一种有效的解决方案。因此,我使用了比需要更多的内存,如果列表变得非常大,我可能会遇到内存问题。因此,如果有人知道更好的解决方案,请发布!


对于这个问题,填充每个视图是一个不错的解决方案。如果你担心内存问题(除非你要显示成千上万个字符串,否则不是很大的问题),你可能希望使用CursorAdapter,并将数据存储在SQLite数据库中。 - Dave

5

正如Dave所说,您应该使用:

view.setBackgroundColor(SELECTED_COLOR);

也许

view.refreshDrawableState(); 

然而,由于Android会回收列表,因此它会在每个未显示在屏幕上的第一个项目上重复您选择的颜色。因此,如果您的屏幕大小可以显示十个项目,则当您滚动时,第11个、第21个等也将显示为已选中。

要避免这种情况,您需要创建自定义适配器。然后在getView中,您需要添加以下内容:

if (myActivity.selectedRow != position){
    v.setBackgroundColor(Color.TRANSPARENT);
} else {
    v.setBackgroundColor(SELECTED_COLOUR);
}

在我的活动myActivity中,selectedRow是一个public static int selectedRow,用于创建列表。在那里,您可以存储在单击列表时选择的行号。


0
    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
    {
   LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

  View itemView = inflater.inflate(R.layout.verselayout, parent, false); 
    txttitle = (TextView) itemView.findViewById(R.id.Versetxt);
    if (position%2 == 0) {
       txttitle.setTextColor(Color.parseColor("#FFFFFF"));
    }
 else
       {
    txttitle.setTextColor(Color.parseColor("#FFFF00"));
       }

     return itemView;
    }

0

listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

  @Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long   id) 
{

    View v;

    int count = parent.getChildCount();
    v =parent.getChildAt(position);
    parent.requestChildFocus(v, view);  v.setBackground(res.getDrawable(R.drawable.transparent_button));

            for (int i=0; i<count; i++)
            {
                if (i!= position)
                {
                    v = parent.getChildAt(i);t  v.setBackground(res.getDrawable(R.drawable.not_clicked));

                }
            }

        }

    });

基本上,创建两个可绘制对象 - 一个是透明的,另一个是所需颜色。在点击位置(如定义的int位置)请求焦点,并更改该行的颜色。然后遍历父列表视图,并相应地更改所有其他行。这考虑了用户多次单击列表视图的情况。


0

我相信这是由于ListViews编号和重复使用行的方式造成的。因此,不要使用parent.getChildAt(i)来获取您想要操作的行,而是使用传递给onItemClickView对象本身。

view.setBackgroundColor(SELECTED_COLOR);

嗨,戴夫,我很遗憾地说我之前尝试过直接使用函数中提供的视图,结果完全相同。因此,我尝试找到更复杂的替代方法,但效果不佳。但还是谢谢你的回答! - Charlie

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