irpas技术客

Android 基于TabLayout + ViewPager2 + Fragment + 角标 实现底部导航栏(升级版)

网络 2290

昨天写了一篇关于导航栏的文章,由于时间太晚了没细弄,今天调整了一下主要有一下功能:

导航栏布局的自定义使用ViewPager2默认样式使用默认自定义样式(带角标的实现)使用自定义样式TabLayout 导航栏在底部或者顶部导航栏高度的调整一些功能的延伸

目录 效果图:核心代码布局TestTablayoutActivity 的布局默认自定义布局 Adapter数据bean调用测试fragment准备数据使用默认布局调用使用tabLayout原始布局加载tabLayout原始布局将导航栏加载到顶部自定义tabLayout布局 并将tab放置在上面 附录

效果图:

核心代码

其中的TableAndViewPage类是传入数据的bean,LogUtil类是记录日志的,替换成自己的即可。

主要方法说明:

TabLayout + ViewPager2 + Fragment 实现默认的加载方式是自定义的布局setData() 设置数据setLoadTypeToDefault() 设置加载方式为默认setTabCustomLayout() 设置自定义布局setTabHeight() 设置tab的高度isRoll() 导航条放置在顶部,默认在底部setJiaoBiaoNO() 设置角标setOffscreenPageLimit() 设置预加载fragment的数量,默认懒加载

SelfViewPageFragmentControl

