关于Fragment + RecyclerView + Toolbar + BottomNavigationView的组合应用
关于Fragment + RecyclerView + Toolbar + BottomNavigationView的组合应用
主要介绍RecyclerView的网格布局以及解决Toolbar和RecyclerView的覆盖问题,方法如下:
1.在AndroidManifest.xml文件中设置主题
第一步,在styles.xml中添加:
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
第二步,在AndroidManifest.xml设置主题为:
android:theme="@style/AppTheme.NoActionBar"
2.在layout目录中添加RecyclerView的单元文件布局文件recyclerview_item.xml,文件内容如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:gravity="center" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/item_icon" android:src="@mipmap/ic_launcher" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/item_title" android:text="名称" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
3.在drawble目录下添加导航菜单的样式文件bottom_navigation_item_selector.xml,文件内容如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="@color/theme" android:state_checked="true" /> <item android:color="@color/bottom_navigation_normal" android:state_checked="false" /> </selector>
4.在布局文件activity_main.xml中,设置Fragment和导航菜单BottomNavigationView,并且去除Toobar,内容如下:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <FrameLayout android:id="@+id/ll_frameLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/nav_view" /> <android.support.design.widget.BottomNavigationView android:id="@+id/nav_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="0dp" android:layout_marginEnd="0dp" android:background="?android:attr/windowBackground" app:itemIconTint="@drawable/bottom_navigation_item_selector" app:itemTextColor="@drawable/bottom_navigation_item_selector" app:labelVisibilityMode="labeled" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:menu="@menu/main_bottom_navigation" /> </android.support.constraint.ConstraintLayout>
5.在layout文件夹中新建Fragment的布局文件,如fragment_contact.xml,注意添加AppBarLayout和SwipeRefreshLayout,并且最外层为CoordinatorLayout布局,内容如下:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_height="?attr/actionBarSize" android:layout_width="match_parent"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_height="?attr/actionBarSize" android:layout_width="match_parent" app:titleTextColor="@color/write"> </android.support.v7.widget.Toolbar> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/srl_flash" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <android.support.v7.widget.RecyclerView android:id="@+id/recycle_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </android.support.v4.widget.SwipeRefreshLayout> </android.support.design.widget.CoordinatorLayout>
6.创建文件GridSpacingItemDecoration.java,内容如下:
package com.masswiz.logistics; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { private String TAG = getClass().getSimpleName(); private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; private Drawable divider; public GridSpacingItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); divider = a.getDrawable(0); a.recycle(); } public GridSpacingItemDecoration(Drawable drawable) { divider = drawable; } public GridSpacingItemDecoration(int height, int color) { GradientDrawable shapeDrawable = new GradientDrawable(); shapeDrawable.setColor(color); shapeDrawable.setShape(GradientDrawable.RECTANGLE); shapeDrawable.setSize(height, height); divider = shapeDrawable; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount(); } return spanCount; } public void drawHorizontal(Canvas c, RecyclerView parent) { int childCount = parent.getChildCount(); //获取可见item的数量 int spanCount = getSpanCount(parent); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + divider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + divider.getIntrinsicHeight(); divider.setBounds(left, top, right, bottom); divider.draw(c); if (i < spanCount) { //画第一行顶部的分割线 drawHorizontalForFirstRow(c, child); } } } private void drawHorizontalForFirstRow(Canvas c, View child) { final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int left = child.getLeft() - params.leftMargin - divider.getIntrinsicWidth(); int top = child.getTop() - params.topMargin - divider.getIntrinsicHeight(); int right = child.getRight() + params.rightMargin + divider.getIntrinsicWidth(); int bottom = top + divider.getIntrinsicHeight(); divider.setBounds(left, top, right, bottom); divider.draw(c); } private void drawVerticalForFirstColum(Canvas c, View child) { final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int left = child.getLeft() - params.leftMargin - divider.getIntrinsicWidth(); int top = child.getTop() - params.topMargin; int right = child.getLeft() - params.leftMargin; int bottom = top + child.getHeight() + divider.getIntrinsicHeight(); divider.setBounds(left, top, right, bottom); divider.draw(c); } public void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int top = child.getTop() - params.topMargin; final int bottom = child.getBottom() + params.bottomMargin; final int left = child.getRight() + params.rightMargin; final int right = left + divider.getIntrinsicWidth(); divider.setBounds(left, top, right, bottom); divider.draw(c); if (isFirstColum(parent, i, getSpanCount(parent))) { //画第一列左边分割线 drawVerticalForFirstColum(c, child); } } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一列,则不需要绘制右边 return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一行,则不需要绘制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; // 如果是最后一行,则不需要绘制底部 if (pos >= childCount) return true; } else { // 如果是最后一行,则不需要绘制底部 if ((pos + 1) % spanCount == 0) { return true; } } } return false; } //是否为第一列 private boolean isFirstColum(RecyclerView parent, int pos, int spanCount) { RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { //网格布局 if ((pos + 1) % spanCount == 1) { return true; } } return false; } //是否为第一行 private boolean isFirstRaw(int pos, int spanCount) { if (pos < spanCount) { return true; } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); //列数 if (itemPosition == 0) { //第一行第一个,四边都画 outRect.set(divider.getIntrinsicWidth(), divider.getIntrinsicHeight(), divider.getIntrinsicWidth(), divider.getIntrinsicHeight()); } else if (isFirstRaw(itemPosition, spanCount)) { //第一行,画上下右三边 outRect.set(0, divider.getIntrinsicHeight(), divider.getIntrinsicWidth(), divider.getIntrinsicHeight()); } else if (isFirstColum(parent, itemPosition, spanCount)) { //第一列,画左右下三边 outRect.set(divider.getIntrinsicWidth(), 0, divider.getIntrinsicWidth(), divider.getIntrinsicHeight()); } else { //其他,画右下两边 outRect.set(0, 0, divider.getIntrinsicWidth(), divider.getIntrinsicHeight()); } } }
7.创建RecycleViewAdapter.java文件,且ViewHolder为RecycleViewAdapter的public static内部类,注意RecyclerView.Adapter的模板为内部类RecycleViewAdapter.ViewHolder,文件内容如下:
package com.masswiz.logistics; import android.content.Context; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.util.List; public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder>{ private List<String> strings; private Context m_context; public RecycleViewAdapter(Context context, List<String> strings) { this.strings = strings; m_context = context; } @Override public int getItemCount() { int ret = 0; if (strings != null) { ret = strings.size(); } return ret; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { // 不需要检查是否复用,因为只要进入此方法,必然没有复用 // 因为RecyclerView 通过Holder检查复用 // R.layout.recycleview_item为layout文件夹下的recycleview_item.xml布局文件,如果没有要新建 View v = LayoutInflater.from(m_context).inflate(R.layout.recycleview_item, null, false); return new ViewHolder(v); } @Override public void onBindViewHolder(@NonNull RecycleViewAdapter.ViewHolder viewHolder, int i) { viewHolder.m_text.setText(strings.get(i)); int resId = R.mipmap.ic_launcher; int index = i % 5; switch (index){ case 0: resId = R.mipmap.ic_launcher; break; case 1: resId = R.mipmap.ic_launcher_round; break; case 2: resId = R.mipmap.ic_launcher; break; } viewHolder.m_image.setImageResource(resId); } public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public ImageView m_image; public TextView m_text; public ViewHolder(View itemView) { super(itemView); // 通常ViewHolder的构造,就是用于获取控件视图的 m_image = (ImageView) itemView.findViewById(R.id.item_icon); m_text = (TextView) itemView.findViewById(R.id.item_title); // TODO 后续处理点击事件的操作 itemView.setOnClickListener(this); } @Override public void onClick(View v) { int position = getAdapterPosition(); Context context = m_image.getContext(); Toast.makeText(context,"显示第"+position+"个项", Toast.LENGTH_SHORT).show(); } } }
8.新建使用RecycleView、Toolbar的使用类文件ContactsFragment.java,内容如下:
package com.masswiz.logistics;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* @author Evan_zch
* @date 2018/8/23 20:41
*/
public class ContactsFragment extends Fragment {
private List<String> m_data;
private RecyclerView m_recyclerView;
//自定义适配器,继承RecyclerView.Adapter
private RecycleViewAdapter m_recycleViewAdapter;
GridLayoutManager m_layoutManager;
AppCompatActivity mAppCompatActivity;
Toolbar m_toolbar;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contacts, container, false);
Context context = getContext();
m_recyclerView = (RecyclerView)view.findViewById(R.id.recycle_view);
m_layoutManager = new GridLayoutManager(context, 3);
m_recyclerView.setLayoutManager(m_layoutManager);
m_recyclerView.addItemDecoration(new GridSpacingItemDecoration(context));
m_recyclerView.setHasFixedSize(true);
m_recycleViewAdapter = new RecycleViewAdapter(context, m_data);
m_recyclerView.setAdapter(m_recycleViewAdapter);
m_toolbar = (Toolbar)view.findViewById(R.id.toolbar);
mAppCompatActivity = (AppCompatActivity) getActivity();
mAppCompatActivity.setSupportActionBar(m_toolbar);
m_toolbar.setTitle("返回");
setHasOptionsMenu(true);
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu,inflater);
//此处R.menu.main_bottom_navigation可以替换成R.menu中人一个单独的菜单
inflater.inflate(R.menu.main_bottom_navigation, menu);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
m_data = new ArrayList<>();
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
m_data.add("订单");
}
}
7.以此类推,可以新建N个Fragment在Activity中,此文在MainActivity.java中创建了四个Fragment,内容如下:
package com.masswiz.logistics;
import android.os.Bundle;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.annotation.NonNull;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.widget.TextView;
import com.masswiz.logistics.utils.BottomNavigationViewHelper;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private Toolbar mToolbar;
private int lastIndex;
private BottomNavigationView mBottomNavigationView;
List<Fragment> mFragments;
public void initView() {
mToolbar = findViewById(R.id.toolbar);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navView = findViewById(R.id.nav_view);
mTextMessage = findViewById(R.id.message);
initView();
initBottomNavigation();
initData();
}
public void initData() {
setSupportActionBar(mToolbar);
mFragments = new ArrayList<>();
mFragments.add(new MessageFragment());
mFragments.add(new ContactsFragment());
mFragments.add(new DiscoverFragment());
mFragments.add(new AccountFragment());
// 初始化展示MessageFragment
setFragmentPosition(0);
}
public void initBottomNavigation() {
mBottomNavigationView = findViewById(R.id.nav_view);
// 解决当item大于三个时,非平均布局问题
BottomNavigationViewHelper.disableShiftMode(mBottomNavigationView);
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_message:
setFragmentPosition(0);
break;
case R.id.menu_contacts:
setFragmentPosition(1);
break;
case R.id.menu_discover:
setFragmentPosition(2);
break;
case R.id.menu_me:
setFragmentPosition(3);
break;
default:
break;
}
// 这里注意返回true,否则点击失效
return true;
}
});
}
private void setFragmentPosition(int position) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment currentFragment = mFragments.get(position);
Fragment lastFragment = mFragments.get(lastIndex);
lastIndex = position;
ft.hide(lastFragment);
if (!currentFragment.isAdded()) {
getSupportFragmentManager().beginTransaction().remove(currentFragment).commit();
ft.add(R.id.ll_frameLayout, currentFragment);
}
ft.show(currentFragment);
ft.commitAllowingStateLoss();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
9,效果如下图所示:
10.最后,此文章为验证技术可行性而进行测试工程,不甚严谨,仅供参考,此文参考文献大多来自于****,简书等文章,再次感谢各位博主提供的技术资料,主要参考文献如下:
[1] 北国雪WRG.Toolbar遮住Recyclerview[EB/OL].https://www.jianshu.com/p/5b103288baa8, 2018.12.26 15:46.
[2]十 月.Recylerview网格布局分割线,实现列表四周也有分[EB/OL].https://blog.****.net/liuxingrong666/article/details/83178213,2018年10月19日 11:16:44
[3]凌枫清扬.Android RecyclerView 添加间距全适配[EB/OL].https://blog.****.net/u013252110/article/details/80182379,2018年05月03日 16:42:23