Android TabLayout与ViewPager实现动态Tab
在项目中很少会遇到动态Tab这种需求,但是遇到了也要灵活处理,下面介绍一下实现方法。这里只是实现一个简单的纯文字的Tabs,重点在实现动态效果。
首先需要添加android.support.design依赖才能使用TabLayout这个控件。Android studio 添加如下代码到Gradle文件。
compile 'com.android.support:design:23.1.1'
布局文件
fragment_tablayout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/blue1"
app:tabSelectedTextColor="@color/white"
app:tabTextColor="@color/white"
style="@style/MyCustomTabLayout"
/>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:background="@android:color/white" />
</LinearLayout>
tab style文件
<style name="MyCustomTabLayout" parent="Widget.Design.TabLayout">
<item name="tabIndicatorColor">@color/deepskyblue</item>
<item name="tabIndicatorHeight">2.5dp</item>
<item name="tabPaddingStart">12dp</item>
<item name="tabPaddingEnd">12dp</item>
<item name="tabBackground">?attr/selectableItemBackground</item>
<item name="tabTextAppearance">@style/MyCustomTabTextAppearance</item>
<item name="tabSelectedTextColor">?android:textColorPrimary</item>
</style>
Tablayout控件 xml属性说明:
- tabSelectedTextColor选中该Tab时文字的颜色
- app:tabTextColor正常状态下也就是没被选中的Tab的文字颜色
- style=”@style/MyCustomTabLayout” 通过style属性可以定义更多属性
- tabIndicatorColor Tab指示器的颜色
- tabIndicatorHeight Tab指示器的高度
Activity代码实现
1.实现一个自己的FragmentPagerAdapter,跟一般的Fragment+viewPager实现是一样的。
public class SimpleFragmentPagerAdapter extends FragmentPagerAdapter {
private Context context;
private List<Fragment> fragments;
public SimpleFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
//这个方法返回Tab显示的文字。这里通过在实例化TabFragment的时候,传入的title参数返回标题。
@Override
public CharSequence getPageTitle(int position) {
TabFragment fragment = (TabFragment) fragments.get(position);
return fragment.getTitle();
}
}
2.初始化Viewpager适配器
pagerAdapter = new SimpleFragmentPagerAdapter(getChildFragmentManager(),fragments);
viewpager.setAdapter(pagerAdapter);
3.从服务器获取Tab分类
loadCategoryPresenter.loadCategory();
4.将返回的类别数据绑定到Tab上
这里有个TabMode说明一下:他是Tab的布局方式,
(1) TabLayout.MODE_SCROLLABLE:适用与多个Tab,可以滚动Tab栏
(2)TabLayout.MODE_FIXED:适用与Tab个数较少的情况,每个Tab都平分TabLayout的宽度,不可滚动。
动态Tab的实现是在Activity生命周期中的onResume()中加载服务器的栏目类别。加载完成后将栏目类别缓存到本地,下次再加载的时候比较栏目是否改变过。从而避免重复刷新界面。也就是说在用户进入这个Activity的时候就检测栏目是否改变,改变就重新加载Tabs数据
业务逻辑是通过MVP模式实现的,具体的Model层自行实现吧,也就是通过Http访问网络获取数据。
————-下面附上Activity、Presenter层、View层代码—————–
CategoryPresenterImp.java
public class CategoryPresenterImp extends BaseParenterImp implements CategoryPresenter {
public CategoryPresenterImp(Context context) {
super(context);
}
private LoadCategoryView categoryView;
private CategoryMod loadCategoryMod;
private AsyncTaskClient taskClient;
private ACache aCache;
public CategoryPresenterImp(Context context, LoadCategoryView categoryView){
super(context);
this.categoryView = categoryView;
this.loadCategoryMod = new CategoryModImp(context);
this.taskClient = new AsyncTaskClient();
aCache = ACache.get(context);
}
@Override
public void onDestroy() {
if(taskClient!=null)taskClient.cancelRequests(context,true);
loadCategoryMod = null;
super.onDestroy();
}
/**
* 解析服务器返回的xml,在子线程执行任务
*/
private TaskRequest.Background<List<Category>> parseBackground =
new TaskRequest.Background<List<Category>>() {
@Override
public List<Category> doInBackground(Object param) {
ArrayList<Category> categories = loadCategoryMod.parseCategory((String) param);
return categories;
}
};
/**
* 解析完成后回调
*/
private TaskResponseHandler parseResponseHandler = new TaskResponseHandler() {
@Override
public void onSuccess(int statusCode, Object responseBody) {
categoryView.categorySuccessCall((List<Category>) responseBody);
}
@Override
public void onFailure(int statusCode, Throwable error) {
categoryView.categoryFailedCall(error.getMessage());
}
};
/**
* 请求网络返回回调,返回成功后开始解析
*/
private RequestCallBack<String> loadCallBack = new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String res = responseInfo.result.toString();
TaskRequest taskRequest = new TaskRequest(res,parseBackground);
taskClient.sendRequest(taskRequest, parseResponseHandler,context );
}
@Override
public void onFailure(HttpException error, String msg) {
categoryView.categoryFailedCall("网络连接异常");
}
};
@Override
public void loadCategory() {
loadCategoryMod.loadCategory(loadCallBack);
}
//缓存栏目类别
@Override
public void saveCategory(ArrayList<Category> categoryArrayList) {
ACache.get(context).put(AppConfig.CATEGORY_LIST,categoryArrayList);
}
//获取本地缓存栏目类别
@Override
public ArrayList<Category> readCategory() {
return (ArrayList<Category>) ACache.get(context).getAsObject(AppConfig.CATEGORY_LIST);
}
//比对当前类别很缓存类别是否发生改变
@Override
public boolean categoryChanged(ArrayList<Category> nowCategoryList) {
ArrayList<Category> lastcategories = readCategory();
if(lastcategories == null || lastcategories.size() == 0
|| nowCategoryList == null || nowCategoryList.size() == 0){
return true;
}else {
if(nowCategoryList.size()!=lastcategories.size())return true;
for(int i=0;i<lastcategories.size();i++){
String nowId = nowCategoryList.get(i).getId();
String lastId = lastcategories.get(i).getId();
if(!nowId.equals(lastId)){
return true;
}
}
}
return false;
}
//清空栏目缓存
@Override
public void clearCategory() {
ACache.get(context).put(AppConfig.CATEGORY_LIST,new ArrayList<Category>());
}
TablayoutFragment.java
public class TablayoutFragment extends BaseFragment implements LoadCategoryView{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tablayout, null);
}
private TabLayout tabs;
private ViewPager viewpager;
private CategoryPresenter loadCategoryPresenter;
private void assignViews() {
tabs = (TabLayout) findView(R.id.tabs);
viewpager = (ViewPager) findView(R.id.viewpager);
}
private SimpleFragmentPagerAdapter pagerAdapter;
private List<Fragment> fragments = new ArrayList<>();
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
loadCategoryPresenter = new CategoryPresenterImp(mContext,this);
loadCategoryPresenter.clearCategory();//每次运行的时候都清一下缓存,以免强制退出后,不会刷新类别
assignViews();
pagerAdapter = new SimpleFragmentPagerAdapter(getChildFragmentManager(),fragments);
viewpager.setAdapter(pagerAdapter);
tabs.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewpager) {
@Override
public void onTabSelected(TabLayout.Tab tab) {
super.onTabSelected(tab);
int position = tab.getPosition();
}
});
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onSaveInstanceState(Bundle outState) {
loadCategoryPresenter.clearCategory();
super.onSaveInstanceState(outState);
}
@Override
public void onResume() {
super.onResume();
loadCategoryPresenter.loadCategory();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void categorySuccessCall(List<Category> categoryList) {
boolean categoryChanged = loadCategoryPresenter.categoryChanged((ArrayList<Category>) categoryList);
if(categoryChanged){
fragments.clear();
for (Category category : categoryList){
String id = category.getId();
String name = category.getName();
TabFragment tabFragment = new TabFragment(id,name);
fragments.add(tabFragment);
}
pagerAdapter.notifyDataSetChanged();
viewpager.setOffscreenPageLimit(fragments.size());
tabs.setupWithViewPager(viewpager);
if(fragments.size()>4){
tabs.setTabMode(TabLayout.MODE_SCROLLABLE);
}else {
tabs.setTabMode(TabLayout.MODE_FIXED);
}
}
loadCategoryPresenter.saveCategory((ArrayList<Category>) categoryList);
}
@Override
public void categoryFailedCall(String info) {
}
LoadCategoryView.java
/**
* 加载栏目界面回调
*/
public interface LoadCategoryView {
void categorySuccessCall(List<Category> categoryList);
void categoryFailedCall(String info);
}