65.9K
CodeProject 正在变化。 阅读更多。
Home

MonoAndroid:用 C# 编写 ExpandableListView 和 BaseExpandableListAdapter

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (15投票s)

2013年11月1日

CPOL

4分钟阅读

viewsIcon

48699

downloadIcon

1047

创建 ExpandableListView 并使用派生的 BaseExpandableListAdapter 类填充它。

引言

在过去的几个月里,我一直在使用 MonoAndroid SDK 学习 Android 移动编程。我在这里发表了几篇文章,参考资料在文末。人们问我,既然 Android 原生支持语法更接近 JAVA 而非 C# 的语言,为什么还要用 C# 编写代码。我的简单回答是,我目前已经从事 VC++ 和 C# 工作了将近 9 年,对我来说,用熟悉的语言学习移动编程,比用 Java 编程同类功能要容易得多。每个人都有自己的观点,我尊重这些观点

在这篇文章中,我将演示如何创建自定义的 BaseExpandableListAdapter,它被 ExpandableListView 用来创建父子类型的视图。在 Android 世界里,它们通常被称为组 (Group) 及其子项 (Child items)。

我们将一步一步进行。

  1. 通过选择 新建 -> 解决方案 来创建 Android 应用程序,并为它命名为“ExpendListBox”。


  2.  
  3. 项目创建完成后,打开 **资源文件夹 -> 布局** 并添加一个新的布局文件,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>
        

    在空白布局中,我添加了一个中等大小的文本视图,并将背景改为深橙色(我说谎了,是某种接近橙色的随机颜色),另外还调整了边距。


  4. 再添加一个布局文件 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>
        

    在空白布局中,我添加了一个小号文本视图,另外还调整了边距。



  5. 现在,添加一个新的 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>,为字典中的所有键赋予值。
    • 编写子项方法,如 GetChildGetChildIdGetChildrenCount 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;
      }
      
    • 同样,组项方法,如 GetGroupGetGroupId 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 是负责膨胀和填充视图的两个方法,我们将在下一步进行解释。



  6. 现在是时候在 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;
    }
    

  7. 在 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>
    

  8. 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>



  9. 现在,使用 FindViewById<ExpandableListView> 并以“Resource.Id.ctlExListBox”作为 ID 来查找 ExpandableListView ,然后通过传递当前活动和我们在上一步创建的字典来创建我们的 **customListAdapter**,并将其分配给 ExpandableListView 对象的 adapter 属性。
    var ctlExListBox = FindViewById<ExpandableListView> (Resource.Id.ctlExListBox);
    	ctlExListBox.SetAdapter (new ExpendListAdapter (this, dictGroup));

    下图解释了 BaseExpandableListAdapter 的工作原理。


  10. 现在添加 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 类的信息。

  11. 现在编译并运行!

      

关注点

我使用了MonoAndroid (C#)Xamarin Studio来构建本教程。请留意,如果我继续学习,您可能会期待更多文章即将发布。

尽管 Xamarin Studio 是专有软件,但他们提供免费的入门版本来构建、测试和发布 Android 应用程序。我正在使用它进行学习,并在此处创建分步教程。

本系列文章!

本系列技巧/窍门

历史  

  • 2013年11月01日:第一个版本
  • 2013年11月22日:更新了其他文章部分
© . All rights reserved.