使用 Xamarin.Android 自定义 ListView 的外观
ListView 的外观由显示的行的布局决定。 若要更改 ListView
的外观,请使用其他行布局。
内置行视图
有 12 种可使用 Android.Resource.Layout 引用的内置视图:
TestListItem - 具有最小格式的单行文本。
SimpleListItem1 - 单行文本。
SimpleListItem2 - 两行文本。
SimpleSelectableListItem - 支持单个或多个项目选择的单行文本(在 API 级别 11 中添加)。
SimpleListItemActivated1 - 类似于 SimpleListItem1,但背景色指示何时选中行(在 API 级别 11 种添加)。
SimpleListItemActivated2 - 类似于 SimpleListItem2,但背景色指示何时选中行(在 API 级别 11 种添加)。
SimpleListItemChecked - 显示用于指示所选内容的选中标记。
SimpleListItemMultipleChoice - 显示复选框以指示多选选择。
SimpleListItemSingleChoice - 显示单选按钮以指示互斥选择。
TwoLineListItem - 两行文本。
ActivityListItem - 包含图像的单行文本。
SimpleExpandableListItem - 按类别对行进行分组,可展开或折叠每个组。
每个内置行视图都有一个与之关联的内置样式。 以下屏幕截图显示了每个视图的显示方式:
BuiltInViews 解决方案中的 BuiltInViews/HomeScreenAdapter.cs 示例文件包含用于生成不可展开的列表项屏幕的代码。 视图在 GetView
方法中设置,如下所示:
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
然后,可以通过引用 Android.Resource.Id
下的标准控件标识符 Text1
、Text2
和 Icon
来设置视图的属性(不要设置视图没有包含的视图,否则会引发异常):
view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Android.Resource.Id.Text2).Text = item.SubHeading;
view.FindViewById<ImageView>(Android.Resource.Id.Icon).SetImageResource(item.ImageResourceId); // only use with ActivityListItem
BuiltInViews 解决方案中的 BuiltInExpandableViews/ExpandableScreenAdapter.cs 示例文件包含用于生成 SimpleExpandableListItem 屏幕的代码。 组视图在 GetGroupView
方法中设置,如下所示:
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleExpandableListItem1, null);
子视图在 GetChildView
方法中设置,如下所示:
view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleExpandableListItem2, null);
然后,可以通过引用标准控件标识符 Text1
和 Text2
来设置组视图和子视图,如上所示。 SimpleExpandableListItem 屏幕截图(如上所示)提供了单行组视图 (SimpleExpandableListItem1) 和两行子视图 (SimpleExpandableListItem2) 的示例。 或者,可以为两行 (SimpleExpandableListItem2) 配置组视图,可以为单行 (SimpleExpandableListItem1) 配置子视图,或者组视图和子视图可具有相同的行数。
Accessories
行可在视图右侧添加附件,以指示选择状态:
SimpleListItemChecked - 创建勾号作为指示器的单选列表。
SimpleListItemSingleChoice - 创建单选按钮类型列表,其中只能选择一项。
SimpleListItemMultipleChoice - 创建复选框类型列表,其中可选择多项。
上述附件按各自顺序显示在下面的屏幕上:
若要显示其中一个附件,请将所需的布局资源 ID 传递给适配器,然后手动设置所需行的选择状态。 以下代码行显示如何使用其中一种布局创建和分配 Adapter
:
ListAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItemChecked, items);
无论显示的附件如何,ListView
本身都支持不同的选择模式。 为了避免混淆,请使用带有 SingleChoice
附件的 Single
选择模式和带有 MultipleChoice
样式的 Checked
或 Multiple
模式。 选择模式由 ListView
的 ChoiceMode
属性控制。
处理 API 级别
早期版本的 Xamarin.Android 将枚举实现为整数属性。 最新版本引入了适当的 .NET 枚举类型,使发现潜在选项变得更加容易。
ChoiceMode
是整数或枚举,具体取决于你面向的 API 级别。 若要以 Gingerbread API 为目标,示例文件 AccessoryViews/HomeScreen.cs 有一个注释掉的块:
// For targeting Gingerbread the ChoiceMode is an int, otherwise it is an
// enumeration.
lv.ChoiceMode = Android.Widget.ChoiceMode.Single; // 1
//lv.ChoiceMode = Android.Widget.ChoiceMode.Multiple; // 2
//lv.ChoiceMode = Android.Widget.ChoiceMode.None; // 0
// Use this block if targeting Gingerbread or lower
/*
lv.ChoiceMode = 1; // Single
//lv.ChoiceMode = 0; // none
//lv.ChoiceMode = 2; // Multiple
//lv.ChoiceMode = 3; // MultipleModal
*/
以编程方式选择项
使用 SetItemChecked
手动设置“选择”哪些项(可调用多次来进行多选):
// Set the initially checked row ("Fruits")
lv.SetItemChecked(1, true);
该代码还需要检测与多选不同的单选。 若要确定在 Single
模式下选择了哪个行,请使用 CheckedItemPosition
整数属性:
FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPosition
若要确定在Multiple
模式下选择了哪些行,需要循环访问CheckedItemPositions
SparseBooleanArray
该行。 稀疏数组就像一个字典,只包含值已更改的条目,因此必须遍历整个数组来查找 true
值,从而了解已在列表中选择了哪些内容,如以下代码片段所示:
var sparseArray = FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPositions;
for (var i = 0; i < sparseArray.Size(); i++ )
{
Console.Write(sparseArray.KeyAt(i) + "=" + sparseArray.ValueAt(i) + ",");
}
Console.WriteLine();
创建自定义行布局
4 个内置行视图非常简单。 若要显示更复杂的布局(例如电子邮件列表、推文或联系人信息),需要自定义视图。 自定义视图通常声明为 Resources/Layout 目录中的 AXML 文件,然后由自定义适配器使用其资源 ID 加载。 视图可以包含任意数量的显示类(例如 TextView 和 ImageView 等控件),还可包含自定义颜色、字体和布局。
此示例在很多方面与上述示例不同:
继承自
Activity
,而不是ListActivity
。 可以自定义任何ListView
的行,但是其他控件也可包含在Activity
布局中(例如标题、按钮或其他用户界面元素)。 此示例在ListView
上方添加一个标题进行说明。需要屏幕的 AXML 布局文件;在前面的示例中,
ListActivity
不需要布局文件。 此 AXML 包含ListView
控件声明。需要一个 AXML 布局文件来呈现每一行。 此 AXML 文件包含具有自定义字体和颜色设置的文本和图像控件。
使用可选的自定义选择器 XML 文件在选定行时设置该行的外观。
Adapter
实现从GetView
重写中返回自定义布局。ItemClick
必须以不同的方式声明(事件处理程序附加到ListView.ItemClick
,而不是ListActivity
中的重写OnListItemClick
)。
下面详细介绍了这些更改,首先是创建活动的视图和自定义行视图,然后介绍为呈现它们而对适配器和活动进行的修改。
将 ListView 添加到活动布局
由于 HomeScreen
不再继承自 ListActivity
,它没有默认视图,因此必须为主屏幕视图创建布局 AXML 文件。 对于此示例,视图将具有标题(使用 TextView
)和 ListView
来显示数据。 布局在 Resources/Layout/HomeScreen.axml 文件中定义,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/Heading"
android:text="Vegetable Groups"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:textSize="30dp"
android:textColor="#FF267F00"
android:textStyle="bold"
android:padding="5dp"
/>
<ListView android:id="@+id/List"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#FFDAFF7F"
/>
</LinearLayout>
将 Activity
和自定义布局(而不是 ListActivity
)结合使用的优势在于能够向屏幕添加其他控件,例如此示例中的标题 TextView
。
创建自定义行布局
需要另一个 AXML 布局文件,才能包含将在列表视图中显示的每一行的自定义布局。 在此示例中,行将具有绿色背景、棕色文本和右对齐图像。 有关用于声明此布局的 Android XML 标记,请参阅 Resources/Layout/CustomView.axml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#FFDAFF7F"
android:padding="8dp">
<LinearLayout android:id="@+id/Text"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip">
<TextView
android:id="@+id/Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF7F3300"
android:textSize="20dip"
android:textStyle="italic"
/>
<TextView
android:id="@+id/Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dip"
android:textColor="#FF267F00"
android:paddingLeft="100dip"
/>
</LinearLayout>
<ImageView
android:id="@+id/Image"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="5dp"
android:src="@drawable/icon"
android:layout_alignParentRight="true" />
</RelativeLayout >
虽然自定义行布局可以包含许多不同的控件,但滚动性能可能会受到复杂设计和图像使用的影响(尤其是在必须通过网络加载它们时)。 有关解决滚动性能问题的详细信息,请参阅 Google 的文章。
引用自定义行视图
自定义适配器示例的实现位于 HomeScreenAdapter.cs
。 关键方法是 GetView
,其中它使用资源 ID Resource.Layout.CustomView
加载自定义 AXML,然后在返回之前设置视图中每个控件的属性。 会显示完整的适配器类:
public class HomeScreenAdapter : BaseAdapter<TableItem> {
List<TableItem> items;
Activity context;
public HomeScreenAdapter(Activity context, List<TableItem> items)
: base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override TableItem this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
View view = convertView;
if (view == null) // no view to re-use, create new
view = context.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Resource.Id.Text2).Text = item.SubHeading;
view.FindViewById<ImageView>(Resource.Id.Image).SetImageResource(item.ImageResourceId);
return view;
}
}
在活动中引用自定义 ListView
由于 HomeScreen
类现在继承自 Activity
,因此在类中声明一个 ListView
字段,用于保存对 AXML 中声明的控件的引用:
ListView listView;
然后,该类必须使用 SetContentView
方法加载活动的自定义布局 AXML。 之后,它可以在布局中找到 ListView
控件,然后创建和分配适配器并分配单击处理程序。 OnCreate 方法的代码如下所示:
SetContentView(Resource.Layout.HomeScreen); // loads the HomeScreen.axml as this activity's view
listView = FindViewById<ListView>(Resource.Id.List); // get reference to the ListView in the layout
// populate the listview with data
listView.Adapter = new HomeScreenAdapter(this, tableItems);
listView.ItemClick += OnListItemClick; // to be defined
最后,必须定义 ItemClick
处理程序;在本例中,它只显示一条 Toast
消息:
void OnListItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
var listView = sender as ListView;
var t = tableItems[e.Position];
Android.Widget.Toast.MakeText(this, t.Heading, Android.Widget.ToastLength.Short).Show();
}
生成的屏幕如下所示:
自定义行选择器颜色
当某一行被触摸时,应突出显示该行以供用户反馈。 当自定义视图指定为背景色时(就像 CustomView.axml 那样),它还会替代所选内容突出显示效果。 CustomView.axml 中的此代码行将背景设置为浅绿色,但这也意味着在触摸行时没有可视指示器:
android:background="#FFDAFF7F"
若要重新启用突出显示行为,并自定义使用的颜色,请改为将背景属性设置为自定义选择器。 该选择器将同时声明默认背景色和突出显示颜色。 Resources/Drawable/CustomSelector.xml 文件包含以下声明:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false"
android:state_selected="false"
android:drawable="@color/cellback" />
<item android:state_pressed="true" >
<shape>
<gradient
android:startColor="#E77A26"
android:endColor="#E77A26"
android:angle="270" />
</shape>
</item>
<item android:state_selected="true"
android:state_pressed="false"
android:drawable="@color/cellback" />
</selector>
若要引用自定义选择器,请将 CustomView.axml 中的背景属性更改为:
android:background="@drawable/CustomSelector"
所选行和相应的 Toast
消息现在如下所示:
防止自定义布局上的闪烁
Android 尝试通过缓存布局信息来提高 ListView
滚动的性能。 如果你有很长的数据滚动列表,还应在活动的 AXML 定义中设置 ListView
声明的 android:cacheColorHint
属性(与自定义行布局的背景相同的颜色值)。 如果不包含此提示,那么在用户滚动浏览具有自定义行背景色的列表时可能会出现“闪烁”。