前两天想熟悉一下tablayout,因此就弄了个特别简单的tablayout+viewpager+fragmet+recyclerview结合的小demo.虽然特别简单,但仍是遇到了很多问题。java
tablayout显示标题,viewpager实现滑动,fragment装载数据,recyclerview显示数据。android
implementation 'com.android.support:appcompat-v7:xxx' implementation 'com.android.support:design:xxx' implementation 'com.android.support:recyclerview-v7:xxx'bash
<android.support.design.widget.TabLayout
复制代码
java文件中:网络
tabLayout = (TabLayout) findViewById(R.id.tab);
tabLayout.setupWithViewPager(viewPager);
复制代码
在这里要记得设一下tabLayout的mode,有两个值,分别是 TabLayout.MODE_FIXED :当Tab较少,且占满整个屏幕时可使用这种模式。 TabLayout.MODE_SCROLLABLE :当Tab数量较多,屏幕宽度不够时使用该模式,整个TabLayout是能够左右滑动的。app
tabLayout.setTabMode(TabLayout.MODE_FIXED);
复制代码
xml引入ide
<android.support.v4.view.ViewPager
复制代码
java文件中函数
viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentList));
复制代码
public class MyFragment extends Fragment {
复制代码
继承了fragmentPagerAdapter的类不须要重写那么多方法,必须有的是getItem(int)和getCount()。不过我还加了这两个方法。布局
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { }
@Override
public CharSequence getPageTitle(int position) {
stringList.add("碎片1");
stringList.add("碎片2");
stringList.add("碎片3");
return stringList.get(position);
}
复制代码
解释一下,第一个方法重载方法是为了防止viewpager在滑动切换的时候,里面的fragment被销毁,致使数据须要从新加载。若是此时viewpager里面的数据是从网络获取的,那么每一轮滑动,都要从新进行网络获取,很是影响性能。性能
查找资料后经试验,直接在destroyItem里面将其super.destroyItem()删除掉。这样就不会从新加载数据了。固然还有其余方式处理这种状况,若是有小伙伴试验成功的,更好的方法欢迎下方留言指教。学习
第二个重载方法,是为了显示tabLayout的标题,标题这里出了个大坑,网上不少例子都是直接在主activity那里addTab,setText,可是很容易会遇到标题没法显示的问题。解决办法就是在执行了tablayout.setUpWithViewPager以后再设置经过tablayout.getTabAt(position).setText(String)去设置。
然而,知道了tabLayout怎么工做以后相信你们会更愿意选择直接在fragment里面利用getPageTitle来操做。
查看源码能够发现,这里竟然把全部添加的tab给remove掉了,因此在这个方法以前添加的tab和标题会都没有效果。另外在for里面咱们能够观察到,其实tabLayout会根据adapter的数量添加tab和标题.因此咱们能够不用在主acivity添加tab,标题设置直接在fragment里面利用getPageTitle就行了。
void populateFromPagerAdapter() {
this.removeAllTabs();
if (this.pagerAdapter != null) {
int adapterCount = this.pagerAdapter.getCount();
int curItem;
for(curItem = 0; curItem < adapterCount; ++curItem) {
this.addTab(this.newTab().setText(this.pagerAdapter.getPageTitle(curItem)), false);
}
if (this.viewPager != null && adapterCount > 0) {
curItem = this.viewPager.getCurrentItem();
if (curItem != this.getSelectedTabPosition() && curItem < this.getTabCount()) {
this.selectTab(this.getTabAt(curItem));
}
}
}
}
复制代码
注:不过有的时候tablayout不跟viewPager和fragment结合在一块儿,那么这个时候就只能在主activity里面将添加tab和标题了。得注意,添加标题在tablayout.setUpWithViewPager()方法后面添加。
viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(), fragmentList));
复制代码
fragmentList就是你事先要准备好的数据源
List<Fragment> fragmentList = new ArrayList<>();
fragmentList.add(new MyFragment());
fragmentList.add(new MyFragment());
fragmentList.add(new MyFragment());
复制代码
内个MyFragment这些是本身自定义的fragment类,包括相关的xml。fragment类里面作的主要是加载xml视图。xml里面的东西就是你要显示的东西啦,那在这里就是recyclerview。 贴fragment类,太长,弄成两张。
public class MyFragment extends Fragment {
@Nullable
List<String> data_top = new ArrayList<>();
List<String> data_bottom = new ArrayList<>();
List<String> data_new = new ArrayList<>();
RecyclerView recyclerView;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View fragment1 = inflater.inflate(R.layout.fragment_first, container, false);
recyclerView = (RecyclerView) fragment1.findViewById(R.id.recycler_first);
initRecycler();
return fragment1;
}
public void initRecycler() {
data_top.add("Layout Manager:Item的布局");
data_top.add("为Item提供数据");
data_top.add("Item Decoration:Item之间的Divider");
data_top.add("Item Animator:添加、删除Item动画");
data_bottom.add("学习雷锋好榜样");
data_bottom.add("学习雷锋好榜样");
data_bottom.add("学习雷锋好榜样");
data_bottom.add("学习雷锋好榜样");
data_new.add("立场坚决斗志强");
data_new.add("立场坚决斗志强");
data_new.add("立场坚决斗志强");
data_new.add("立场坚决斗志强");
MainRecyclerAdapter mainRecyclerAdapter = new MainRecyclerAdapter(data_top, data_bottom, data_new, getContext());
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(mainRecyclerAdapter);
mainRecyclerAdapter.notifyDataSetChanged();
}
}
复制代码
欸又看到新朋友了哦,来来来RecyclerView,介绍一下你是干吗的~
若是你是想在fragment里面显示简单的数据,那么好办,但若是你是想经过RecyclerView显示你的数据呢,那可就没那么简单了。 RecyclerView呢,它本身也须要有一个数据适配器。 好咯那就给他造一个咯。插一个完整的文件代码。
public class MainRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
Context context;
//设置不一样类型adapter的区分enum
public static enum TYPE_ENUM {
IMAGE_ENUM,
TEXT_ENUM
}
public static class TextHolder extends RecyclerView.ViewHolder {
TextView text_top, text_bottom;
ImageView icon;
public TextHolder(@NonNull View itemView) {
super(itemView);
text_top = (TextView) itemView.findViewById(R.id.tv_top);
text_bottom = (TextView) itemView.findViewById(R.id.tv_bottom);
icon = (ImageView) itemView.findViewById(R.id.iv_icon);
}
}
public static class ImageHolder extends RecyclerView.ViewHolder {
TextView textTop;
EditText textBottom;
ImageView editIcon;
public ImageHolder(@NonNull View itemView) {
super(itemView);
textTop = (TextView) itemView.findViewById(R.id.tv_top);
textBottom = (EditText) itemView.findViewById(R.id.tv_bottom);
editIcon = (ImageView) itemView.findViewById(R.id.iv_image_left);
}
}
List<String> mDatas_top = new ArrayList<>();
List<String> mData_bottom = new ArrayList<>();
List<String> mData_new = new ArrayList<>();
public MainRecyclerAdapter(List<String> mDatas_top, List<String> mData_bottom, List<String> mData_new, Context context) {
this.mDatas_top = mDatas_top;
this.mData_bottom = mData_bottom;
this.mData_new = mData_new;
this.context = context;
}
//判断是哪一类型的adapter
@Override
public int getItemViewType(int position) {
return position % 2 == 0 ? TYPE_ENUM.IMAGE_ENUM.ordinal() : TYPE_ENUM.TEXT_ENUM.ordinal();
}
//getItemViewType的返回值就是该方法的int i,因此经过i判断是哪一类型的adapter
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (i == TYPE_ENUM.IMAGE_ENUM.ordinal()) {
return new ImageHolder(LayoutInflater.from(context).inflate(R.layout.recycleritemnew, viewGroup, false));
} else {
return new TextHolder(LayoutInflater.from(context).inflate(R.layout.recycler_item, viewGroup, false));
}
}
//根据不一样类型的holder进行不一样操做
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) {
if (holder instanceof ImageHolder) {
//((ImageHolder) holder).textTop.setText(mData_new.get(i));
((ImageHolder) holder).textBottom.setText("haoya");
((ImageHolder) holder).editIcon.setBackgroundResource(R.mipmap.ic_launcher_round);
} else {
((TextHolder) holder).text_top.setText(mDatas_top.get(i));
((TextHolder) holder).text_bottom.setText(mData_bottom.get(i));
((TextHolder) holder).icon.setBackgroundResource(R.mipmap.ic_launcher);
//holder.itemView.setBackgroundResource(R.mipmap.ic_launcher);
}
}
@Override
public int getItemCount() {
return mDatas_top.size();
}
}
复制代码
代码有点长。先不说不一样类型的holder,先说单一类型的,用RecyclerView要怎么实现。 RecyclerView的实现相对于咱们熟悉的listview来讲确实复杂了一点点的。好嘞~如今详细说说各部分。
(1)先写一下列表每一行想要有什么布局,个人是上字下字右图,图是固定的。这个的话就不贴图了,看你本身须要。
(2)通常嘛,要在java文件里将xml里面的东西加载出来,那么RecyclerView里谁完成了这个任务呢?,没错就是他啦~建立holder的视图。
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (i == TYPE_ENUM.IMAGE_ENUM.ordinal()) {
return new ImageHolder(LayoutInflater.from(context).inflate(R.layout.recycleritemnew, viewGroup, false));
} else {
return new TextHolder(LayoutInflater.from(context).inflate(R.layout.recycler_item, viewGroup, false));
}
}
复制代码
(3)那么建立好了,就得绑定是吧,嗯嗯,这里
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int i) {
if (holder instanceof ImageHolder) {
//((ImageHolder) holder).textTop.setText(mData_new.get(i));
((ImageHolder) holder).textBottom.setText("haoya");
((ImageHolder) holder).editIcon.setBackgroundResource(R.mipmap.ic_launcher_round);
} else {
((TextHolder) holder).text_top.setText(mDatas_top.get(i));
((TextHolder) holder).text_bottom.setText(mData_bottom.get(i));
((TextHolder) holder).icon.setBackgroundResource(R.mipmap.ic_launcher);
//holder.itemView.setBackgroundResource(R.mipmap.ic_launcher);
}
}
复制代码
根据你xml里面的控件,进行设置。一种类型的话,就直接这样就行了 代码为举例,具体你懂的
holder.itemView.setBackgroundResource(R.mipmap.ic_launcher);
复制代码
还有什么呢看一下,噢~绑定视图的时候数据从哪来,是的,从构造函数来。就是说将准备好的数据这样 MainRecyclerAdapter mainRecyclerAdapter = new MainRecyclerAdapter(data_top, data_bottom, data_new, getContext()); 再这样,
List<String> mDatas_top = new ArrayList<>();
List<String> mData_bottom = new ArrayList<>();
List<String> mData_new = new ArrayList<>();
public MainRecyclerAdapter(List<String> mDatas_top, List<String> mData_bottom, List<String> mData_new, Context context) {
this.mDatas_top = mDatas_top;
this.mData_bottom = mData_bottom;
this.mData_new = mData_new;
this.context = context;
}
复制代码
最后这样,
((TextHolder) holder).text_top.setText(mDatas_top.get(i));
((TextHolder) holder).text_bottom.setText(mData_bottom.get(i));
((TextHolder) holder).icon.setBackgroundResource(R.mipmap.ic_launcher);
复制代码
哇文章这么长啦。应该完了吧。欸不对好像还少了点什么嗯嗯,咱可不能遗忘了他的存在,他但是颇有存在感的呢~ 它!是继承了viewholder的一个住在adapter类里面的类,做用是将xml的控件findviewbyid。
public static class TextHolder extends RecyclerView.ViewHolder {
TextView text_top, text_bottom;
ImageView icon;
public TextHolder(@NonNull View itemView) {
super(itemView);
text_top = (TextView) itemView.findViewById(R.id.tv_top);
text_bottom = (TextView) itemView.findViewById(R.id.tv_bottom);
icon = (ImageView) itemView.findViewById(R.id.iv_icon);
}
}
复制代码
有一个点,这里的话,若是是一个holder而已,画框部分能够写成MainRecyclerAdapter.VH--VH是你的内部类名。
public class MainRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
复制代码
我我的对这个adapter的理解,首先他继承了RecyclerView.adapter,实现了几个必要的方法,加了个内部类
holder的话,我以为是一个兜,兜住了视图的控件。 oncreateviewholder的话,那么就是建立出这个holder。 onbindviewholder的话,就是将控件须要的数据源给到对应的控件。
有可能理解得不许确,但愿你们不吝赐教。
好了呢,单类型的adapter就这样啦。单类型的意思就是说RecyclerView从头至尾,控件的位置类型都是一致的,同种风格。可是有的时候,也会遇到须要不一样类型的需求。这个时候就要出动另外一个父类方法getItemViewType(int position),在这里面根据position设置这一行须要的是什么类型的adapter,详细见上边的完整代码。相关地方有注解。相信各位能够看懂~
完结!
- 杨晓华:广州芦苇科技 APP 团队 Android 开发实习生
- 咱们正在招募小伙伴,有兴趣的小伙伴能够把简历发到 app@talkmoney.cn,备注:来自掘金社区
- 详情能够戳这里--> 广州芦苇信息科技