安卓tab,viewPaper以及frament的使用

安卓TabLayout,ViewPager以及fragment的使用

Demo效果

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

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

 


 

 

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

  • tabLayout :在图中表示的上面的可以进行滑动的tab。android

  • fragment:git

    fragment 简单点来讲就是一个模块,相似activity,在里面能够放一些其余的组件(好比说textView等等),而且有着本身的生命周期。可是它必须放在activity中间,具体的一些信息,能够去看一看官方文档中文英文(推荐看英文的,感受中文的就是机翻,怪怪的)github

  • viewPaper:app

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

     


     

     

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

所以简单点说,就是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的适配器进行序列化了。

 


 

 

总的来讲这个做业仍是比较简单的,只不过在这个序列化的这个地方被坑了很久,查问题感受本身的头发都掉了好多。

相关文章
相关标签/搜索