import android.annotation.SuppressLint; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import androidx.viewpager2.widget.ViewPager2; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; import com.pksh.tools.R; import com.pksh.tools.style.moudle.TableAndViewPage; import com.pksh.tools.utils.log.LogUtil; //这时个记录日志的,替换成自己的就行 import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Target; import java.util.List; /** * 底部导航栏 * TabLayout + ViewPager2 + Fragment 实现 * 默认的加载方式是自定义的布局 * setData() 设置数据 * setLoadTypeToDefault() 设置加载方式为默认 * setTabCustomLayout() 设置自定义布局 * setTabHeight() 设置tab的高度 * isTop() 导航条放置在顶部,默认在底部 * isRoll() 导航条是否可以滚动 * setJiaoBiaoNO() 设置角标 * * 2022年10月27日13:19:51 */ public class SelfViewPageFragmentControl extends RelativeLayout{ private static final String TAG = "底部导航栏"; private final ViewPager2 viewPager2; private final TabLayout tabLayout; //默认加载方式 public static final int TAB_LOAD_TYPE_DEFAULT = 0; //默认自定义布局 public static final int TAB_LOAD_TYPE_DEFAULT_CUSTOM = 1; //自定义布局 public static final int TAB_LOAD_TYPE_CUSTOM = 2; //加载方式注解 @IntDef(value = {TAB_LOAD_TYPE_DEFAULT, TAB_LOAD_TYPE_DEFAULT_CUSTOM, TAB_LOAD_TYPE_CUSTOM}) //取值 @Inherited @Target({ElementType.PARAMETER,ElementType.FIELD}) //作用域 public @interface LoadType { } //导航栏加载方式 private @LoadType int loadType = TAB_LOAD_TYPE_DEFAULT_CUSTOM; //数据 private List<TableAndViewPage> data; //是否将导航列表放在顶部 private boolean isTop = false; //是否允许导航栏滚动 private boolean isRoll = false; //导航栏高度 private int tabHeight = 0; //上一个选中的tab下标 private int oldId; //自定义布局接口 private TabLayoutLoadCustomView tabLayoutLoadCustomView; public SelfViewPageFragmentControl(Context context, @Nullable AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.slef_view_page_fragment, this); viewPager2 = findViewById(R.id.view_pager); tabLayout = findViewById(R.id.tab_layout); } /** * 创建 */ public void build(){ //根据导航条位置设置布局格式 //顶部布局 if (isTop) { //设置viewPager2布局 RelativeLayout.LayoutParams layoutParams = (LayoutParams) viewPager2.getLayoutParams(); layoutParams.addRule(RelativeLayout.BELOW,R.id.tab_layout); viewPager2.setLayoutParams(layoutParams); //设置tablayout高度 if (tabHeight != 0) { RelativeLayout.LayoutParams layoutParams1 = new LayoutParams(LayoutParams.MATCH_PARENT,tabHeight); tabLayout.setLayoutParams(layoutParams1); } //底部布局 } else { //设置viewPager2布局 RelativeLayout.LayoutParams layoutParams = (LayoutParams) viewPager2.getLayoutParams(); layoutParams.addRule(RelativeLayout.ABOVE,R.id.tab_layout); viewPager2.setLayoutParams(layoutParams); //设置tablayout布局 RelativeLayout.LayoutParams layoutParams1; if (tabHeight != 0) layoutParams1 = new LayoutParams(LayoutParams.MATCH_PARENT,tabHeight); else layoutParams1 = (LayoutParams) tabLayout.getLayoutParams(); layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); tabLayout.setLayoutParams(layoutParams1); } //tabLayout是否可滚动 if (isRoll) tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); if (loadType != TAB_LOAD_TYPE_DEFAULT) { tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { //选择之后的 @Override public void onTabSelected(TabLayout.Tab tab) { int selectId = tabLayout.getSelectedTabPosition(); oldId = selectId; if (loadType == TAB_LOAD_TYPE_DEFAULT_CUSTOM && data.get(selectId).getIconSelected() != 0 && tab.getCustomView() != null) { ImageView imageView = tab.getCustomView().findViewById(R.id.imageview); imageView.setImageResource(data.get(selectId).getIconSelected()); return; } if (loadType == TAB_LOAD_TYPE_CUSTOM) { View view = tabLayoutLoadCustomView.onTabSelected(tab, selectId); if (view != null) tab.setCustomView(view); return; } //打印日志 LogUtil.e(TAG,"未配置选择后的图片或者当前tab选项卡未配置自定义布局,当前加载方式:"+loadType); } //选择之前的 @Override public void onTabUnselected(TabLayout.Tab tab) { //自定义布局 if (loadType == TAB_LOAD_TYPE_DEFAULT_CUSTOM && data.get(oldId).getIcon() != 0 && tab.getCustomView() != null) { ImageView imageView = tab.getCustomView().findViewById(R.id.imageview); imageView.setImageResource(data.get(oldId).getIcon()); return; } if (loadType == TAB_LOAD_TYPE_CUSTOM) { View view = tabLayoutLoadCustomView.onTabUnselected(tab, oldId); if (view != null) tab.setCustomView(view); return; } //打印日志 LogUtil.e(TAG,"未配置未选中的图片或者当前tab选项卡未配置自定义布局,当前加载方式:"+loadType); } @Override public void onTabReselected(TabLayout.Tab tab) {} }); } if (data != null) { //设置ViewPage2的Adapter AdapterViewPage2 adapterViewPage = new AdapterViewPage2((FragmentActivity) getContext(),data); viewPager2.setAdapter(adapterViewPage); //tabLayout和viewPager2关联 //smoothScroll 参数为true时点击tablayout标签会从当前页面滚动到点击的页面 //这个参数决定了点击tab按钮fragment是懒加载还是重新加载 new TabLayoutMediator(tabLayout, viewPager2,true,false, (tab, position) -> { //设置布局 switch (loadType){ case TAB_LOAD_TYPE_DEFAULT: if (data.get(position).getIcon() != 0) tab.setIcon(data.get(position).getIcon()); tab.setText(data.get(position).getText()); break; case TAB_LOAD_TYPE_DEFAULT_CUSTOM: tab.setCustomView(makeTabView(position)); break; case TAB_LOAD_TYPE_CUSTOM: View view = tabLayoutLoadCustomView.loadTabLayoutView(tab, position); if (view != null) tab.setCustomView(view); break; } }).attach(); } } /** * 引入布局设置图标和标题 * @param position 当前选择的选项卡 * @return view */ private View makeTabView(int position){ @SuppressLint("InflateParams") View tabView = LayoutInflater.from(getContext()).inflate(R.layout.tab_text_icon,null); TextView textView = tabView.findViewById(R.id.textview); ImageView imageView = tabView.findViewById(R.id.imageview); textView.setText(data.get(position).getText()); if (data.get(position).getIcon() != 0) imageView.setImageResource(data.get(position).getIcon()); return tabView; } //自定义布局接口 public interface TabLayoutLoadCustomView { //加载方式 View loadTabLayoutView(TabLayout.Tab tab, int position); //修改后的tab //可在该回调中通过tab.getCustomView().findViewById(控件id)获取控件直接修改,返回NULL即可 View onTabSelected(TabLayout.Tab tab, int position); //修改前的tab //可在该回调中通过tab.getCustomView().findViewById(控件id)获取控件直接修改,返回NULL即可 View onTabUnselected(TabLayout.Tab tab, int position);//修改前的tab } //设置加载方式为默认 public SelfViewPageFragmentControl setLoadTypeToDefault(){ loadType = TAB_LOAD_TYPE_DEFAULT; return this; } /** * 设置角标 * @param position * @param number */ public void setJiaoBiaoNO(@IntRange(from = 0) int position, int number){ if (position > data.size()-1) { LogUtil.e(TAG,"角标数字设置失败,TAB下标过大, 最大"+(data.size()-1)); return; } if (loadType == TAB_LOAD_TYPE_DEFAULT_CUSTOM) { TabLayout.Tab tabAt = tabLayout.getTabAt(position); if (tabAt != null && tabAt.getCustomView() != null) { RelativeLayout relativeLayout = tabAt.getCustomView().findViewById(R.id.jiao_biao); if (number < 1) { relativeLayout.setVisibility(GONE); return; } relativeLayout.setVisibility(VISIBLE); TextView textView = tabAt.getCustomView().findViewById(R.id.jiao_biao_text); textView.setText(String.valueOf(number)); return; } //打印日志 LogUtil.e(TAG,"角标数字设置失败,当前tab为空或默认布局为空"); } //打印日志 LogUtil.e(TAG,"角标数字设置失败,当前加载方式不是默认布局"); } //设置预加载fragment数量不包含当前页面 public SelfViewPageFragmentControl setOffscreenPageLimit(@ViewPager2.OffscreenPageLimit int limit){ viewPager2.setOffscreenPageLimit(limit); return this; } /** * 设置数据 */ public SelfViewPageFragmentControl setData(@NonNull List<TableAndViewPage> data){ this.data = data; return this; } /** * 导航条是否在顶部 * 根据传入的参数设置布局 */ public SelfViewPageFragmentControl isTop(boolean isTop){ this.isTop = isTop; return this; } /** * 导航条是否可以滚动 * 根据传入的参数设置布局 */ public SelfViewPageFragmentControl isRoll(boolean isRoll){ this.isRoll = isRoll; return this; } /** * 设置tabLayout的高度 */ public SelfViewPageFragmentControl setTabHeight(@IntRange(from = 20, to = 100) int height){ this.tabHeight = height; return this; } /** * 设置自定义view */ public SelfViewPageFragmentControl setTabCustomLayout(TabLayoutLoadCustomView tabLayoutLoadCustomView){ this.tabLayoutLoadCustomView = tabLayoutLoadCustomView; if (tabLayoutLoadCustomView != null) { this.loadType = TAB_LOAD_TYPE_CUSTOM; } return this; } } 布局 TestTablayoutActivity 的布局

