MonoAndroid:用 C# 编写 ExpandableListView 和 BaseExpandableListAdapter
创建 ExpandableListView 并使用派生的 BaseExpandableListAdapter 类填充它。
引言
在过去的几个月里,我一直在使用 MonoAndroid
SDK 学习 Android 移动编程。我在这里发表了几篇文章,参考资料在文末。人们问我,既然 Android 原生支持语法更接近 JAVA
而非 C#
的语言,为什么还要用 C#
编写代码。我的简单回答是,我目前已经从事 VC++ 和 C# 工作了将近 9 年,对我来说,用熟悉的语言学习移动编程,比用 Java 编程同类功能要容易得多。每个人都有自己的观点,我尊重这些观点。
在这篇文章中,我将演示如何创建自定义的 BaseExpandableListAdapter,它被 ExpandableListView 用来创建父子类型的视图。在 Android 世界里,它们通常被称为组 (Group) 及其子项 (Child items)。
我们将一步一步进行。
- 通过选择 新建 -> 解决方案 来创建 Android 应用程序,并为它命名为“ExpendListBox”。
- 项目创建完成后,打开 **资源文件夹 -> 布局** 并添加一个新的布局文件,ListControl_Group.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" android:minWidth="25px" android:minHeight="25px" android:background="#ffdf872c"> <TextView android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/txtLarge" android:layout_marginLeft="42.0dp" android:layout_marginRight="10dp" android:background="#ffdf872c" android:layout_marginTop="10.0dp" android:layout_marginBottom="6.7dp" android:textColor="#ffbd1c1c" android:shadowColor="#fff9ea14" android:gravity="center_vertical" /> </LinearLayout>
在空白布局中,我添加了一个中等大小的文本视图,并将背景改为深橙色(我说谎了,是某种接近橙色的随机颜色),另外还调整了边距。
- 再添加一个布局文件 ListControl_Child.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" android:id="@+id/linearLayout1" android:minWidth="25px" android:minHeight="25px"> <TextView android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/txtSmall" android:layout_marginLeft="26.0dp" /> </LinearLayout>
在空白布局中,我添加了一个小号文本视图,另外还调整了边距。
- 现在,添加一个新的 C# 文件并将其命名为 expendListAdapter.cs,并实现
BaseExpandableListAdapter
。骨架类如下所示:public class ExpendListAdapter: BaseExpandableListAdapter { #region implemented abstract members of BaseExpandableListAdapter public override Java.Lang.Object GetChild (int groupPosition, int childPosition) { throw new NotImplementedException (); } public override long GetChildId (int groupPosition, int childPosition) { throw new NotImplementedException (); } public override int GetChildrenCount (int groupPosition) { throw new NotImplementedException (); } public override View GetChildView (int groupPosition, int childPosition, bool isLastChild, View convertView, ViewGroup parent) { throw new NotImplementedException (); } public override Java.Lang.Object GetGroup (int groupPosition) { throw new NotImplementedException (); } public override long GetGroupId (int groupPosition) { throw new NotImplementedException (); } public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent) { throw new NotImplementedException (); } public override bool IsChildSelectable (int groupPosition, int childPosition) { throw new NotImplementedException (); } public override int GroupCount { get { throw new NotImplementedException (); } } public override bool HasStableIds { get { throw new NotImplementedException (); } } #endregion } }
- 现在添加三个私有成员,类型分别为
Dictionary<string, List<string>>
、List<string>
和Activity
。 - 添加 **带参数的构造函数**,它接受
Activity
和Dictionary<string, List<string>>
作为参数,并将这些值赋给第一步列出的私有成员。 - 对于
List<string>
,为字典中的所有键赋予值。 - 编写子项方法,如 GetChild、GetChildId、GetChildrenCount 和 IsChildSelectable 非常简单,因为我们只是从字典中返回相应的项。请查看下面的代码。
Dictionary<string, List<string>> _dictGroup =null; List<string> _lstGroupID = null; Activity _activity; public ExpendListAdapter (Activity activity, Dictionary<string, List<string>> dictGroup) { _dictGroup = dictGroup; _activity = activity; _lstGroupID = dictGroup.Keys.ToList(); } public override Java.Lang.Object GetChild (int groupPosition, int childPosition) { return _dictGroup [_lstGroupID [groupPosition]] [childPosition]; } public override long GetChildId (int groupPosition, int childPosition) { return childPosition; } public override int GetChildrenCount (int groupPosition) { return _dictGroup [_lstGroupID [groupPosition]].Count; } public override bool IsChildSelectable (int groupPosition, int childPosition) { return true; }
- 同样,组项方法,如 GetGroup、GetGroupId 和 GroupCount 也非常简单,因为我们只是从字典中返回相应的项。请查看下面的代码。
public override Java.Lang.Object GetGroup (int groupPosition) { return _lstGroupID [groupPosition]; } public override long GetGroupId (int groupPosition) { return groupPosition; } public override int GroupCount { get { return _dictGroup.Count; } }
GetChildView 和 GetGroupView 是负责膨胀和填充视图的两个方法,我们将在下一步进行解释。
- 现在添加三个私有成员,类型分别为
- 现在是时候在
ExpendListAdapter
类中添加 GetGroupView 和 GetChildView 了。这是 GetGroupView 的代码:public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent) { var item = _lstGroupID [groupPosition]; if (convertView == null) convertView = _activity.LayoutInflater.Inflate (Resource.Layout.ListControl_Group, null); var textBox = convertView.FindViewById<TextView> (Resource.Id.txtLarge); textBox.SetText (item, TextView.BufferType.Normal); return convertView; }
在这里,我检查 convertView 是否有内存。如果没有,则膨胀
ListControl_Group
布局。膨胀完成后,尝试找到文本视图并用组标题更新它。对于 GetChildView,编码与 GetGroupView 类似,只是膨胀的不是 ListControl_Group,而是 ListControl_Child。代码如下:
public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent) { var item = _dictGroup [_lstGroupID [groupPosition]] [childPosition]; if (convertView == null) convertView = _activity.LayoutInflater.Inflate (Resource.Layout.ListControl_ChildItem, null); var textBox = convertView.FindViewById<TextView> (Resource.Id.txtSmall); textBox.SetText (item, TextView.BufferType.Normal); return convertView; }
- 在 Main.axml 文件中,添加
ExpandableListView
控件。之后 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"> <ExpandableListView android:minWidth="25px" android:minHeight="25px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/ctlExListBox" /> </LinearLayout>
- 在 MainActivity.cs 文件中,添加一个名为 CreateExpendableListData 的方法,该方法将创建临时数据用于在 ListView 中显示。代码如下:
void CreateExpendableListData () { for (int iGroup = 1; iGroup <= 3; iGroup++) { var lstChild = new List<string> (); for (int iChild = 1; iChild <= 3; iChild++) { lstChild.Add (string.Format ("Group {0} Child {1}", iGroup, iChild)); } dictGroup.Add (string.Format ("Group {0}", iGroup), lstChild); } lstKeys = new List<string>(dictGroup.Keys); }
此方法将添加三个组,每个组有三个子项。
dictGroup
是类型为 Dictionary<string, List<string>> 的局部字段,lstKeys
是类型为 List<string> 。
- 现在,使用 FindViewById<ExpandableListView> 并以“Resource.Id.ctlExListBox”作为 ID 来查找 ExpandableListView ,然后通过传递当前活动和我们在上一步创建的字典来创建我们的 **customListAdapter**,并将其分配给 ExpandableListView 对象的 adapter 属性。
var ctlExListBox = FindViewById<ExpandableListView> (Resource.Id.ctlExListBox); ctlExListBox.SetAdapter (new ExpendListAdapter (this, dictGroup));
下图解释了 BaseExpandableListAdapter 的工作原理。
- 现在添加
ctlExListBox.ChildClick
事件处理程序,并在处理程序代码中添加一个 Toast 来生成点击用户的消息。ctlExListBox.ChildClick += delegate(object sender, ExpandableListView.ChildClickEventArgs e) { var itmGroup = lstKeys [e.GroupPosition]; var itmChild = dictGroup [itmGroup] [e.ChildPosition]; Toast.MakeText (this, string.Format ("You Click on Group {0} with child {1}", itmGroup, itmChild), ToastLength.Long).Show (); };
您可以在 此处 阅读更多关于Toast
类的信息。
- 现在编译并运行!
关注点
我使用了MonoAndroid (C#)和Xamarin Studio来构建本教程。请留意,如果我继续学习,您可能会期待更多文章即将发布。
尽管 Xamarin Studio 是专有软件,但他们提供免费的入门版本来构建、测试和发布 Android 应用程序。我正在使用它进行学习,并在此处创建分步教程。
本系列文章!
- MonoAndroid:在您的移动应用程序中使用 GridView
- MonoAndroid:在移动应用程序中使用 TabHost
- MonoAndroid:在移动应用中使用 Fragments
- MonoAndroid:使用 dotnet Web 服务(ASMX)
- MonoAndroid:使用 Started Service
- MonoAndroid:调用次级活动
- MonoAndroid:用 C# 编写自定义通用 BaseAdapter
- MonoAndroid:使用 AlertDialog
本系列技巧/窍门
历史
- 2013年11月01日:第一个版本
- 2013年11月22日:更新了其他文章部分