HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造网易新闻Tab及滑动页面效果(三十六)

      仿36Kr客户端开发过程中,因为他们网站上面的新闻文章分类比较多,所以我这边还是打算模仿网易新闻APP的主界面新闻标签Tab以及页面滑动效果来进行实现。要实现的顶部的Tab标签的效果有很多方法例如采用开源项目ViewPagerIndicator中的TabPageIndicator就可以实现,不过查看了源码发现该控件其实就是继承自HorizontalScrollView自定义出来的。那既然这样我这边就准备带着大家直接使用HorizontalScrollView来实现顶部tab标签效果。底部的页面滑动直接采用Fragment+ViewPager+FragmentStatePagerAdapter实现即可。后面我也会更新一篇直接使用ViewPagerIndicator开源控件实现tab标签效果的文章,敬请期待~

          本例子具体代码已经上传到下面的项目中,欢迎各位去star和fork一下。

         FastDev4Android框架项目地址:https://github.com/jiangqqlmj/FastDev4Android

().实现原理:   

HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造网易新闻Tab及滑动页面效果(三十六)_第1张图片

         上面我这边直接贴了36Kr官方APP的主界面顶部Tab标签,我这边直接模仿这个做。首先看上面截图红色框起来的部分,这边的Tab是可以横向滑动的那可以采用HorizontalScrollView控件实现,并且里边的每一项Tab Item都是可以进行添加和点击。我们直接往HorizontalScrollViewaddView子控件即可。Tab下面是若干个新闻文章列表的页面且可以进行左右滑动可以采用ViewPager实现,每个页面采用Fragment实现。

HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造网易新闻Tab及滑动页面效果(三十六)_第2张图片

上图是具体控件的分布和嵌套,下面我们来看一下具体实现:

().具体实现

            3.1.首先我们需要有一个继承自FragmentActivityMainInfoActivity,该用来承载Fragment,该Activity中实现基本没有啥代码,就不贴详细到时候去FastDev4Android项目中下载即可,这边我们看一下Activity的布局文件:

[html]  view plain  copy
 
  1. <?xmlversionxmlversion="1.0" encoding="utf-8"?>  
  2. <LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
  3.    xmlns:tools="http://schemas.android.com/tools"  
  4.     android:orientation="vertical"android:layout_width="match_parent"  
  5.    android:layout_height="match_parent"  
  6.     >  
  7.     <includelayoutincludelayout="@layout/common_top_bar_layout"/>  
  8.     <fragment  
  9.        android:id="@+id/info_fragment"  
  10.        class="com.chinaztt.fda.fragment.InfoFragment"  
  11.        android:layout_width="fill_parent"  
  12.        android:layout_height="fill_parent"  
  13.        tools:layout="@layout/info_fragment_layout"  
  14.         />  
  15. </LinearLayout>  

该布局文件中直接把承载的InfoFragment写在里边了,当我们Activity加载的时候该Fragment也被加载了。

      3.2.接下来就是InfoFragment了,让我们首先看下我这边定义的布局文件:

[html]  view plain  copy
 
  1. <?xmlversionxmlversion="1.0" encoding="utf-8"?>  
  2. <LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"android:layout_width="match_parent"  
  4.    android:layout_height="match_parent"  
  5.    android:background="@color/white">  
  6.     <!--横向滑动的容器-->  
  7.     <HorizontalScrollView  
  8.        android:id="@+id/horizontal_info"  
  9.        android:layout_width="fill_parent"  
  10.        android:layout_height="wrap_content"  
  11.         android:scrollbars="none">  
  12.         <!--装入每一个Tab项容器-->  
  13.         <LinearLayout  
  14.            android:id="@+id/linearlayout_container"  
  15.            android:layout_width="fill_parent"  
  16.            android:layout_height="49dp"  
  17.            android:orientation="horizontal">  
  18.         </LinearLayout>  
  19.     </HorizontalScrollView>  
  20.     <android.support.v4.view.ViewPager  
  21.        android:id="@+id/info_viewpager"  
  22.        android:layout_width="fill_parent"  
  23.        android:layout_height="fill_parent"/>  
  24. </LinearLayout>  

           该布局中主要分为两部分,第一部分就是HorizontalScrollView控件该用来实现横向滑动,为标签Tab的容器,我们可以往里边动态的添加Tab Item。第二部分为ViewPager控件该用来实现页面的左右滑动,其中每一项Item为Fragment。以上关键控件已经定义好了,下面就是需要在InfoFragment中实现Tab效果了。

