ViewPager+Fragment+ViewPager+Fragment

最近一段时间,Android行业大不如从前轻松,企业要求越来越高了,就算入职了很多时候Android这块也不太受重视,现在Android开发者还能经常接到新需求就算是很幸运的事了,我最近的工作也是没什么活,闲来无事就整理一下Android的一些东西,以免遗忘。

这次要做的类似于许多新闻资讯类app,底部tab切换不同的功能页面,首页中又可以切换不同的新闻类型页面,当切换到最后一个新闻fragment时,再向后滑则父viewpager滑动,具体实现是用的ViewPager+Fragment+ViewPager+Fragment,tab用的是desgin库中的TabLayout,fragment实现懒加载,数据源用的是聚合数据(https://www.juhe.cn/),网络框架用的是张鸿洋大神的OkhttpUtils,因为只是做一个效果,就不搭建Rxjava+Retrofit网络请求框架了,主要用到的三方依赖如下:

 

compile "com.android.support:design:25.+"
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.zhy:okhttputils:2.6.2'
compile 'com.android.support:recyclerview-v7:25+'

 

 

 

一、准备工作

1. BaseApplica

可以自定义一个Application,一是为了初始化OkhttpUtils,另一个是为了获取一个使用较为方便的Context,因为fragment嵌套过多,使得Context获取比较麻烦

public class BaseApplication extends Application {
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
//                .addInterceptor(new LoggerInterceptor("TAG"))
                .connectTimeout(10000L, TimeUnit.MILLISECONDS)
                .readTimeout(10000L, TimeUnit.MILLISECONDS)
                //其他配置
                .build();

        OkHttpUtils.initClient(okHttpClient);
    }

    public static Context getContext(){
        return context;
    }
}

 

2. SharedPreferencesHelper

 

定义一个SharedPreferences用户偏好存储主要是因为我想实现一个可以添减新闻类型的效果,不过因为昨晚感觉时间有点晚了,而且代码量对博客而言已经不少了,就给这个思路,有兴趣或者有这个需求的兄弟可以参考下这个思路完成。

public class SharedPreferencesHelper {
    private SharedPreferences sharedPreferences;
    private SharedPreferences.Editor editor;

    public SharedPreferencesHelper(Context context,String preferenceName){
        sharedPreferences = context.getSharedPreferences(preferenceName,Context.MODE_PRIVATE);
        editor = sharedPreferences.edit();
    }

    public void setSharedPreferencesString(String  tag,boolean isFirst) {
        editor.putBoolean(tag,isFirst);
        editor.commit();
    }

    public boolean getSharedPreferencesString(String tag) {
        return sharedPreferences.getBoolean(tag,false);
    }

    public <T> void setSharedPreferencesList(String tag, List<T> list){
        if (null==list||list.size()==0) {
            return;
        }
        Gson gson = new Gson();
        String s = gson.toJson(list);
        editor.clear();
        editor.putString(tag,s);
        editor.commit();
    }

    public <T> List<T> getSharedPreferencesList(String tag) {
        List<T> datas = new ArrayList<>();
        String string = sharedPreferences.getString(tag, null);
        if (null==string) {
            return datas;
        }
        Gson gson = new Gson();
        datas = gson.fromJson(string,new TypeToken<List<T>>(){}.getType());
        return datas;
    }
}

 

这个类很简单,初始化的时候传入Context和存储名,创建sharedPreferencesEditor然后存取文字一对方法,存取list集合一对方法,因为SharedPreferences存储只能存一些基本类型,所以先用Gson将list转化为String类型再进行存储,取的时候再用Gson把数据转化为list。使用也很简单,在MainActivity中进行是否初次进入判断,初次进入赋给喜欢新闻类型的list集合初始值,当然一般app的设计都是有几个新闻类型是一定存在的,这个无所谓,实现上大同小异。然后在初始化浏览新闻页面时,获取新闻类型集合,根据集合的数量创建相应个数的fragment,每个fragment根据新闻类型再进行网络访问获取新闻内容,具体实现如下。

 

二、页面设计

1. MainActivity

