安卓TabLayout,ViewPager以及fragment的使用

Demo效果

首先先说一下这个demo的最终效果吧:

项目地址:https://github.com/xiaohuiduan/fragment_demo

 

我们的安卓作业需要我们结合RecyclerView,TabLayout,PaperView以及fragment做一个简单的Demo,于是便有了这样的一个demo,其中页面中的数据来自于玩安卓的开放API,使用的是其中的公众号接口。

  • tabLayout :在图中表示的上面的能够进行滑动的tab。

  • fragment:

    fragment 简单点来说就是一个模块,类似activity,在里面可以放一些其他的组件(比如说textView等等),并且有着自己的生命周期。但是它必须放在activity中间,具体的一些信息,可以去看一看官方文档中文英文(推荐看英文的,感觉中文的就是机翻,怪怪的)

  • viewPaper:

    这个是一段来自官网的介绍

     

    ViewPaper就是简单的页面切换组件,我们往里面填充View,然后就可以使用左滑和右滑来进行View的切换。和RecycleView(或者ListView)很类似,我们都需要使用Adapter(PagerAdapter)来填充数据。google官方文档中,推荐我们使用Fragment来填充ViewPager。

因此简单点说,就是TabLayout和ViewPager相关联,然后通过滑动ViewPager实现Fragment的切换,然后在Fragment中使用RecycleView来显示数据。

主要代码分析

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"> <com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:tabMode="scrollable"
xmlns:android="http://schemas.android.com/apk/res/android" /> <androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/article_title_view_pager" /> </LinearLayout>

MainActivity的代码

public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private ViewPager viewPager; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.article_title_view_pager);
init();
} private void init() {
// 下面的这个代表目前是横屏还是竖屏,不需要理会
if (findViewById(R.id.land_content) != null) {
PhoneMsg.isTwoPane = true;
} else {
PhoneMsg.isTwoPane = false;
}
initTab();
} public void initTab() {
try {
// 下面的代表获取数据,我们只需要知道PhoneMsg.wxAuthorList保存了文章的信息即可
AsyncTask<ProcessInterface, Integer, Object> asyncTask = new AsyncRequest().execute(new GetWxAuthorList());
PhoneMsg.wxAuthorList = (WxAuthorList) asyncTask.get(); tabLayout = findViewById(R.id.tablayout); // 创建Tab页
for (int i = 0; i < PhoneMsg.wxAuthorList.getData().size(); i++) {
tabLayout.addTab(tabLayout.newTab());
}
// ArticleTitlePageAdapter表示的就是ViewPager的适配器(后面会说明),其中我们需要传过去的参数是,getSupportFragmentManager():为了拿到Fragment的控制器, tabLayout.getTabCount(),获得view的个数
ArticleTitlePageAdapter adapter = new ArticleTitlePageAdapter(getSupportFragmentManager(), tabLayout.getTabCount()); viewPager.setAdapter(adapter);
// 这一步是为了将tabLayout与viewpaper同步,值得注意的点是:如果将这一步放在tablayOut的setText后面,则会导致tab的名字空白
tabLayout.setupWithViewPager(viewPager);
// 创建Tab页
for (int i = 0; i < PhoneMsg.wxAuthorList.getData().size(); i++) {
String name = PhoneMsg.wxAuthorList.getData().get(i).getName();
int id = PhoneMsg.wxAuthorList.getData().get(i).getId();
tabLayout.getTabAt(i).setText(name); // ArticleListAdapter代表的是RecycleView的适配器,我们将这个Adapter保存起来(为什么要这样做我后面说)
PhoneMsg.articleListAdapters.add(i,new ArticleListAdapter(MainActivity.this,id));
}
// 添加选择事件
tabLayout.addOnTabSelectedListener(new TabClick(this));
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
} class TabClick implements TabLayout.OnTabSelectedListener {
Context context; public TabClick(Context context) {
this.context = context;
}
@Override
public void onTabSelected(TabLayout.Tab tab) {
// 将viewPaper的位置改变
viewPager.setCurrentItem(tab.getPosition());
} @Override
public void onTabUnselected(TabLayout.Tab tab) { } @Override
public void onTabReselected(TabLayout.Tab tab) { }
}
}

ViewPager的适配器

这个适配器的功能很简单,就是为了返回Fragment去填充ViewPager。可以看到下面的代码,就是根据不同position来产生不同的Fragment。

public class ArticleTitlePageAdapter extends FragmentStatePagerAdapter {