首先定义和初始化控件:

[java]  view plain  copy
 
  1. /** 
  2.      * 当前选择的分类 
  3.      */  
  4.     private int mCurClassIndex=0;  
  5.     /** 
  6.      * 选择的分类字体颜色 
  7.      */  
  8.     private int mColorSelected;  
  9.     /** 
  10.      * 非选择的分类字体颜色 
  11.      */  
  12.     private int mColorUnSelected;  
  13.     /** 
  14.      * 水平滚动的Tab容器 
  15.      */  
  16.     private HorizontalScrollView mScrollBar;  
  17.     /** 
  18.      * 分类导航的容器 
  19.      */  
  20.     private ViewGroup mClassContainer;  
  21.     /** 
  22.      * 水平滚动X 
  23.      */  
  24.     private int mScrollX=0;  
  25.  mScrollBar=(HorizontalScrollView)mView.findViewById(R.id.horizontal_info);  
  26.  mClassContainer=(ViewGroup)mView.findViewById(R.id.linearlayout_container);  

 对于Tab Item的动态添加使用下面写得方法addScrollView()

[java]  view plain  copy
 
  1. /** 
  2.     * 动态添加顶部Tab滑动的标签 
  3.     * @param titles 
  4.     */  
  5.    private void addScrollView(String[]titles){  
  6.        LayoutInflater mLayoutInflater=LayoutInflater.from(FDApplication.getInstance());  
  7.        final int count=titles.length;  
  8.        for(int i=0;i<count;i++){  
  9.            final String title=titles[i];  
  10.            final Viewview=mLayoutInflater.inflate(R.layout.horizontal_item_layout,null);  
  11.            final LinearLayout linearLayout=(LinearLayout)view.findViewById(R.id.horizontal_linearlayout_type);  
  12.            final ImageView img_type=(ImageView)view.findViewById(R.id.horizontal_img_type);  
  13.            final TextView type_name=(TextView)view.findViewById(R.id.horizontal_tv_type);  
  14.            type_name.setText(title);  
  15.            if(i==mCurClassIndex){  
  16.                //已经选中  
  17.               type_name.setTextColor(mColorSelected);  
  18.               img_type.setImageResource(R.drawable.bottom_line_blue);  
  19.            }else {  
  20.                //未选中  
  21.               type_name.setTextColor(mColorUnSelected);  
  22.               img_type.setImageResource(R.drawable.bottom_line_gray);  
  23.            }  
  24.            final int index=i;  
  25.            //点击顶部Tab标签,动态设置下面的ViewPager页面  
  26.            view.setOnClickListener(new View.OnClickListener() {  
  27.                @Override  
  28.                public void onClick(View v) {  
  29.                    //首先设置当前的Item为正常状态  
  30.                    View currentItem=mClassContainer.getChildAt(mCurClassIndex);  
  31.                   ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);  
  32.                   ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);  
  33.                    mCurClassIndex=index;  
  34.                    //设置点击状态  
  35.                   img_type.setImageResource(R.drawable.bottom_line_blue);  
  36.                   type_name.setTextColor(mColorSelected);  
  37.                    //跳转到指定的ViewPager  
  38.                   info_viewpager.setCurrentItem(mCurClassIndex);  
  39.                }  
  40.            });  
  41.            mClassContainer.addView(view);  
  42.        }  
  43.    }  
    该方法传入了标签的数组,根据标签的数量进行遍历动态添加,主要步骤如下 :

  • 加载每一项Tab Item的布局,并且获取Item中的相关控件并且设置数据和资源文件
  • 判断当前是否选中项,对于选中和未选中设置不同的字体颜色和资源文件
  • 给每一项Item添加点击事件,用来切换ViewPager跳转到具体每一项页面(Fragment)
  • 最终每一项Tab Item加入到容器中

 

         上面我们有讲到,使用Fragment+ViewPager实现页面滑动切换,那我们需要一个页面的自定义适配器了,我这边创建了CNKFixedPagerAdapter该类继承自FragmengStatePagerAdaper具体实现代码如下,比较简单就不详细讲解了:

