NavigationView内的Android ExpandableListView

时间:2020-02-23 14:28:54  来源:igfitidea点击:

在本教程中,我们将实现一个应用程序,其中在NavigationView中显示项目的ExpandableListView。

NavigationView内的Android ExpandableListView

正如我们在NavigationView教程中看到的那样,我们可以有子菜单,但不能像在ExpandableListViews中那样展开/折叠这些子菜单。
因此,让我们尝试在NavigationView中实现ExpandableListView。

首先创建一个新的Android Studio项目。
选择活动模板作为"导航抽屉活动",如下所示。

这将我们的活动类封装在DrawerLayout中,并且默认情况下其中添加了NavigationView。
在我们的xml编辑器中,我们看到活动的以下设计:

现在,我们需要做的就是用一个ExpandableListView替换菜单。

NavigationView项目结构内部的Android ExpandableListView

在此应用程序中,我们的每个菜单都在WebView中打开一个URL。
某些菜单可以扩展,而其他菜单则不能。
我们将使用" MenuModel"类填充数据。

Android ExpandableListView NavigationView代码

让我们看一下布局文件" activity_main.xml"

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:id="@+id/drawer_layout"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="true"
  tools:openDrawer="start">

  <include
      layout="@layout/app_bar_main"
      android:layout_width="match_parent"
      android:layout_height="match_parent" 

  <android.support.design.widget.NavigationView
      android:id="@+id/nav_view"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_gravity="start"
      android:fitsSystemWindows="true"
      app:headerLayout="@layout/nav_header_main">

      <ExpandableListView
          android:id="@+id/expandableListView"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginTop="@dimen/nav_header_height"
          android:background="@android:color/white"
          android:dividerHeight="0dp"
          android:groupIndicator="@null" 

  </android.support.design.widget.NavigationView>

</android.support.v4.widget.DrawerLayout>

下面给出了content_main.xml类的代码。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  app:layout_behavior="@string/appbar_scrolling_view_behavior"
  tools:context="com.theitroad.navigationviewexpandablelistview.MainActivity"
  tools:showIn="@layout/app_bar_main">

  <WebView
      android:id="@+id/webView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" 

</android.support.constraint.ConstraintLayout>

下面给出了将在Adapter类中放大的" list_group_header.xml"和" list_group_child.xml"布局的代码。

list_group_header.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical">

  <TextView
      android:id="@+id/lblListHeader"
      android:layout_width="match_parent"
      android:layout_height="?attr/listPreferredItemHeightSmall"
      android:gravity="center_vertical"
      android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
      android:paddingRight="?attr/listPreferredItemPaddingRight"
      android:textColor="#1f2124"
      android:textSize="16sp" 
</LinearLayout>

list_group_child.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical"
  android:padding="8dp">

  <TextView
      android:id="@+id/lblListItem"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft" 
</LinearLayout>

MenuModel.java的代码如下。

package com.theitroad.navigationviewexpandablelistview;

public class MenuModel {

  public String menuName, url;
  public boolean hasChildren, isGroup;

  public MenuModel(String menuName, boolean isGroup, boolean hasChildren, String url) {

      this.menuName = menuName;
      this.url = url;
      this.isGroup = isGroup;
      this.hasChildren = hasChildren;
  }
}

本教程中,同一类可用于ExpandableListView的标题行和子行。

下面给出MainActivity.java类的代码。