布局很简单就一个ViewPager2 和一个TabLayout这两个怎么放都可以,在上面代码里调整其布局 slef_view_page_fragment.xml

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.google.android.material.tabs.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="50dp" app:tabTextColor="#B8B8B8" app:tabPaddingStart="1dp" app:tabPaddingEnd="1dp" app:tabPaddingBottom="1dp" app:tabPaddingTop="1dp" app:tabIndicatorHeight="0dp" app:tabSelectedTextColor="#000000" app:tabMinWidth="10dp" app:tabMaxWidth="200dp" app:tabPadding="0dp" /> </RelativeLayout> 默认自定义布局

角标的图片见文章最后附录 tab_text_icon.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/text_view_def" android:gravity="center"> <RelativeLayout android:id="@+id/icon" android:layout_width="30dp" android:layout_height="30dp"> <ImageView android:id="@+id/imageview" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitCenter" android:padding="2dp" /> <RelativeLayout android:id="@+id/jiao_biao" android:layout_width="15dp" android:layout_height="15dp" android:layout_alignParentEnd="true" android:visibility="gone" > <ImageView android:id="@+id/jiao_biao_img" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/def_img_jiao_biao" /> <TextView android:id="@+id/jiao_biao_text" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textColor="@color/text_white_color" android:ellipsize="end" android:lines="1" android:maxLength="2" android:textSize="@dimen/def_text_small_small_size" /> </RelativeLayout> </RelativeLayout> <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:gravity="center" android:layout_gravity="center" android:textSize="@dimen/def_text_small_small_size" android:ellipsize="end" android:lines="1" android:text="1258" /> </LinearLayout> Adapter

viewpage2的Adapter

AdapterViewPage2.class

import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.viewpager2.adapter.FragmentStateAdapter; import java.util.List; import com.pksh.tools.style.moudle.TableAndViewPage; /** * TabLayout + ViewPager2 + Fragment 实现底部栏ViewPager2的Adapter * * 2022年10月27日13:23:37 */ public class AdapterViewPage2 extends FragmentStateAdapter{ private final List<TableAndViewPage> data; public AdapterViewPage2(@NonNull FragmentActivity fragmentActivity, List<TableAndViewPage> data) { super(fragmentActivity); this.data = data; } @NonNull @Override public Fragment createFragment(int position) { return data.get(position).getFragment(); } @Override public int getItemCount() { return data.size(); } } 数据bean

TableAndViewPage.class

