如何在 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 中正确更改特定行的背景颜色? (安卓)的主要内容,如果未能解决你的问题,请参考以下文章