NavigationView内的Android ExpandableListView
在本教程中,我们将实现一个应用程序,其中在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"

