如何在 ListView 中正确更改特定行的背景颜色? (安卓)

Posted

技术标签:

【中文标题】如何在 ListView 中正确更改特定行的背景颜色? (安卓)【英文标题】:How can I change the background color for an specific row properly in a ListView? (Android) 【发布时间】:2011-06-05 19:24:27 【问题描述】:

我花了几天时间试图解决我在 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_
  android:layout_ android:gravity="center"
  android:textColor="#FFFFFF" android:padding="10dp" android:textSize="16sp">
</TextView>

例如,如果我在 listView 之前向下滚动时单击“选项 11”条目,直到我看到的第一行是“选项 4”,则“选项 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);

出现消息Option 11,但实际上是选中的option 7。这是 Android 的错误吗?

【问题讨论】:

下次请使用四个空格来标记代码,而不是使用 html 标记。有关详细信息,请参阅Markdown Editing Help 页面。 【参考方案1】:

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

view.setBackgroundColor(SELECTED_COLOR);

【讨论】:

你好 Dave,很遗憾说我之前尝试过,直接使用函数中提供的 View 的结果是完全一样的。出于这个原因,我试图找到一种更复杂的替代方法,但效果不佳。不过谢谢你的回答!【参考方案2】:

是的,你应该像戴夫所说的那样使用:

view.setBackgroundColor(SELECTED_COLOR);

也许

view.refreshDrawableState(); 

但是,由于 Android 回收列表,它会在屏幕上未显示的每个第一个项目上重复您选择的颜色。因此,如果您的屏幕尺寸可以显示十个项目而不是第 11 个,那么当您滚动时,第 21 个等也会显示为选中状态。

为避免这种情况,您必须创建一个自定义适配器。然后在 getView 中你需要这样说:

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

其中 selectedRow 是 myActivity 中的 public static int selectedRow,创建您的列表的活动。在那里您存储单击列表时选择的行号。

【讨论】:

【参考方案3】:

感谢 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 扩展而来的,它假设提供了所有数据的副本。因此,我使用的内存比需要的多得多,如果列表变得非常大,我可能会遇到内存问题。所以,如果有人知道更好的解决方案,请发布!

【讨论】:

膨胀每个视图是解决这个问题的好方法。如果您担心内存影响(除非您显示 1,000 个 String,这不是一个大问题),您可能希望改用 CursorAdapter 并将您的数据存储在 SQLite 数据库中。 【参考方案4】:

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 位置)并更改所述行的颜色。然后遍历父列表视图,并相应地更改所有其他行。这说明了用户多次单击列表视图的情况。

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:
    @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;
    

【讨论】:

【参考方案7】:

试试这个,

OnItemClickListener onitemclick = new OnItemClickListener() 

        @Override
        public void onItemClick(AdapterView<?> adapter, View arg1, int position, long id) 
                selectedItem= position;
                adapter.notifyDataSetChanged();
        
    ;

覆盖适配器的 getView() 方法:

 @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        final View view = View.inflate(context, R.layout.item_list, null);

        if (position == selectedItem) 
            // set your color
        

        return view;
    

【讨论】:

以上是关于如何在 ListView 中正确更改特定行的背景颜色? (安卓)的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Sqlite 数据库中的特定数据上更改 Listview 中的行颜色

如何在颤动/飞镖的 ListView 项目中更改背景颜色

如何更改 Pygments 中特定行的背景?

从 ListView 更改特定元素

更改 slickgrid 中特定行的背景颜色?

如何更改所选 SlidingMenu 行的背景颜色并保持不变,直到选择其他行?