package com.theitroad.navigationviewexpandablelistview;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebView;
import android.widget.ExpandableListView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class MainActivity extends AppCompatActivity
      implements NavigationView.OnNavigationItemSelectedListener {

  ExpandableListAdapter expandableListAdapter;
  ExpandableListView expandableListView;
  List<MenuModel> headerList = new ArrayList<>();
  HashMap<MenuModel, List<MenuModel>> childList = new HashMap<>();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Toolbar toolbar = findViewById(R.id.toolbar);
      setSupportActionBar(toolbar);

      FloatingActionButton fab = findViewById(R.id.fab);
      fab.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                      .setAction("Action", null).show();
          }
      });

      expandableListView = findViewById(R.id.expandableListView);
      prepareMenuData();
      populateExpandableList();

      DrawerLayout drawer = findViewById(R.id.drawer_layout);
      ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
              this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
      drawer.addDrawerListener(toggle);
      toggle.syncState();

      NavigationView navigationView = findViewById(R.id.nav_view);
      navigationView.setNavigationItemSelectedListener(this);
  }

  @Override
  public void onBackPressed() {
      DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
      if (drawer.isDrawerOpen(GravityCompat.START)) {
          drawer.closeDrawer(GravityCompat.START);
      } else {
          super.onBackPressed();
      }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
      //Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.main, menu);
      return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
      //Handle action bar item clicks here. The action bar will
      //automatically handle clicks on the Home/Up button, so long
      //as you specify a parent activity in AndroidManifest.xml.
      int id = item.getItemId();

      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
          return true;
      }

      return super.onOptionsItemSelected(item);
  }

  @SuppressWarnings("StatementWithEmptyBody")
  @Override
  public boolean onNavigationItemSelected(MenuItem item) {
      //Handle navigation view item clicks here.
      int id = item.getItemId();

      if (id == R.id.nav_camera) {
          //Handle the camera action
      } else if (id == R.id.nav_gallery) {

      } else if (id == R.id.nav_slideshow) {

      } else if (id == R.id.nav_manage) {

      } else if (id == R.id.nav_share) {

      } else if (id == R.id.nav_send) {

      }

      DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
      drawer.closeDrawer(GravityCompat.START);
      return true;
  }

  private void prepareMenuData() {

      MenuModel menuModel = new MenuModel("Android WebView Tutorial", true, false, "https://www.theitroad.local/9333/android-webview-example-tutorial"); //Menu of Android Tutorial. No sub menus
      headerList.add(menuModel);

      if (!menuModel.hasChildren) {
          childList.put(menuModel, null);
      }

      menuModel = new MenuModel("Java Tutorials", true, true, ""); //Menu of Java Tutorials
      headerList.add(menuModel);
      List<MenuModel> childModelsList = new ArrayList<>();
      MenuModel childModel = new MenuModel("Core Java Tutorial", false, false, "https://www.theitroad.local/7153/core-java-tutorial");
      childModelsList.add(childModel);

      childModel = new MenuModel("Java FileInputStream", false, false, "https://www.theitroad.local/19187/java-fileinputstream");
      childModelsList.add(childModel);

      childModel = new MenuModel("Java FileReader", false, false, "https://www.theitroad.local/19115/java-filereader");
      childModelsList.add(childModel);

      if (menuModel.hasChildren) {
          Log.d("API123","here");
          childList.put(menuModel, childModelsList);
      }

      childModelsList = new ArrayList<>();
      menuModel = new MenuModel("Python Tutorials", true, true, ""); //Menu of Python Tutorials
      headerList.add(menuModel);
      childModel = new MenuModel("Python AST – Abstract Syntax Tree", false, false, "https://www.theitroad.local/19243/python-ast-abstract-syntax-tree");
      childModelsList.add(childModel);

      childModel = new MenuModel("Python Fractions", false, false, "https://www.theitroad.local/19226/python-fractions");
      childModelsList.add(childModel);

      if (menuModel.hasChildren) {
          childList.put(menuModel, childModelsList);
      }
  }

  private void populateExpandableList() {

      expandableListAdapter = new ExpandableListAdapter(this, headerList, childList);
      expandableListView.setAdapter(expandableListAdapter);

      expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
          @Override
          public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {

              if (headerList.get(groupPosition).isGroup) {
                  if (!headerList.get(groupPosition).hasChildren) {
                      WebView webView = findViewById(R.id.webView);
                      webView.loadUrl(headerList.get(groupPosition).url);
                      onBackPressed();
                  }
              }

              return false;
          }
      });

      expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
          @Override
          public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {

              if (childList.get(headerList.get(groupPosition)) != null) {
                  MenuModel model = childList.get(headerList.get(groupPosition)).get(childPosition);
                  if (model.url.length() > 0) {
                      WebView webView = findViewById(R.id.webView);
                      webView.loadUrl(model.url);
                      onBackPressed();
                  }
              }

              return false;
          }
      });
  }
}

