目前主流实现方式
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">
<FrameLayout
android:id="@+id/home_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
<view
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:alpha="0.6"
android:background="@android:color/darker_gray"
/>
<android.support.design.widget.TabLayout
android: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.TabLayout
android: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.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab1"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab2"/>
<android.support.design.widget.TabItem
android: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() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
// 选中了标签的逻辑
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// 未选中tab的逻辑
}
@Override
public 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.ViewPager
android: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;
}
@Override
public Fragment getItem(int position) {
return mfragments.get(position);
}
@Override
public int getCount() {
return mfragments.size();
}
}
只需要实现getItem()和getCount()方法
然后,在活动中初始化调用
// 设置FragmentList
List<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() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
vpContext.setCurrentItem(tab.getPosition());
// 选中了标签的逻辑
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
// 未选中tab的逻辑
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
// 再次选中tab的逻辑
}
});
vpContext.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
hostTabLayout.getTabAt(position).select();
}
@Override
public 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
@Override
public 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
@Override
public 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