Android ExpandableListView 教程(附 Android 自定义适配器)






4.62/5 (5投票s)
在本 Android 可扩展列表视图教程中,我们将学习如何在 Android 中创建一个可扩展列表。
在本 Android 可扩展列表视图教程中,我们将学习如何在 Android 中创建一个可扩展列表。您可以将其视为一个 可扩展列表 Android 示例。 在这里,我们将创建一个 自定义 ExpandablelistView
,用户可以在菜单中添加数据,甚至可以添加新类别。 为了添加新类别,我们将使用一个下拉微调器,并使用一个搜索按钮来在该类别中添加产品。
什么是 Android ExpandableListView
Android 可扩展列表视图以垂直滚动的两级列表显示项目。 它与 ListView
的不同之处在于,它允许两个级别:可以单独展开以显示其子级的组。 您可以将监听器附加到 Android ExpandableListView
,以监听组或单个子项上的 OnClick
事件。
在本教程中,我们将创建不同的类别部分,例如 Vegetables
、Fruits
、Grocery
、Books
等。在每个类别中,我们可以有不同的产品。 例如:我们可以在 Vegetables
中添加 Asparagus
、Potato
、Cabbage
等。 我们将把这个 Android 可扩展列表视图分解为多个部分,以便您能够轻松理解代码及其应用程序。
创建新项目
- 转到 文件 → 新建 → 新项目,然后输入您的应用程序名称(我们命名为 Android ExpandablelistView Tutorial)。
- 输入公司域名,用于在全球范围内唯一标识您的应用程序包。
- 选择项目位置和最低 SDK,然后在下一个屏幕上,选择 空 Activity,因为我们将自己添加大部分代码。 然后单击下一步。
- 选择一个 Activity 名称。 确保选中“生成布局文件”复选框,否则我们必须自己生成它。然后单击“完成”。 我们将 Activity 名称保留为
MainActivity
。 - Gradle 将配置您的项目并解决依赖关系。 完成后,请继续执行下一步。 确保
build.grade
具有以下代码,尤其是 compile ‘com.android.support:appcompat-v7:23.1.1’
首先,我们将讨论其布局,然后我们将介绍 MainActivity
。
Android ExpandablelistView 教程的布局
在布局中,我们将有一个微调器来选择类别,一个按钮来添加产品,最后是一个 Android 可扩展列表视图。
activity_main.xml
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Spinner android:id="@+id/department"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:textStyle="bold"
android:paddingLeft="100sp"/>
<Button android:id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/department"
android:text="Add" />
<EditText android:id="@+id/product"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/add"
android:layout_alignParentLeft="true"
android:layout_below="@id/department"
android:layout_toLeftOf="@id/add"
android:ems="10"
android:hint="Enter Product for above Category"
android:inputType="text" />
<TextView android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/product"
android:layout_margin="5dp"
android:background="#000080"
android:padding="5dp"
android:text="Different Categories with Products..."
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" android:textColor="#ffffff"/>
<ExpandableListView android:id="@+id/myList"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:layout_below="@id/textView1" />
</RelativeLayout>
此外,在与 activity_main.xml 相同的路径中创建另外两个布局文件,即 child_row.xml 和 group_heading.xml。 child_row.xml 将负责为 Android 可扩展列表视图的不同类别下添加的产品生成布局,而 group_heading.xml 将为主要类别生成视图。
child_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/sequence"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:paddingLeft="35sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/childItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/sequence"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
group_heading.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="55dip"
android:orientation="vertical" >
<TextView
android:id="@+id/heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="35sp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textStyle="bold" />
</LinearLayout>
在 strings.xml 中进行以下更改
<resources>
<string name="app_name">"Android Expandablelistview Tutorial "
</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_main">MainActivity</string>
<string-array name="dept_array">
<item>Vegetable</item>
<item>Fruits</item>
<item>Grocery</item>
<item>Electronics</item>
<item>Books</item>
<item>Language</item>
</string-array>
</resources>
因此,Android ExpandablelistView Tutorial 的布局已完成。 现在,让我们开始为布局添加生命,即 MainActivity
。
Android 自定义适配器
在 MainActivity.java 所在的同一路径下创建一个名为 MyListAdapter.java 的新 Java 文件,即 …/AndroidExpandablelistviewTutorial/app/src/main/java/com/androidtutorialpoint/androidexpandablelistviewtutorial/MyListAdapter.java。
适配器将 Android ExpandableListView
与底层数据链接起来。 此接口的实现将提供对子项(按组分类)的数据的访问权限,并实例化子项和组的视图。
MyListAdapter.java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
public class MyListAdapter extends BaseExpandableListAdapter {
private Context context;
private ArrayList<HeaderInfo> deptList;
public MyListAdapter(Context context, ArrayList<HeaderInfo> deptList) {
this.context = context;
this.deptList = deptList;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
ArrayList<DetailInfo> productList =
deptList.get(groupPosition).getProductList();
return productList.get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View view, ViewGroup parent) {
DetailInfo detailInfo = (DetailInfo) getChild(groupPosition, childPosition);
if (view == null) {
LayoutInflater infalInflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = infalInflater.inflate(R.layout.child_row, null);
}
TextView sequence = (TextView) view.findViewById(R.id.sequence);
sequence.setText(detailInfo.getSequence().trim() + ") ");
TextView childItem = (TextView) view.findViewById(R.id.childItem);
childItem.setText(detailInfo.getName().trim());
return view;
}
@Override
public int getChildrenCount(int groupPosition) {
ArrayList<DetailInfo> productList =
deptList.get(groupPosition).getProductList();
return productList.size();
}
@Override
public Object getGroup(int groupPosition) {
return deptList.get(groupPosition);
}
@Override
public int getGroupCount() {
return deptList.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isLastChild, View view,
ViewGroup parent) {
HeaderInfo headerInfo = (HeaderInfo) getGroup(groupPosition);
if (view == null) {
LayoutInflater inf = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inf.inflate(R.layout.group_heading, null);
}
TextView heading = (TextView) view.findViewById(R.id.heading);
heading.setText(headerInfo.getName().trim());
return view;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
此外,我们将为类别类型及其关联的产品创建单独的类。 Category
的类型为 HeaderInfo
。 在与 MyListAdapter.java 相同的路径下创建一个名为 HeaderInfo.java 的类。
HeaderInfo.java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;
import java.util.ArrayList;
public class HeaderInfo {
private String name;
private ArrayList<DetailInfo> productList = new ArrayList<DetailInfo>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ArrayList<DetailInfo> getProductList() {
return productList;
}
public void setProductList(ArrayList<DetailInfo> productList) {
this.productList = productList;
}
}
每个类别中的产品类型为 DetailInfo
,因此创建一个名为 DetailInfo.java 的新类
DetailInfo.java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;
public class DetailInfo {
private String sequence = "";
private String name = "";
public String getSequence() {
return sequence;
}
public void setSequence(String sequence) {
this.sequence = sequence;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
最后,我们将通过 MainActivity
在 Android ExpandablelistView
教程的列表中填充类别和产品。
MainActivity.java
package com.androidtutorialpoint.androidexpandablelistviewtutorial;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
public class MainActivity extends AppCompatActivity implements OnClickListener {
private LinkedHashMap<String, HeaderInfo> mySection = new LinkedHashMap<>();
private ArrayList<HeaderInfo> SectionList = new ArrayList<>();
private MyListAdapter listAdapter;
private ExpandableListView expandableListView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Spinner spinner = (Spinner) findViewById(R.id.department);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.dept_array, android.R.layout.simple_spinner_item);
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// Apply the adapter to the spinner
spinner.setAdapter(adapter);
//Just add some data to start with
AddProduct();
//get reference to the ExpandableListView
expandableListView = (ExpandableListView) findViewById(R.id.myList);
//create the adapter by passing your ArrayList data
listAdapter = new MyListAdapter(MainActivity.this, SectionList);
//attach the adapter to the list
expandableListView.setAdapter(listAdapter);
//expand all Groups
expandAll();
//add new item to the List
Button add = (Button) findViewById(R.id.add);
add.setOnClickListener(this);
//listener for child row click
expandableListView.setOnChildClickListener(myListItemClicked);
//listener for group heading click
expandableListView.setOnGroupClickListener(myListGroupClicked);
}
public void onClick(View v) {
switch (v.getId()) {
//add entry to the List
case R.id.add:
Spinner spinner = (Spinner) findViewById(R.id.department);
String department = spinner.getSelectedItem().toString();
EditText editText = (EditText) findViewById(R.id.product);
String product = editText.getText().toString();
editText.setText("");
//add a new item to the list
int groupPosition = addProduct(department,product);
//notify the list so that changes can take effect
listAdapter.notifyDataSetChanged();
//collapse all groups
collapseAll();
//expand the group where item was just added
expandableListView.expandGroup(groupPosition);
//set the current group to be selected so that it becomes visible
expandableListView.setSelectedGroup(groupPosition);
break;
}
}
//method to expand all groups
private void expandAll() {
int count = listAdapter.getGroupCount();
for (int i = 0; i < count; i++){
expandableListView.expandGroup(i);
}
}
//method to collapse all groups
private void collapseAll() {
int count = listAdapter.getGroupCount();
for (int i = 0; i < count; i++){
expandableListView.collapseGroup(i);
}
}
//load some initial data into out list
private void AddProduct(){
addProduct("Vegetable","Potato");
addProduct("Vegetable","Cabbage");
addProduct("Vegetable","Onion");
addProduct("Fruits","Apple");
addProduct("Fruits","Orange");
}
//our child listener
private OnChildClickListener myListItemClicked = new OnChildClickListener() {
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
//get the group header
HeaderInfo headerInfo = SectionList.get(groupPosition);
//get the child info
DetailInfo detailInfo = headerInfo.getProductList().get(childPosition);
//display it or do something with it
Toast.makeText(getBaseContext(), "Clicked on Detail " + headerInfo.getName()
+ "/" + detailInfo.getName(), Toast.LENGTH_LONG).show();
return false;
}
};
//our group listener
private OnGroupClickListener myListGroupClicked = new OnGroupClickListener() {
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
//get the group header
HeaderInfo headerInfo = SectionList.get(groupPosition);
//display it or do something with it
Toast.makeText(getBaseContext(), "Child on Header " + headerInfo.getName(),
Toast.LENGTH_LONG).show();
return false;
}
};
//here we maintain our products in various departments
private int addProduct(String department, String product){
int groupPosition = 0;
//check the hash map if the group already exists
HeaderInfo headerInfo = mySection.get(department);
//add the group if doesn't exists
if(headerInfo == null){
headerInfo = new HeaderInfo();
headerInfo.setName(department);
mySection.put(department, headerInfo);
SectionList.add(headerInfo);
}
//get the children for the group
ArrayList<DetailInfo> productList = headerInfo.getProductList();
//size of the children list
int listSize = productList.size();
//add to the counter
listSize++;
//create a new child and add that to the group
DetailInfo detailInfo = new DetailInfo();
detailInfo.setSequence(String.valueOf(listSize));
detailInfo.setName(product);
productList.add(detailInfo);
headerInfo.setProductList(productList);
//find the group position inside the list
groupPosition = SectionList.indexOf(headerInfo);
return groupPosition;
}
}
在上面的代码中,我们首先创建一个微调器,以在列表中添加类别,并将其与 ArrayAdapter
相关联。 此 ArrayAdapter
填充了 Strings.xml 中定义为 string-array 的项目。 填充 Spinner
后,我们通过 addProduct()
逐个将产品添加到可扩展列表视图中。 在 addProduct()
中,我们首先检查产品是否已存在。 如果不存在,则将其添加到相应的类别。 此外,此函数返回该产品在该类别中的位置,该位置在 onClick()
中使用。 添加初始产品后,我们将 MyListAdapter
附加到可扩展列表视图。 该适配器将负责在列表视图中添加和更新数据。 最后,为按钮添加了监听器,这样每当单击按钮时,就会调用 onClick()
,并将新项目添加到列表中。
让我们告诉您这个可扩展列表 Android 示例是如何整体工作的。 首先,一些项目将通过 AddProduct()
自动添加。 现在,如果用户想在 ExpandableListView
中添加新的类别和产品,则按钮 Add 将发挥作用。 单击 Add 按钮后,产品将通过 groupPosition = addProduct(department,product)
添加,并且 ExpandableListView
将通过 listAdapter.notifyDataSetChanged()
更新,以便更改生效。
因此,我们的 Android ExpandablelistView
教程终于完成了。 运行此应用程序,并在 ExpandablelistView
中添加您想要的新类别和产品。 您还可以参考本教程开头提供的 Android ExpandablelistView
教程的演示。