目前主流实现方式
a 运用Material Design提供的插件TabLayout
b 运用Material Design提供的BottomNavigationView
c FragmentTabHost
一 TabLayout
资料来源:
https://blog.csdn.net/qq_23205911/article/details/73430979
准备工作
由于Material Design属于扩展库,需要实现添加依赖
implementation 'com.android.support:design:27.1.1'
后面的版本号必须跟V7包的版本一致
implementation 'com.android.support:appcompat-v7:27.1.1'
1.1通过代码动态实现tablayout
布局文件
<LinearLayout 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:orientation="vertical"><FrameLayoutandroid:id="@+id/home_container"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"></FrameLayout><viewandroid:layout_width="match_parent"android:layout_height="0.5dp"android:alpha="0.6"android:background="@android:color/darker_gray"/><android.support.design.widget.TabLayoutandroid:id="@+id/host_tab_layout"android:layout_width="match_parent"android:layout_height="50dp"app:tabSelectedTextColor="@android:color/black"//标题选中的颜色app:tabTextColor="@android:color/darker_gray"//未选中的颜色app:tabIndicatorHeight="0dp"></android.support.design.widget.TabLayout>
</LinearLayout>
indicator指示器
指的就是当前标签页底部的红色的线,设置属性有color和height
UI效果通过代码实现(安装并添加里ButterKnife插件)
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_tablayout);initView();}/*** 用ButterKnife快速绑定View控件*/private void initView() {unbinder=ButterKnife.bind(this);hostTabLayout.addTab(hostTabLayout.newTab().setText("TDab1"));hostTabLayout.addTab(hostTabLayout.newTab().setText("TDab2"));hostTabLayout.addTab(hostTabLayout.newTab().setText("TDab3"));}
1.2在布局文件中实现
<android.support.design.widget.TabLayoutandroid:id="@+id/host_tab_layout"android:layout_width="match_parent"android:layout_height="50dp"app:tabIndicatorHeight="0dp"app:tabSelectedTextColor="@android:color/black"app:tabTextColor="@android:color/darker_gray"><android.support.design.widget.TabItemandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Tab1"/><android.support.design.widget.TabItemandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Tab2"/><android.support.design.widget.TabItemandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Tab3"/></android.support.design.widget.TabLayout>
实现效果图
1.3添加新的功能
1.4TabLayout的监听事件
hostTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {@Overridepublic void onTabSelected(TabLayout.Tab tab) {// 选中了标签的逻辑}@Overridepublic void onTabUnselected(TabLayout.Tab tab) {// 未选中tab的逻辑}@Overridepublic void onTabReselected(TabLayout.Tab tab) {// 再次选中tab的逻辑}});
三个回调接口的执行顺序
当切换新的tab时A--B
onTabUnselected(A)
onTabReselected(B)
onTabSelected(B)
当重复选择当前tab时C---C
onTabReselected(C)
2ViewPaper
思路
ViewPaper实现了若干个界面的滑动切换效果,通过跟tabLayout关联,实现两者的联动效果
2.1布局文件
<android.support.v4.view.ViewPagerandroid:id="@+id/vp_context"android:layout_width="match_parent"android:layout_height="match_parent"></android.support.v4.view.ViewPager>
2.2代码实现
ViewPaper要实现若干个界面的切换,需要进行适配器填装paperadapter,目前主流的上使用FragmentPaperAdapter
创建自己的Adapter
public class FragAdapter extends FragmentPagerAdapter{private List<Fragment> mfragments=null;public FragAdapter(FragmentManager fm,List<Fragment> fragments) {super(fm);mfragments=fragments;}@Overridepublic Fragment getItem(int position) {return mfragments.get(position);}@Overridepublic int getCount() {return mfragments.size();}
}
只需要实现getItem()和getCount()方法
然后,在活动中初始化调用
// 设置FragmentListList<Fragment> fragmentList=new ArrayList<Fragment>();fragmentList.add(BlankFragment.newInstance());fragmentList.add(BlankFragment2.newInstance());fragmentList.add(BlankFragment3.newInstance());// 设置适配器并装载adapter=new FragAdapter(getSupportFragmentManager(),fragmentList);vpContext.setAdapter(adapter);
4和ViewPager的联动
有资料建议用setupWithViewPaper()关联两者,实验后,发现tab标签变成里空白,原因上在关联代码内部封装函数中,移除里所有tab
tabLayout.setupWithViewPager(Viewpager);
所以采用在监听器中回调互相指定变化
hostTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {@Overridepublic void onTabSelected(TabLayout.Tab tab) {vpContext.setCurrentItem(tab.getPosition());
// 选中了标签的逻辑}@Overridepublic void onTabUnselected(TabLayout.Tab tab) {
// 未选中tab的逻辑}@Overridepublic void onTabReselected(TabLayout.Tab tab) {
// 再次选中tab的逻辑}});vpContext.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}@Overridepublic void onPageSelected(int position) {hostTabLayout.getTabAt(position).select();}@Overridepublic void onPageScrollStateChanged(int state) {}});
填坑:
1,我自己理解的上,在ViewPaper变化时,应该在onPageScrolled()方法中设置标签,结果造成通过标签无法切换界面
个人认为因为nPageScrolled()的不断调用,阻止了界面的变化.
实际上应该在onPageSelected()中设置tab的标签.
参考资料:https://www.cnblogs.com/Dionexin/p/5727297.html
ViewPaper监听器的方法执行顺序:
用手指拖动翻页时,最先执行一遍onPageScrollStateChanged(1),然后不断执行onPageScrolled,放手指的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)。
通过setCurrentItem()设置界面的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)。
2.限定vieewPager预加载数量
通过vp.setOffscreenPageLimit()方法,可以指定加载当前frag时,提前加载frag的数量.
默认的设置是自动预加载下一个页面,最多持有当前frag,上一个和下一个3个frag,多余的frag会被销毁
如果我们的页面固定显示4个,直接用该方法,设置viewPager加载4个页面即可.
3 实现标签选中跟未选中的变化
3.1tablayout自带的标签
首先,对图标和标签标题进行数组管理
final int[] tabResPressed=new int[]{R.drawable.ic_left,R.drawable.ic_stop,R.drawable.ic_right};final int[] tabRes=new int[]{R.drawable.ic_insmall,R.drawable.ic_location,R.drawable.ic_inbigger};final String[] tabTitle=new String[]{"tab1","tab2","tab3"};
第二步,通过数组初始化标签图标,(文本和图标两部分)
文本 tab.setText(string)
图标 tab.setIcon(resources)
// 给TablLayout添加标签for(int i=0;i<tabTitle.length;i++){hostTabLayout.addTab(hostTabLayout.newTab().setText(tabTitle[i]).setIcon(getResources().getDrawable(tabRes[i])));}
第三步,在tablayout的监听器中,对选中图标进行设置
A---B,设置B
@Overridepublic void onTabSelected(TabLayout.Tab tab) {
// ViewPaper联动vpContext.setCurrentItem(tab.getPosition());
// 图标变为选中状态for (int i=0;i<tabTitle.length;i++){if (tab.getText().equals(tabTitle[i])){tab.setIcon(tabResPressed[i]);}}}
第四步,对tablayout监听器中,对原来被选中的图标更新图标
A--B,设置A
@Overridepublic void onTabUnselected(TabLayout.Tab tab) {
// 图标变回未选中状态for (int i=0;i<tabTitle.length;i++){if (tab.getText().equals(tabTitle[i])){tab.setIcon(tabRes[i]);}}
// 未选中tab的逻辑}
第五步,设置默认
/ 设置默认选中第一个tab并改变图标hostTabLayout.getTabAt(0).select();setIconViewSelected(0);
具体的实现方法
private void setIconView(TabLayout.Tab tab,int position) {View mView=tab.getCustomView();ImageView mImageView=(ImageView) mView.findViewById(R.id.iv_icon);TextView mtextView=(TextView)mView.findViewById(R.id.tv_title);mImageView.setImageResource(tabRes[position]);mtextView.setTextColor(getResources().getColor(android.R.color.darker_gray));}private void setIconViewSelected(int position) {View mView=hostTabLayout.getTabAt(position).getCustomView();ImageView mImageView=(ImageView) mView.findViewById(R.id.iv_icon);TextView mtextView=(TextView)mView.findViewById(R.id.tv_title);mImageView.setImageResource(tabResPressed[position]);mtextView.setTextColor(getResources().getColor(android.R.color.black));}
github地址:
https://github.com/wwqby/MyApplication.git