[java]  view plain  copy
 
  1. public class CNKFixedPagerAdapter extends FragmentStatePagerAdapter {  
  2.     private String[] titles;  
  3.     public void setTitles(String[] titles) {  
  4.         this.titles = titles;  
  5.     }  
  6.     private List<Fragment> fragments;  
  7.     public CNKFixedPagerAdapter(FragmentManager fm) {  
  8.         super(fm);  
  9.     }  
  10.    
  11.     @Override  
  12.     public Fragment getItem(int position) {  
  13.         return this.fragments.get(position);  
  14.     }  
  15.     @Override  
  16.     public int getCount() {  
  17.         return this.fragments.size();  
  18.     }  
  19.    
  20.     @Override  
  21.     public Object instantiateItem(ViewGroup container, int position) {  
  22.         Fragment fragment=null;  
  23.         try {  
  24.            fragment=(Fragment)super.instantiateItem(container,position);  
  25.         }catch (Exception e){  
  26.    
  27.         }  
  28.         return fragment;  
  29.     }  
  30.    
  31.     @Override  
  32.     public void destroyItem(ViewGroup container, int position, Object object) {  
  33.    
  34.     }  
  35.    
  36.     public List<Fragment> getFragments(){  
  37.         return fragments;  
  38.     }  
  39.     public void setFragments(List<Fragment> fragments) {  
  40.         this.fragments = fragments;  
  41.     }  
  42. }  
        然后我们实例化 ViewPager 以及自定义适配器和显示的 Fragment 数据绑定即可 :

[java]  view plain  copy
 
  1. fragments=new ArrayList<>();  
  2.         for(int i=0;i<12;i++){  
  3.             OneFragment oneFragment=new OneFragment();  
  4.             Bundle bundle=new Bundle();  
  5.            bundle.putString("extra",titles[i]);  
  6.             oneFragment.setArguments(bundle);  
  7.             fragments.add(oneFragment);  
  8.         }  
  9.    
  10.         mPagerAdater=new CNKFixedPagerAdapter(getChildFragmentManager());  
  11.         mPagerAdater.setTitles(titles);  
  12.         mPagerAdater.setFragments(fragments);  
  13.        info_viewpager.setAdapter(mPagerAdater);    

       最后我们不要忘记有一点是:当我们的ViewPager页面切换的时候我们需要实习改变顶部Tab Item的选中情况以及字体颜色等。所以我们需要给ViewPager添加页面切换监听器OnPageChangeListener,然后在回调的onPageSelected()方法中重新设置一下Tab Item的效果。

[java]  view plain  copy
 
  1. info_viewpager.setOnPageChangeListener(this);  

[java]  view plain  copy
 
  1. //下面三个回调方法 分别是在ViewPager进行滑动的时候调用  
  2.     @Override  
  3.     public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels) {  
  4.    
  5.     }  
  6.     @Override  
  7.     public void onPageSelected(int position) {  
  8.         //首先设置当前的Item为正常状态  
  9.         View preView=mClassContainer.getChildAt(mCurClassIndex);  
  10.        ((TextView)(preView.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);  
  11.        ((ImageView)(preView.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);  
  12.         mCurClassIndex=position;  
  13.         //设置当前为选中状态  
  14.         View currentItem=mClassContainer.getChildAt(mCurClassIndex);  
  15.        ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_blue);  
  16.        ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorSelected);  
  17.         //这边移动的距离 是经过计算粗略得出来的  
  18.         mScrollX=currentItem.getLeft()-300;  
  19.         Log.d("zttjiangqq","mScrollX:" + mScrollX);  
  20.         mScrollBar.post(new Runnable() {  
  21.             @Override  
  22.             public void run() {  
  23.                mScrollBar.scrollTo(mScrollX,0);  
  24.             }  
  25.         });  
  26.     }  
  27.     @Override  
  28.     public void onPageScrollStateChanged(int state) {  
  29.     }  

   上面onPageSelected()方法中我们首先设置原先的Item的颜色为正常未选中状态,然后设置当前的位置选中以及字体颜色改变,最后让HorizontalScrollView平移到合适的位置即可:

   3.3.以上我们的核心代码已经讲解完成了,下面我们看一下运行效果:

HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造网易新闻Tab及滑动页面效果(三十六)_第3张图片

  3.4.为了大家方便阅读代码,我这边把InfoFragment的全部代码贴出来:

[java]  view plain  copy
 
  1. public class InfoFragment extends Fragment implements ViewPager.OnPageChangeListener{  
  2.    
  3.     private View mView;  
  4.     ViewPager info_viewpager;  
  5.     private List<Fragment> fragments;  
  6.     private CNKFixedPagerAdapter mPagerAdater;  
  7.     private String[] titles=new String[]{"全部","氪TV","O2O","新硬件","Fun!!","企业服务","Fit&Health","在线教育","互联网金融","大公司","专栏","新产品"};  
  8.     /** 
  9.      * 当前选择的分类 
  10.      */  
  11.     private int mCurClassIndex=0;  
  12.     /** 
  13.      * 选择的分类字体颜色 
  14.      */  
  15.     private int mColorSelected;  
  16.     /** 
  17.      * 非选择的分类字体颜色 
  18.      */  
  19.     private int mColorUnSelected;  
  20.     /** 
  21.      * 水平滚动的Tab容器 
  22.      */  
  23.     private HorizontalScrollView mScrollBar;  
  24.     /** 
  25.      * 分类导航的容器 
  26.      */  
  27.     private ViewGroup mClassContainer;  
  28.     /** 
  29.      * 水平滚动X 
  30.      */  
  31.     private int mScrollX=0;  
  32.     @Override  
  33.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
  34.         if(mView==null){  
  35.            mView=inflater.inflate(R.layout.info_fragment_layout,container,false);  
  36.             initViews();  
  37.             initValidata();  
  38.    
  39.         }  
  40.         return mView;  
  41.     }  
  42.     /** 
  43.      * 初始化布局控件 
  44.      */  
  45.     private void initViews(){  
  46.        info_viewpager=(ViewPager)mView.findViewById(R.id.info_viewpager);  
  47.        mScrollBar=(HorizontalScrollView)mView.findViewById(R.id.horizontal_info);  
  48.        mClassContainer=(ViewGroup)mView.findViewById(R.id.linearlayout_container);  
  49.    
  50.     }  
  51.     private void initValidata(){  
  52.         mColorSelected=FDApplication.getInstance().getResources().getColor(R.color.color_selected);  
  53.        mColorUnSelected=FDApplication.getInstance().getResources().getColor(R.color.color_unselected);  
  54.         //添加Tab标签  
  55.         addScrollView(titles);  
  56.         mScrollBar.post(new Runnable() {  
  57.             @Override  
  58.             public void run() {  
  59.                 mScrollBar.scrollTo(mScrollX,0);  
  60.             }  
  61.         });  
  62.         fragments=new ArrayList<>();  
  63.         for(int i=0;i<12;i++){  
  64.             OneFragment oneFragment=new OneFragment();  
  65.             Bundle bundle=new Bundle();  
  66.            bundle.putString("extra",titles[i]);  
  67.             oneFragment.setArguments(bundle);  
  68.             fragments.add(oneFragment);  
  69.         }  
  70.    
  71.         mPagerAdater=new CNKFixedPagerAdapter(getChildFragmentManager());  
  72.         mPagerAdater.setTitles(titles);  
  73.         mPagerAdater.setFragments(fragments);  
  74.        info_viewpager.setAdapter(mPagerAdater);  
  75.        info_viewpager.setOnPageChangeListener(this);  
  76.     }  
  77.     /** 
  78.      * 动态添加顶部Tab滑动的标签 
  79.      * @param titles 
  80.      */  
  81.     private void addScrollView(String[]titles){  
  82.         LayoutInflater mLayoutInflater=LayoutInflater.from(FDApplication.getInstance());  
  83.         final int count=titles.length;  
  84.         for(int i=0;i<count;i++){  
  85.             final String title=titles[i];  
  86.             final View view=mLayoutInflater.inflate(R.layout.horizontal_item_layout,null);  
  87.             final LinearLayout linearLayout=(LinearLayout)view.findViewById(R.id.horizontal_linearlayout_type);  
  88.             final ImageView img_type=(ImageView)view.findViewById(R.id.horizontal_img_type);  
  89.             final TextView type_name=(TextView)view.findViewById(R.id.horizontal_tv_type);  
  90.             type_name.setText(title);  
  91.             if(i==mCurClassIndex){  
  92.                 //已经选中  
  93.                type_name.setTextColor(mColorSelected);  
  94.                img_type.setImageResource(R.drawable.bottom_line_blue);  
  95.             }else {  
  96.                 //未选中  
  97.                type_name.setTextColor(mColorUnSelected);  
  98.                img_type.setImageResource(R.drawable.bottom_line_gray);  
  99.             }  
  100.             final int index=i;  
  101.             //点击顶部Tab标签,动态设置下面的ViewPager页面  
  102.             view.setOnClickListener(newView.OnClickListener() {  
  103.                 @Override  
  104.                 public void onClick(View v) {  
  105.                     //首先设置当前的Item为正常状态  
  106.                     View currentItem=mClassContainer.getChildAt(mCurClassIndex);  
  107.                    ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);  
  108.                    ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);  
  109.                     mCurClassIndex=index;  
  110.                     //设置点击状态  
  111.                    img_type.setImageResource(R.drawable.bottom_line_blue);  
  112.                    type_name.setTextColor(mColorSelected);  
  113.                     //跳转到指定的ViewPager  
  114.                    info_viewpager.setCurrentItem(mCurClassIndex);  
  115.                 }  
  116.             });  
  117.             mClassContainer.addView(view);  
  118.         }  
  119.     }  
  120.     //下面三个回调方法 分别是在ViewPager进行滑动的时候调用  
  121.     @Override  
  122.     public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels) {  
  123.    
  124.     }  
  125.    
  126.     @Override  
  127.     public void onPageSelected(int position) {  
  128.         //首先设置当前的Item为正常状态  
  129.         View preView=mClassContainer.getChildAt(mCurClassIndex);  
  130.        ((TextView)(preView.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);  
  131.        ((ImageView)(preView.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);  
  132.         mCurClassIndex=position;  
  133.         //设置当前为选中状态  
  134.         View currentItem=mClassContainer.getChildAt(mCurClassIndex);  
  135.        ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_blue);  
  136.        ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorSelected);  
  137.         //这边移动的距离 是经过计算粗略得出来的  
  138.         mScrollX=currentItem.getLeft()-300;  
  139.         Log.d("zttjiangqq","mScrollX:" + mScrollX);  
  140.         mScrollBar.post(new Runnable() {  
  141.             @Override  
  142.             public void run() {  
  143.                mScrollBar.scrollTo(mScrollX,0);  
  144.             }  
  145.         });  
  146.     }  
  147.    
  148.     @Override  
  149.     public void onPageScrollStateChanged(int state) {  
  150.    
  151.     }  
  152. }  

().最后总结

           今天我们通过Fragment+ViewPager+FragmentStatePagerAdapter+HorizontalScrollView实现了仿照网易新闻客户端(或者36Kr)首页的页面滑动和顶部Tab效果。

           本次实例代码因为比较多,代码全贴比较浪费篇幅,重点在于讲解思路了。不过实例注释过的全部代码已经上传到Github项目中了。同时欢迎大家去Github站点进行clone或者fork浏览整个开源快速开发框架项目~

https://github.com/jiangqqlmj/FastDev4Android

你可能感兴趣的:(viewpager)