分类:C#、Android、VS2015;
创建日期:2016-02-18
一、简介
自定义的列表视图通常用Resources/Layout文件夹下的axml文件中的资源来声明,适配器则通过Id去加载它。一个视图可以包含任意数量的类 (如 TextViews、 ImageViews 和其他控件) 以及自定义的颜色、字体和布局。
由于ListView的外观是由行的布局决定的,因此,若要更改列表视图的外观,只需要使用不同的行布局即可。
如果希望显示更复杂的布局 (如电子邮件、联系人列表),必须用自定义视图来实现。
二、运行截图
三、主要设计步骤
1、在colors.xml中添加颜色定义
在/Resources/values/ colors.xml文件中添加本示例选中行的颜色:
<color name="cellback">#FFDAFF7F</color>
2、添加ch0902CustomSelector.xml文件
在 /Resources/Drawable文件夹下添加该文件。
<?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>
3、添加ch0902_CustomView.axml文件
要为列表视图中每一行都创建一个自定义的布局,必须定义一个单独的布局文件。在此示例中,每行都用绿色背景、棕色文本以及一个右对齐的图像来呈现。这个单独的布局保存在/Resources/Layout/ch0902_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="@drawable/ch0902CustomSelector" 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>
虽然自定义行布局可以包含许多不同的控件,但是滚动性能可能会受到影响(特别是通过网络加载图像时)。
关于解决滚动性能问题的详细信息,可参考谷歌官网上的相关文章。
4、添加ch0902_Main.axml文件
在/Resources/Layout文件夹下添加该文件。
<?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: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>
5、添加ch0902MyBaseAdapter.cs文件
这个文件的关键是GetView方法,该方法使用Resource.Layout.ch0902_CustomView的资源ID加载自定义的axml,然后设置视图中控件的每个属性,最后返回结果。
using System.Collections.Generic; using Android.App; using Android.Views; using Android.Widget; namespace MyDemos.SrcDemos { public class ch0902MyBaseAdapter : BaseAdapter<ch0901TableItem> { List<ch0901TableItem> items; Activity context; public ch0902MyBaseAdapter(Activity context, List<ch0901TableItem> items) { this.context = context; this.items = items; } public override long GetItemId(int position) { return position; } public override ch0901TableItem 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; // 如果没有可复用的视图(view为null),就创建一个新视图 if (view == null) { view = context.LayoutInflater.Inflate(Resource.Layout.ch0902_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; } } }
6、添加ch0902Main.cs文件
在SrcDemos文件夹下添加该文件。
using System.Collections.Generic; using Android.App; using Android.OS; using Android.Widget; namespace MyDemos.SrcDemos { [Activity(Label = "【例9-2】 自定义视图")] public class ch0902Main : Activity { List<ch0901TableItem> tableItems = new List<ch0901TableItem>(); ListView listView; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.ch0902_Main); listView = FindViewById<ListView>(Resource.Id.List); tableItems.Add(new ch0901TableItem() { Heading = "Vegetables", SubHeading = "65 items", ImageResourceId = Resource.Drawable.ch09Vegetables }); tableItems.Add(new ch0901TableItem() { Heading = "Fruits", SubHeading = "17 items", ImageResourceId = Resource.Drawable.ch09Fruits }); tableItems.Add(new ch0901TableItem() { Heading = "Flower Buds", SubHeading = "5 items", ImageResourceId = Resource.Drawable.ch09FlowerBuds }); tableItems.Add(new ch0901TableItem() { Heading = "Legumes", SubHeading = "33 items", ImageResourceId = Resource.Drawable.ch09Legumes }); tableItems.Add(new ch0901TableItem() { Heading = "Bulbs", SubHeading = "18 items", ImageResourceId = Resource.Drawable.ch09Bulbs }); tableItems.Add(new ch0901TableItem() { Heading = "Tubers", SubHeading = "43 items", ImageResourceId = Resource.Drawable.ch09Tubers }); listView.Adapter = new ch0902MyBaseAdapter(this, tableItems); } } }
四、代码解释
1、行复用
如果所有行可在一个屏幕内全部显示出来,此时不需要复用。但是,当显示成百上千行的数据时,一次创建这么多的视图但只能使用几行太浪费内存空间了。为了避免这种情况,当某行从屏幕上消失时,可将其保存到一个队列中,以便复用。
具体实现办法是:当用户滚动屏幕时,先判断在convertView参数中传递的视图实例,如果该值为null,就创建新的视图实例,否则重新设置该对象的属性以便复用它。
GetView方法应该按下面的模式来复用行视图:
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView; // re-use an existing view, if one is supplied
if (view == null) view = context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null);
view.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position];
return view;
}
创建新的视图前,自定义适配器应该总是实现convertView对象,这样可确保显示大量的列表数据时不会导致内存溢出。
注意,某些适配器的实现(例如CursorAdapter)可能没有GetView方法,不过,这些适配器采用的策略是将GetView的职责分成两种不同的方法NewView和BindView,从而确保强制执行复用的行。
2、快速滚动
当一个ListView包含多行数据时,可利用快速滚动导航到该列表的不同部分(Api 11及更高版本都支持快速滚动)。
将FastScrollEnabled属性设置为true,即可显示快速滚动手柄(handle):
ListView.FastScrollEnabled = true;
3、通过C#代码设置或查找选项
设置列表中的初始化选项时,用SetItemChecked方法实现即可,例如:
listview.SetItemChecked(1, true);
也可能需要从多个选项中查找某个已选择的单项,例如:
FindViewById<ListView>(Android.Resource.Id.List).CheckedItemPosition
要确定在多选模式中用户选择了哪些行,需要遍历用稀疏数组(sparseArray)保存的所有可选项,该数组类似于一个保存更改记录的字典,所以必须遍历整个数组才能找到所有选项,例如:
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、自定义所选行的颜色
当用户选择某行时,一般应突出显示该行。突出显示行是在ch0902_CustomView.axml文件中实现的(将背景设置为淡绿色),重启突出显示的行也是用它来实现,但是采用的是反色背景。因此,先在 /Resources/Drawable/ch0902CustomSelector.xml 文件中包含对应的声明,然后在ch0902_CustomView.axml 文件中通过引用自定义的选择器来改变背景:
android:background="@drawable/ch0902CustomSelector"
当选择某行时(按住不抬起来),即得到运行截图所示的效果。
5、避免滚动闪烁
安卓系统是通过缓存布局信息来改善ListView的滚动性能的。如果列表视图中有比较长的滚动列表的数据,还应该在axml布局文件的ListView控件中声明android:cacheColorHint属性(将其值设置为和自定义行布局中的背景色相同),否则的话,滚动列表时就可能会出现一闪一闪(flicker)的情况。