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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (5投票s)

2016年11月1日

CPOL

4分钟阅读

viewsIcon

54037

在本 Android 可扩展列表视图教程中,我们将学习如何在 Android 中创建一个可扩展列表。

在本 Android 可扩展列表视图教程中,我们将学习如何在 Android 中创建一个可扩展列表。您可以将其视为一个 可扩展列表 Android 示例。 在这里,我们将创建一个 自定义 ExpandablelistView,用户可以在菜单中添加数据,甚至可以添加新类别。 为了添加新类别,我们将使用一个下拉微调器,并使用一个搜索按钮来在该类别中添加产品。

Android ExpandablelistView TutorialAndroid ExpandablelistView Tutorial Android ExpandablelistView Tutorial

什么是 Android ExpandableListView

Android 可扩展列表视图以垂直滚动的两级列表显示项目。 它与 ListView 的不同之处在于,它允许两个级别:可以单独展开以显示其子级的组。 您可以将监听器附加到 Android ExpandableListView,以监听组或单个子项上的 OnClick 事件。

在本教程中,我们将创建不同的类别部分,例如 VegetablesFruitsGroceryBooks 等。在每个类别中,我们可以有不同的产品。 例如:我们可以在 Vegetables 中添加 AsparagusPotatoCabbage 等。 我们将把这个 Android 可扩展列表视图分解为多个部分,以便您能够轻松理解代码及其应用程序。

创建新项目

  1. 转到 文件 → 新建 → 新项目,然后输入您的应用程序名称(我们命名为 Android ExpandablelistView Tutorial)。
  2. 输入公司域名,用于在全球范围内唯一标识您的应用程序包。
  3. 选择项目位置和最低 SDK,然后在下一个屏幕上,选择 空 Activity,因为我们将自己添加大部分代码。 然后单击下一步。
  4. 选择一个 Activity 名称。 确保选中“生成布局文件”复选框,否则我们必须自己生成它。然后单击“完成”。 我们将 Activity 名称保留为 MainActivity
  5. 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.xmlgroup_heading.xmlchild_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 教程的演示。

© . All rights reserved.