在" prepareMenuData()"中,我们用伪数据填充数据结构。

对于组标题,我们使用MenuModels的ArrayList。

子行数据填充在HashMap中,其中键是标题MenuModel,而值是子MenuModels的列表。

在ExpandableListView的onGroupClick和onChildClick侦听器中,我们获取当前位置的url,并将其加载到Activity的WebView中。

让我们看一下ExpandableListAdapter.java类。

package com.theitroad.navigationviewexpandablelistview;

import android.content.Context;
import android.graphics.Typeface;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

import java.util.HashMap;
import java.util.List;

public class ExpandableListAdapter extends BaseExpandableListAdapter {
  private Context context;
  private List<MenuModel> listDataHeader;
  private HashMap<MenuModel, List<MenuModel>> listDataChild;

  public ExpandableListAdapter(Context context, List<MenuModel> listDataHeader,
                               HashMap<MenuModel, List<MenuModel>> listChildData) {
      this.context = context;
      this.listDataHeader = listDataHeader;
      this.listDataChild = listChildData;
  }

  @Override
  public MenuModel getChild(int groupPosition, int childPosititon) {
      return this.listDataChild.get(this.listDataHeader.get(groupPosition))
              .get(childPosititon);
  }

  @Override
  public long getChildId(int groupPosition, int childPosition) {
      return childPosition;
  }

  @Override
  public View getChildView(int groupPosition, final int childPosition,
                           boolean isLastChild, View convertView, ViewGroup parent) {

      final String childText = getChild(groupPosition, childPosition).menuName;

      if (convertView == null) {
          LayoutInflater infalInflater = (LayoutInflater) this.context
                  .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          convertView = infalInflater.inflate(R.layout.list_group_child, null);
      }

      TextView txtListChild = convertView
              .findViewById(R.id.lblListItem);

      txtListChild.setText(childText);
      return convertView;
  }

  @Override
  public int getChildrenCount(int groupPosition) {

      if (this.listDataChild.get(this.listDataHeader.get(groupPosition)) == null)
          return 0;
      else
          return this.listDataChild.get(this.listDataHeader.get(groupPosition))
                  .size();
  }

  @Override
  public MenuModel getGroup(int groupPosition) {
      return this.listDataHeader.get(groupPosition);
  }

  @Override
  public int getGroupCount() {
      return this.listDataHeader.size();

  }

  @Override
  public long getGroupId(int groupPosition) {
      return groupPosition;
  }

  @Override
  public View getGroupView(int groupPosition, boolean isExpanded,
                           View convertView, ViewGroup parent) {
      String headerTitle = getGroup(groupPosition).menuName;
      if (convertView == null) {
          LayoutInflater infalInflater = (LayoutInflater) this.context
                  .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          convertView = infalInflater.inflate(R.layout.list_group_header, null);
      }

      TextView lblListHeader = convertView.findViewById(R.id.lblListHeader);
      lblListHeader.setTypeface(null, Typeface.BOLD);
      lblListHeader.setText(headerTitle);

      return convertView;
  }

  @Override
  public boolean hasStableIds() {
      return false;
  }

  @Override
  public boolean isChildSelectable(int groupPosition, int childPosition) {
      return true;
  }
}

不要忘记在您的AndroidManifest.xml文件中添加以下权限。

<uses-permission android:name="android.permission.INTERNET"