Google官方在14年Google I/O上推出了全新的设计语言——Material Design。一并推出了一系列实现Material Design效果的控件库——Android Design Support Library。其中,有TabLayout, NavigationView,Floating labels for editing text,Floating Action Button,Snackbar, CoordinatorLayout, CollapsingToolbarLayout等等控件。在今后的学习中,我将一一介绍它们的特点和用法。
我们可以使用三方开源的PagerSlidingTabStrip去实现,或者viewpagerindicator,我一般都偏向前者。现在我们可以使用Design support library库的TabLayout去实现了。
Demo地址:https://github.com/WeiShuaiDev/TabLayout
Demo首页图:
最终的效果图:
1.在应用的Build.gradle中添加support.design支持库(注意:必须和v7包的版本相同)。
2.TabLayout第一种使用–Tab固定模式。
1)创建布局文件activity_short_tab,在布局文件中添加TabLayout及ViewPager。
2)定义一个FragmentFactivity的Fragment工厂类,专门生产Fragment对象(用了工厂类,方便Fragment使用,提高了性能,在项目中很常见,记得在android5.0的Demo中也是怎么写的,可能有些读者觉得没必要,也可以每次去创建Fragment,并添加到ViewPager中也可)。
package com.linwei.lw.tablayoutdemo.ui.fragment;
import java.util.HashMap;
/**
* Fragment工厂类。
* Created by LW on 2017/4/20.
*/
public class FragmentFactory {
private static HashMap mBaseFragments = new HashMap();
public static BaseFragment createFragment(int pos) {
BaseFragment baseFragment = mBaseFragments.get(pos);
if (baseFragment == null) {
switch (pos) {
case 0:
baseFragment = new TopLineFragment();//头条
break;
case 1:
baseFragment = new NewsFragment();//要闻
break;
case 2:
baseFragment = new EntertainmentFragment();//娱乐
break;
case 3:
baseFragment = new SportsFragment();//体育
break;
case 4:
baseFragment = new FinanceFragment();//财经
break;
case 5:
baseFragment = new ScienceFragment();//科技
break;
case 6:
baseFragment = new ModeFragment();//时尚
break;
case 7:
baseFragment = new VideoFragment();//视频
break;
case 8:
baseFragment = new DirectSeedingFragment();//直播
break;
}
mBaseFragments.put(pos, baseFragment);
}
return baseFragment;
}
}
3)定义一个Fragment的父类BaseFragment,我就不解释了看代码。
package com.linwei.lw.tablayoutdemo.ui.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by LW on 2017/4/20.
*/
public abstract class BaseFragment extends Fragment {
protected Context mContent;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContent = getContext();//上下文。
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return initView();//初始化布局。
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loadData();//初始化数据。
}
protected abstract void loadData();
protected abstract View initView();
}
4)BaseFragment的实现子类TopLineFragment,这里我就只列出一个子类代码,其他子类代码也是一样的。
package com.linwei.lw.tablayoutdemo.ui.fragment;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
/**
* Created by LW on 2017/4/21.
*/
public class TopLineFragment extends BaseFragment {
@Override
protected void loadData() {
Toast.makeText(mContent,"Fragment头条加载数据",Toast.LENGTH_SHORT).show();
}
@Override
protected View initView() {
TextView mView = new TextView(mContent);
mView.setText("Fragment头条");
mView.setGravity(Gravity.CENTER);
mView.setTextSize(18);
mView.setTextColor(Color.BLACK);
return mView;
}
}
5)arrays.xml中第一显示数据。我了方便使用,通过字符串数组定义起来。
- 头条
- 要闻
- 娱乐
- 体育
- 头条
- 要闻
- 娱乐
- 体育
- 财经
- 科技
- 时尚
- 视频
- 直播
6)ShortTabActivity类,实现TabLayout和ViewPager的业务逻辑。
package com.linwei.lw.tablayoutdemo.ui.activity;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import com.linwei.lw.tablayoutdemo.R;
import com.linwei.lw.tablayoutdemo.Utils.CommentUtils;
import com.linwei.lw.tablayoutdemo.ui.fragment.BaseFragment;
import com.linwei.lw.tablayoutdemo.ui.fragment.FragmentFactory;
public class ShortTabActivity extends AppCompatActivity {
private TabLayout mTab;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_short_tab);
initView();
initData();
}
private void initData() {
ShortPagerAdapter adapter = new ShortPagerAdapter(getSupportFragmentManager());
mViewPager.setAdapter(adapter);
mTab.setupWithViewPager(mViewPager);
}
private void initView() {
mTab = (TabLayout) findViewById(R.id.tab);
mViewPager = (ViewPager) findViewById(R.id.viewpager);
}
private class ShortPagerAdapter extends FragmentPagerAdapter {
public String[] mTilte;
public ShortPagerAdapter(FragmentManager fm) {
super(fm);
mTilte = getResources().getStringArray(R.array.tab_short_Title);
}
@Override
public CharSequence getPageTitle(int position) {
return mTilte[position];
}
@Override
public BaseFragment getItem(int position) {
BaseFragment fragment = FragmentFactory.createFragment(position);
return fragment;
}
@Override
public int getCount() {
return CommentUtils.TAB_SHORT_COUNT;
}
}
}
3.TabLayout第二种使用–Tab滚动模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FVb4VM4w-1590030518439)(https://img-blog.csdn.net/20170422164745063?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGVyc2lzdGVuY2VfbHc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
1)Tab滚动模式和Tab固定模式差不多,只是Apdater中的getCount的个数和app:tabMode发生改变,这里我就不从新写了,把发生改变写出来,如果想看Demo,下面有地址。
1.1)TabMode属性从fill改为scrollable
1.2)适配器中getCount个数。
4.TabLayout第三种使用–Tab图片显示。
1)实现Tab图片显示,只需要改变ImagePagerAdapter适配器中getpageTitle方法,下面是关键代码。
1.1)把图片的id放在数组中,方便对图片使用 。
1.2)getPageTitle()方法,用于Tab显示方式定义,是显示图片关键的代码。
1.4)此时,设置完之后运行,发现Tab上并没有显示图标,而是什么也没有了。这是因为TabLayout的textAllCaps属性默认值是true,会阻止ImageSpan的渲染,我们只需要将其重写为false即可。
1.4.1)对TabLayout抽取出样式,并对样式调用。
1.4.2)对textAllCaps设置和TabLayout样式定义。
5.TabLayout第四种使用–Tab图片和文字显示。
1)获取图片和文字资源。
2)对Tab图片和文字设置。
6.TabLayout第五种使用–自定义View。
1)对适配器CustomAdapter中getPageTitle返回null
2)在适配器中定义一个方法getTabView(int position),只要对自定义布局调用和数据设置。
3.对TabLayout中获取每个标签,并调用指定getTabView,通过setCustom设置给当前Tab。
上面我们使用了自定义View作为Tab,但是有个问题上没有处理,就是Tab在选中时候的高亮状态。如果使用文本Tab,我们可以设置Style中的tabSelectedTextColor来实现,那么想一想TabLayout是在什么时机使用这个资源值的?我们是否也可以在此时机改变自定义View的显示来标记选中。
上面我们使用了自定义View作为Tab,但是有个问题上没有处理,就是Tab在选中时候的高亮状态。如果使用文本Tab,我们可以设置Style中的tabSelectedTextColor来实现,那么想一想TabLayout是在什么时机使用这个资源值的?我们是否也可以在此时机改变自定义View的显示来标记选中。
可以看到,给ViewPager添加,注意是添加而不是设置,说明现在ViewPager可以添加任意个页面改变监听器了。与PagerSlidingTabTrip的设置方式相比较,这种方式更好了,看来代码与时俱进还是很重要的,使用TabLayout还是值得的。
好了,放弃题外话,继续关注TabLayoutOnPageChangeListener的实现,关注onPageSelected()方法:
当ViewPager的页面改变时会调用selectTab()方法:
当新的选中Tab与当前不同时,调用了setSelectedTabView()方法:
最后,重新设置了每个TabView的选中状态。也就说,当选中某个Tab时,我们对应的自定义View的setSelected()方法就会调用。所以,当需要根据Tab是否选中更新自定义View的状态时,可以重写setSelected()方法:
好了,分析完毕,原谅我这个标题党,我只想简单分析如何根据Tab是否选中更新自定义View的状态。对于源码感兴趣的,可以自行前往研究…
Android Material Design 中的TabLayout有两个比较有用的属性 app:tabMode、app:tabGravity。
(1)app:tabMode有两个值:fixed和scrollable。
(2)app:tabGravity有两个值:fill和center。
比较常用的是app:tabMode设置值scrollable,以及app:tabGravity设置值center。
比如,当app:tabMode设置值scrollable表示此TabLayout中当子view超出屏幕边界时候,将提供滑动以便滑出不可见的那些子view。
而app:tabGravity设置值center,在有些情况下,比如TabLayout中子view较少需要居中显示时候的情景。
现在给出一个例子加以说明。
1)tabGravity="fill"和tabMode=“fixed”
2)tabGravity="center"和tabMode=“fixed”
3)tabGravity="fill"和tabMode=“scrollable”
4)tabGravity="cente"和tabMode=“scrollable”
最后,附上源码地址:https://github.com/setasmallgoal/TabLayoutDemo