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"