    private int num;
private HashMap<Integer,ArticleTitleFra> map; public ArticleTitlePageAdapter(@NonNull FragmentManager fm, int num) {
super(fm, num);
this.num = num;
map = new HashMap(num);
} /**
* 返回对应位置的Fragment
* @param position 代表目前滑动所处的位置
* @return
*/
@NonNull
@Override
public Fragment getItem(int position) {
if (map.containsKey(position)){
return map.get(position);
}
return createFragment(position);
} /**
* 创建一个frame
* @param position
* @return
*/
private Fragment createFragment(int position) {
// ArticleTitleFra.newInstance(position)会返回一个Fragment
map.put(position, ArticleTitleFra.newInstance(position));
return map.get(position);
} @Override
public int getCount() {
return num;
}
}

下面就是关于Fragment的适配器,其中在Fragment中是一个RecycleView。

Fragment的适配器

public class ArticleTitleFra extends Fragment {

    private View view;
private RecyclerView recyclerView;
private ArticleListAdapter adapter; public static ArticleTitleFra newInstance(int position) {
Bundle bundle = new Bundle();
bundle.putInt("Position", position);
ArticleTitleFra articleTitleFra = new ArticleTitleFra();
articleTitleFra.setArguments(bundle);
return articleTitleFra;
} @Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
int position = getArguments().getInt("Position");
// PhoneMsg.articleListAdapters里面保存了RecycleView的适配器
adapter = PhoneMsg.articleListAdapters.get(position);
// article_title_fra表示的就是Fragment 的xml文件
view = inflater.inflate(R.layout.article_title_fra, container, false);
// 设置recyclerView
initFrame();
// refresh()目的是为了设置recycleView的Adapter
refresh();
return view;
} private void initFrame() {
recyclerView = view.findViewById(R.id.article_list);
recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext()));
// 设置 ItemAnimator动画
recyclerView.setItemAnimator(new DefaultItemAnimator());
} /**
* 刷新数据
*
* @param
*/
public void refresh() {
recyclerView.setAdapter(this.adapter);
}
}

RecycleView的适配器我就不展示code了。在这里个里面我们有一个值得注意的点,在Fragment中,我们是使用了一个newInstance来示例化一个对象,并且将position保存在Bundle中,为什么我们这样做呢?假设我们不这样做,那么在横竖屏切换的时候会产生一个问题。就是在竖屏切换到横屏的时候,recycleView的数据会消失。(值得注意点的是:在MainActivity中onCreate方法会再次执行一遍)。按照道理来说,不应该出现这种情况的,那么为什么会出现这种情况呢?初略的浏览了一下源代码,我觉的可能是这样的:

FragmentStatePagerAdapter中会将Fragment进行序列化,在加载新的页面的时候,它会看以前有没有进行加载,如果加载了,则从序列化的数据中将Fragment拿出来。我们可以用Log进行日志输出,然后会发现,在横竖屏转动的时候,这一个日志并不会进行打印。

那么,为什么我们使用newInstance来实例化对象而不是通过构造方法来示例化对象呢?很可惜,不行,因为在Fragment中,会通过反射调用Fragment的无参构造函数来获得Fragment的实例。所以如果我们在Fragment中只写了有参构造器而没有写无参构造器,那么程序则会报java.lang.RuntimeException: Unable to start activity ComponentInfo{cc.weno.android_news/cc.weno.android_news.MainActivity}: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment cc.weno.android_news.fragment.ArticleTitleFra: could not find Fragment constructor的错误。

而在Fragment序列化的过程中,会将Bundle进行序列化,而在反序列化中,又会将boundlei进行反序列化,so,我们就可以在创建实例化的过程中将同时设置Bundle,因此下面的情况也是ok的。

public ArticleTitleFra(){

}

public ArticleTitleFra(int position){
Bundle bundle = new Bundle();
bundle.putInt("Position", position);
setArguments(bundle);
}

这也就解释了为什么我使用PhoneMsg.articleListAdapters来保存recycleView的适配器了,因为在Fragment createView的时候,我们根据position的位置来获得对应的适配器。那么我们将recycleview的适配器添加到Bundle中呢?当然没有问题,不过我们就需要对recycleview的适配器进行序列化了。

 

总的来说这个作业还是比较简单的,只不过在这个序列化的这个地方被坑了好久,查问题感觉自己的头发都掉了好多。