import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; /** * TabLayout + ViewPager2 + Fragment 实现自定义底部栏控件的module * * 2022年10月27日13:22:23 */ public class TableAndViewPage { private int icon; //未选中时的图片 private int iconSelected; //选中时的图片 private @NonNull String text; //显示的文本 private Fragment fragment; //关联的fragment public TableAndViewPage(@NonNull String text,int icon,int iconSelected,Fragment fragment){ this.fragment = fragment; this.icon = icon; this.iconSelected = iconSelected; this.text = text; } public int getIcon() { return icon; } public void setIcon(int icon) { this.icon = icon; } public int getIconSelected() { return iconSelected; } public void setIconSelected(int iconSelected) { this.iconSelected = iconSelected; } public String getText() { return text; } public void setText(@NonNull String text) { this.text = text; } public Fragment getFragment() { return fragment; } public void setFragment(Fragment fragment) { this.fragment = fragment; } @Override public String toString() { return "TableAndViewPage{" + "icon=" + icon + ", iconSelected=" + iconSelected + ", text='" + text + '\'' + ", fragment=" + fragment + '}'; } } 调用 测试fragment

fragment_tab.xml文件里就一个textview这里就不贴了

public class ViewPagerFragment extends Fragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_tab, container, false); } public static ViewPagerFragment newInstance(String label) { Bundle args = new Bundle(); args.putString("label", label); ViewPagerFragment fragment = new ViewPagerFragment(); fragment.setArguments(args); return fragment; } @Override public void onStart() { super.onStart(); LogUtil.d("页面","创建fragment"); String label = getArguments().getString("label"); TextView text = getView().findViewById(R.id.tv_bg); text.setText(label); text.setBackgroundColor(Color.rgb((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255))); } } 准备数据 ViewPagerFragment.newInstance("123")参数是fragment替换为自己的fragment就行了 SelfViewPageFragmentControl selfVierPageFragmentControl = findViewById(R.id.self_view_page_1); List<TableAndViewPage> data = new ArrayList<>(); //如果不想带图片,TableAndViewPage里面的icon及iconSelected属性设置为0或者不传值即可 data.add(new TableAndViewPage("测试1",com.pksh.tools.R.mipmap.sel_lie_biao,0,ViewPagerFragment.newInstance("123"))); data.add(new TableAndViewPage("测试2",com.pksh.tools.R.mipmap.def_map_select,0,ViewPagerFragment.newInstance("123"))); data.add(new TableAndViewPage("测试3",com.pksh.tools.R.mipmap.def_map_navigation_neibu,0,ViewPagerFragment.newInstance("123"))); data.add(new TableAndViewPage("测试4",com.pksh.tools.R.mipmap.def_map_navigation_tengxun,0,ViewPagerFragment.newInstance("123"))); 使用默认布局调用

默认布局可以设置角标

// 默认自定义布局加载 selfVierPageFragmentControl.setData(data).build(); //设置角标,仅支持默认自定义布局 selfVierPageFragmentControl.setJiaoBiaoNO(0,45); selfVierPageFragmentControl.setJiaoBiaoNO(3,45); selfVierPageFragmentControl.setJiaoBiaoNO(1,0); 使用tabLayout原始布局加载 //不带图片的原始布局,TableAndViewPage里面的icon设置为0即可 selfVierPageFragmentControl.setData(data).setLoadTypeToDefault().build(); tabLayout原始布局将导航栏加载到顶部 selfVierPageFragmentControl.setData(data).isTop(true).setLoadTypeToDefault().build(); 自定义tabLayout布局 并将tab放置在上面

onTabSelected() 可在该回调中通过tab.getCustomView().findViewById(控件id)获取控件直接修改,返回NULL即可 onTabUnselected()可在该回调中通过tab.getCustomView().findViewById(控件id)获取控件直接修改,返回NULL即可

selfVierPageFragmentControl.setData(data).isTop(true).setTabCustomLayout(new SelfViewPageFragmentControl.TabLayoutLoadCustomView() { //默认加载方式 @Override public View loadTabLayoutView(TabLayout.Tab tab, int position) { View tabView = LayoutInflater.from(getBaseContext()).inflate(R.layout.tab_test_text,null); TextView textView = tabView.findViewById(R.id.test_textview); textView.setText(data.get(position).getText()); tab.setCustomView(tabView); return null; } //更改选择后的tab @Override public View onTabSelected(TabLayout.Tab tab, int position) { return null; } //更改选择前的tab @Override public View onTabUnselected(TabLayout.Tab tab, int position) { return null; } }).build();

以上就是全部的调用方式了

附录

角标图片链接:https://lxh-customer.oss-cn-qingdao.aliyuncs.com/def_img_jiao_biao.png


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #tablayout #ViewPager2 #fragment #角标