MainActivity是承载整个ViewPager+Fragment+ViewPager+Fragment的载体,专业来说是他们的父布局,我对它的设计很简单,就是上边一个ViewPager滑动页卡,下边一个TabLayout存放tab,然后在viewPager中添加fragment,TabLayout中添加tab,最后将二者关联起来:

public class MainActivity extends AppCompatActivity {
    private TabLayout tabLayout;
    private ViewPager viewPager;
    private List<Fragment> fragments = new ArrayList<>();
    private List<String> pageTitle = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SharedPreferencesHelper isFirstSave = new SharedPreferencesHelper(this, "isFirstSave");
        if (!isFirstSave.getSharedPreferencesString("isFirst")) {
            isFirstSave.setSharedPreferencesString("isFirst",true);
            List<String> list = new ArrayList<>();
            list.add("头条");
            list.add("社会");
            list.add("科技");
            new SharedPreferencesHelper(BaseApplication.getContext(),"newsDatasSave").setSharedPreferencesList("newsDatas",list);
        }
        viewPager = (ViewPager) findViewById(R.id.activity_main_pager);
        tabLayout = (TabLayout) findViewById(R.id.activity_main_tab);
        initPager();
        initTab();
        tabLayout.setupWithViewPager(viewPager);//关联viewPager和fragment
        for (int i = 0; i < fragments.size(); i++) {
            tabLayout.getTabAt(i).setText(pageTitle.get(i)).setIcon(R.mipmap.ic_launcher);//设置tab显示文字和图片
        }
    }

    private void initPager() {
        fragments.add(new MainFragment());
        fragments.add(new MsgFragment());
        fragments.add(new MineFragment());
        pageTitle.add("首页");
        pageTitle.add("消息");
        pageTitle.add("我的");
        ActivityFragmentAdapter activityFragmentAdapter = new ActivityFragmentAdapter(getSupportFragmentManager());
        viewPager.setAdapter(activityFragmentAdapter);
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    private void initTab() {
        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    class ActivityFragmentAdapter extends FragmentPagerAdapter {
        public ActivityFragmentAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return fragments.get(position);
        }

        @Override
        public int getCount() {
            return fragments.size();
        }
        
//        这里设置的文字值将会在tab上显示
//        @Override
//        public CharSequence getPageTitle(int position) {
//            return pageTitle.get(position);
//        }
    }
}

这里需要注意的一点事tab文字和图片的设置一定要放在viewpager和fragment关联之后或者在getPageTitle方法中仅设置显示文字,否则设置的文字不会显示。

2. BaseFragment

接下来就要设计Fragment,寿险我们要设计一个基类,用作实现懒加载。所谓懒加载就是只有当fragment显示且无数据才加载数据,当数据加载完毕,我们要将其显示到控件,所以此时控件必须已加载完毕,否则会报空指针,综上,我们共需要三个判断。

 

public abstract class BaseFragment extends Fragment {
    private boolean isInitData = false;//是否初始化数据
    private boolean isVisibleToUser = false;//是否可见
    private boolean isPrepareView = false;//view是否加载完成

    protected List<String> newsDatas;//子fragment对应的tab集合

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(getLayoutId(), container, false);

    }

    protected abstract int getLayoutId();

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        newsDatas = new SharedPreferencesHelper(BaseApplication.getContext(), "newsDatasSave").getSharedPreferencesList("newsDatas");
        isPrepareView = true;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        this.isVisibleToUser = isVisibleToUser;
        lazyInitData();
    }

    private void lazyInitData() {
        if(!isInitData&&isVisibleToUser&&isPrepareView){
            isInitData = true;
            initData();
        }
    }

    protected abstract void initData();

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        lazyInitData();
    }
}

 

onViewCreated方法执行我们可以获取到view已初始化完毕的信息,setUserVisibleHint方法获取的是此fragment是否呈现的信息,当呈现时我们可以试着做第一次懒加载,如果无数据获取则讲数据获取的值改为true,并进行数据获取。为了防止fragment第一次可见未加载数据,在onActivityCreated再次做一个加载判断。

 