安卓tab,viewPaper以及frament的使用的更多相关文章

  1. ionic安卓tab栏跑到顶部

    安卓下的ionic的tab会在顶部显示,而不是在底部 解决办法如下: 在app.js文件中的.config代码块里中添加以下代码: 注意依赖注入$ionicConfigProvider $ionicC ...

  2. Android源码:(一) 安卓2.1到4.4操作系统通用的Actionbar实现的tab导航例子。

    说一下我在完成这个例子之前的尝试吧 一,修改“actionbarsherlock”的导航例子.我在实现这个例子之前,尝试过“actionbarsherlock”,修改它的一个tab导航的例子,修改成功 ...

  3. 安卓开发_慕课网_Fragment实现Tab(App主界面)

    学习内容来自“慕课网” 这里用Fragment来实现APP主界面 思路: 底部横向排列4个LinearLayout,每个LinearLayout包含一个图片按钮和一个文字 1.默认显示第一个功能(微信 ...

  4. 安卓开发_慕课网_ViewPager与FragmentPagerAdapter实现Tab实现Tab(App主界面)

    学习内容来自“慕课网” ViewPager与FragmentPagerAdapter实现Tab 将这两种实现Tab的方法结合起来.效果就是可以拖动内容区域来改变相应的功能图标亮暗 思路: Fragme ...

  5. 安卓开发_慕课网_ViewPager实现Tab(App主界面)

    学习内容来自“慕课网” 网站上一共有4种方法来实现APP主界面的TAB方法 这里学习第一种 ViewPager实现Tab 布局文件有7个, 主界面acitivity.layout <Linear ...

  6. 安卓开发之使用viewpager+fragment实现滚动tab页

    闲着.用viewpager+fragment实现了个滚动tab..轻拍,以后会陆续发先小东西出来..爱分享,才快乐.demo见附件.. package com.example.demo; import ...

  7. 安卓仿微信Tab页用Fragment实现

    最终效果图如: 实现步骤: 新建项目tabdemo,我选的是4.0.3版本,然后依次新建三个Fragment,名字分别为:ChatFragment.FriendFragment.FindFragmen ...

  8. ios 和安卓常用图标、启动图 尺寸

    ---------------------------------------------ios---------------------------------------------------- ...

  9. 微信5.4安卓版重回ios风格 导航菜单都放底栏位置

    微信5.4安卓版发布更新了,由于本人的手机设置软件自动更新,中午的时候才发现微信换成了5.4版本,启动微信后是一个大大的“转账,就是发消息”,进入微信界面有点小惊喜,导航菜单都改为底部tab方式,顶部 ...

随机推荐

  1. codeforces 284 D. Cow Program(记忆化搜索)

    题目链接:http://codeforces.com/contest/284/problem/D 题意:给出n个数,奇数次操作x,y都加上a[x],偶数次操作y加上a[x],x减去a[x],走出了范围 ...

  2. 牛客练习赛51 A abc

    A. abc 题意: 给出一个字符串s,你需要做的是统计s中子串”abc”的个数.子串的定义就是存在任意下标a<b<c,那么”s[a]s[b]s[c]”就构成s的一个子串.如”abc”的子 ...

  3. CSS动效集锦,视觉魔法的碰撞与融合(二)

    引言 长久以来,我认识到.CSS,是存在极限的.正如曾经替你扛下一切的那个男人,也总有他眼含热泪地拼上一切,却也无法帮你做到的事情,他只能困窘地让你看到他的无能为力,怅然若失. 然后和曾经他成长的时代 ...

  4. javase复习(一)

    break,continue,return区别: continue:跳出本次循环,还要再执行下次循环 break:跳出循环,若有多层循环则只跳出本层循环,其他层的循环需要挨个break return: ...

  5. 2019沈阳网络赛B.Dudu's maze

    https://www.cnblogs.com/31415926535x/p/11520088.html 啊,,不在状态啊,,自闭一下午,,都错题,,然后背锅,,,明明这个简单的题,,, 这题题面不容 ...

  6. ORM之Dapper运用

    一.前言 上一篇[分层架构设计]我们已经有了架构的轮廓,现在我们就在这个轮廓里面造轮子.项目要想开始,肯定先得确定ORM框架,目前市面上的ORM框架有很多,对于.net人员来说很容易就想到以ADO.N ...

  7. 六星教育php vip视频(分享)

    最近看的一个swoole的课程,应该也算是vip课程了,不是公开的直播课 比较有特点有一定深度,swoole的实战教程一直也不多,结合swoole构建一个新型框架,最后讲解如何实现分布式RPC的调用. ...

  8. ScrollView内嵌ViewPager导致ViewPager滑动困难问题

    转自:http://titanseason.iteye.com/blog/1858874 解决方式:重写ScrollView,然后在xml中定义布局的时候,使用自定义的PagerScrollView而 ...

  9. Redis常用命令(key、string、List)

    1.Key 1.keys *   查询所有数据 2.exists key名   判断key名是否存在 3.move key名  数据库号(0-15)  移动数据key名到相应的数据库 4.expire ...

  10. Android 本地化适配:RTL(right-to-left) 适配清单

    本文首发自公众号:承香墨影(ID:cxmyDev),欢迎关注. 一. 序 越来越多的公司 App,都开始淘金海外,寻找更多的机会.然而海外市场千差万别,无论是市场还是用户的使用习惯,都有诸多的不同. ...