由于我最初的设想是显示新闻内容的fragment个数是可变的,而且fragment只有一个类,所以要获取页卡是什么新闻类型,最简单的实现应该就是把这两个参数放到基类里面,父tab显示的新闻类型在基类中获取。

3. MainFragment

显示新闻页卡的父fragment,布局实现和MainActivity差不多

public class MainFragment extends BaseFragment {
    private TabLayout tabLayout;
    private ViewPager viewPager;

    @Override
    protected int getLayoutId() {
        return R.layout.fragment_main;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        tabLayout = (TabLayout) view.findViewById(R.id.fragment_main_tab);
        viewPager = (ViewPager) view.findViewById(R.id.fragment_main_pager);
        initTab();
        initPager();
        tabLayout.setupWithViewPager(viewPager);
        for (int i = 0; i < newsDatas.size(); i++) {
            tabLayout.getTabAt(i).setText(newsDatas.get(i));
        }
    }

    private void initTab() {

        tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });
    }

    private void initPager() {
        final List<Fragment> fragments = new ArrayList<>();
        for (int i = 0; i < newsDatas.size(); i++) {
            NewsFragment newsFragment = new NewsFragment();
            fragments.add(newsFragment);
            newsFragment.setPosition(i);
        }
        viewPager.setAdapter(new FragmentPagerAdapter(getChildFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return fragments.get(position);
            }

            @Override
            public int getCount() {
                return fragments.size();
            }

            //            @Override
//            public CharSequence getPageTitle(int position) {
//                return newsDatas.get(position);
//            }
        });
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });
    }

    @Override
    protected void initData() {

    }
}

 

这里要注意的问题是新闻fragment集合fragments的初始化,fragment经常会出现突然多一块白色区域,嵌套时多了白色页卡或者页卡被挤在一旁等等,这些要注意的问题是fragment中view大小设置和viewpager的list个数变化,此demo中如果fragments在类中初始化话,方法initPager中就会不断添加list数据,导致视图被挤在一旁。

 

还有一点需要注意就是FragmentManager 的使用,总的来说三种获取方式:getFragmentManager()、getSupportFragmentManager()和getChildFragmentManager(),Android V4扩展包获取的FragmentManager 使用getSupportFragmentManager(),android.app获取使用getFragmentManager(),像这种嵌套的使用getChildFragmentManager()。我就直接说使用,具体区别有兴趣的可以去搜下。

4. NewsFragment

显示新闻的fragment,简单期间,我就直接用RecyclerView显示标题实现了

public class NewsFragment extends BaseFragment {
    private List<NewsBean.ResultBean.DataBean> newsDatasValue = new ArrayList<>();//子fragment内获取的新闻内容
    private RecyclerView recyclerView;
    private NewsAdapter newsAdapter = new NewsAdapter();
    private int position;

    @Override
    protected int getLayoutId() {
        return R.layout.fragment_main_news;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        recyclerView = (RecyclerView) view.findViewById(R.id.fragment_main_news_recycler);
        recyclerView.setLayoutManager(new LinearLayoutManager(BaseApplication.getContext()));
        recyclerView.setAdapter(newsAdapter);
    }

    @Override
    protected void initData() {
        String url = "";
        switch (newsDatas.get(position)) {
            case "头条":
                url = "http://v.juhe.cn/toutiao/index?type=top&key=28aa4ac2a689c881a4e6e51cf918d695";
                break;
            case "社会":
                url = "http://v.juhe.cn/toutiao/index?type=shehui&key=28aa4ac2a689c881a4e6e51cf918d695";
                break;
            case "科技":
                url = "http://v.juhe.cn/toutiao/index?type=keji&key=28aa4ac2a689c881a4e6e51cf918d695";
                break;
            default:
                url = "http://v.juhe.cn/toutiao/index?type=top&key=28aa4ac2a689c881a4e6e51cf918d695";
                break;
        }
        OkHttpUtils
                .get()
                .url(url)
                .build()
                .execute(new StringCallback() {
                    @Override
                    public void onError(Call call, Exception e, int id) {

                    }

                    @Override
                    public void onResponse(String response, int id) {
                        NewsBean newsBean = new Gson().fromJson(response, NewsBean.class);
                        if (newsBean.getError_code() == 0) {
                            newsDatasValue = newsBean.getResult().getData();
                            newsAdapter.notifyDataSetChanged();
                        }
                    }
                });
    }

    class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new ViewHolder(LayoutInflater.from(BaseApplication.getContext()).inflate(R.layout.item_news, parent, false));
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.tv.setText(newsDatasValue.get(position).getTitle());
        }

        @Override
        public int getItemCount() {
            return newsDatasValue.size();
        }

        public class ViewHolder extends RecyclerView.ViewHolder {
            TextView tv;

            public ViewHolder(View itemView) {
                super(itemView);
                tv = (TextView) itemView.findViewById(R.id.item_news_tv);
            }
        }
    }

    //fragment所在页卡序号
    public void setPosition(int position){
        this.position = position;
    }
}

还是很简单的,主要就是要注意定义fragment页卡所在序号用来区分fragment。

三、总结

其实像ViewPager+Fragment+ViewPager+Fragment这种不像知识点,更像是思路,当有这个需求的时候,又没做过,可以参考一下这个思路,然后根据自身情况再修改,有想法的人应该会看着博客写,出来的东西却完全不一样,如果真的用到项目上,肯定要提取Adapter,甚至写出各种不同的Adapter基类,还要抽离网络框架,界面设计的更美,要考虑适配问题,可能会做很多性能优化,复用、压缩、缓存。。。。。。线程要注意线程池和信息传递这些,页面多了也会有很多问题,譬如上边说的fragment多出白色区域,还有newsPosition这样的传值问题,不过代码的乐趣就是这些吧,头脑风暴,陷入进去真的会上瘾,根本停不下来。

这是我CSDN有排名后的第一篇博客(150万+......),现在我确实是个菜鸡,但希望以后能快速的成长,也能写出更好的博客,同时也祝福看到这篇博客的码友们,工作顺利,跳槽的都能找到更好的平台,祝Android行业越来越好。不要如我现在,java后端、web前端都是核心技术人员,唯独移动开发像个打杂的,现在妹子都不联系了就想找个好平台【害羞......】

 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
ViewPager 和 FragmentAndroid 开发中常用的组件,ViewPager 可以实现左右滑动切换不同的页面,而 Fragment 可以实现模块化开发,将界面和逻辑分离,便于维护和扩展。 将两者结合起来可以实现多个 FragmentViewPager 中的左右滑动切换。具体实现步骤如下: 1. 创建多个 Fragment,每个 Fragment 对应一个页面。 2. 创建一个继承自 FragmentPagerAdapter 或 FragmentStatePagerAdapter 的适配器类,重写 getItem() 方法,返回对应位置的 Fragment。 3. 在布局文件中添加 ViewPager 组件,并设置适配器。 4. (可选)设置 ViewPager 的页面切换动画效果。 示例代码: 1. 创建 Fragment: ``` public class MyFragment extends Fragment { // ... } ``` 2. 创建适配器: ``` public class MyPagerAdapter extends FragmentPagerAdapter { private List<Fragment> fragmentList; public MyPagerAdapter(FragmentManager fm, List<Fragment> fragmentList) { super(fm); this.fragmentList = fragmentList; } @Override public Fragment getItem(int position) { return fragmentList.get(position); } @Override public int getCount() { return fragmentList.size(); } } ``` 3. 在布局文件中添加 ViewPager 组件: ``` <androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 4. 设置适配器: ``` List<Fragment> fragmentList = new ArrayList<>(); fragmentList.add(new MyFragment()); fragmentList.add(new MyFragment()); fragmentList.add(new MyFragment()); ViewPager viewPager = findViewById(R.id.view_pager); viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), fragmentList)); ``` 5. (可选)设置页面切换动画效果: ``` viewPager.setPageTransformer(true, new DepthPageTransformer()); ``` 以上是 ViewPager 和 Fragment 的基本用法,你还可以根据实际需求对它们进行更多的扩展和定制。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值