天天看點

【FastDev4Android架構開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)(一).前言:   (二).實作原理:   (三).具體實作: (四).最後總結

轉載請标明出處:

http://blog.csdn.net/developer_jiangqq/article/details/50145759

本文出自:【江清清的部落格】

(一).前言:   

         【好消息】個人網站已經上線運作,後面部落格以及技術幹貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org  

       仿36Kr用戶端開發過程中,因為他們網站上面的新聞文章分類比較多,是以我這邊還是打算模仿網易新聞APP的主界面新聞标簽Tab以及頁面滑動效果來進行實作。要實作的頂部的Tab标簽的效果有很多方法例如采用開源項目ViewPagerIndicator中的TabPageIndicator就可以實作,不過檢視了源碼發現該控件其實就是繼承自HorizontalScrollView自定義出來的。那既然這樣我這邊就準備帶着大家直接使用HorizontalScrollView來實作頂部tab标簽效果。底部的頁面滑動直接采用Fragment+ViewPager+FragmentStatePagerAdapter實作即可。後面我也會更新一篇直接使用ViewPagerIndicator開源控件實作tab标簽效果的文章,敬請期待~

          本例子具體代碼已經上傳到下面的項目中,歡迎各位去star和fork一下。

         FastDev4Android架構項目位址:https://github.com/jiangqqlmj/FastDev4Android

(二).實作原理:   

【FastDev4Android架構開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)(一).前言:   (二).實作原理:   (三).具體實作: (四).最後總結

         上面我這邊直接貼了36Kr官方APP的主界面頂部Tab标簽,我這邊直接模仿這個做。首先看上面截圖紅色框起來的部分,這邊的Tab是可以橫向滑動的那可以采用HorizontalScrollView控件實作,并且裡邊的每一項Tab Item都是可以進行添加和點選。我們直接往HorizontalScrollView addView子控件即可。Tab下面是若幹個新聞文章清單的頁面且可以進行左右滑動可以采用ViewPager實作,每個頁面采用Fragment實作。

【FastDev4Android架構開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)(一).前言:   (二).實作原理:   (三).具體實作: (四).最後總結

上圖是具體控件的分布和嵌套,下面我們來看一下具體實作:

(三).具體實作: 

            3.1.首先我們需要有一個繼承自FragmentActivity的MainInfoActivity,該用來承載Fragment,該Activity中實作基本沒有啥代碼,就不貼詳細到時候去FastDev4Android項目中下載下傳即可,這邊我們看一下Activity的布局檔案:

<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"android:layout_width="match_parent"
   android:layout_height="match_parent"
    >
    <includelayout="@layout/common_top_bar_layout"/>
    <fragment
       android:id="@+id/info_fragment"
       class="com.chinaztt.fda.fragment.InfoFragment"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       tools:layout="@layout/info_fragment_layout"
        />
</LinearLayout>
           

該布局檔案中直接把承載的InfoFragment寫在裡邊了,當我們Activity加載的時候該Fragment也被加載了。

      3.2.接下來就是InfoFragment了,讓我們首先看下我這邊定義的布局檔案:

<?xmlversion="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/white">
    <!--橫向滑動的容器-->
    <HorizontalScrollView
       android:id="@+id/horizontal_info"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
        android:scrollbars="none">
        <!--裝入每一個Tab項容器-->
        <LinearLayout
           android:id="@+id/linearlayout_container"
           android:layout_width="fill_parent"
           android:layout_height="49dp"
           android:orientation="horizontal">
        </LinearLayout>
    </HorizontalScrollView>
    <android.support.v4.view.ViewPager
       android:id="@+id/info_viewpager"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"/>
</LinearLayout>
           

           該布局中主要分為兩部分,第一部分就是HorizontalScrollView控件該用來實作橫向滑動,為标簽Tab的容器,我們可以往裡邊動态的添加Tab Item。第二部分為ViewPager控件該用來實作頁面的左右滑動,其中每一項Item為Fragment。以上關鍵控件已經定義好了,下面就是需要在InfoFragment中實作Tab效果了。

首先定義和初始化控件:

/**
     * 目前選擇的分類
     */
    private int mCurClassIndex=0;
    /**
     * 選擇的分類字型顔色
     */
    private int mColorSelected;
    /**
     * 非選擇的分類字型顔色
     */
    private int mColorUnSelected;
    /**
     * 水準滾動的Tab容器
     */
    private HorizontalScrollView mScrollBar;
    /**
     * 分類導航的容器
     */
    private ViewGroup mClassContainer;
    /**
     * 水準滾動X
     */
    private int mScrollX=0;
 mScrollBar=(HorizontalScrollView)mView.findViewById(R.id.horizontal_info);
 mClassContainer=(ViewGroup)mView.findViewById(R.id.linearlayout_container);
           

 對于Tab Item的動态添加使用下面寫得方法addScrollView():

/**
     * 動态添加頂部Tab滑動的标簽
     * @param titles
     */
    private void addScrollView(String[]titles){
        LayoutInflater mLayoutInflater=LayoutInflater.from(FDApplication.getInstance());
        final int count=titles.length;
        for(int i=0;i<count;i++){
            final String title=titles[i];
            final Viewview=mLayoutInflater.inflate(R.layout.horizontal_item_layout,null);
            final LinearLayout linearLayout=(LinearLayout)view.findViewById(R.id.horizontal_linearlayout_type);
            final ImageView img_type=(ImageView)view.findViewById(R.id.horizontal_img_type);
            final TextView type_name=(TextView)view.findViewById(R.id.horizontal_tv_type);
            type_name.setText(title);
            if(i==mCurClassIndex){
                //已經選中
               type_name.setTextColor(mColorSelected);
               img_type.setImageResource(R.drawable.bottom_line_blue);
            }else {
                //未選中
               type_name.setTextColor(mColorUnSelected);
               img_type.setImageResource(R.drawable.bottom_line_gray);
            }
            final int index=i;
            //點選頂部Tab标簽,動态設定下面的ViewPager頁面
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //首先設定目前的Item為正常狀态
                    View currentItem=mClassContainer.getChildAt(mCurClassIndex);
                   ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
                   ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
                    mCurClassIndex=index;
                    //設定點選狀态
                   img_type.setImageResource(R.drawable.bottom_line_blue);
                   type_name.setTextColor(mColorSelected);
                    //跳轉到指定的ViewPager
                   info_viewpager.setCurrentItem(mCurClassIndex);
                }
            });
            mClassContainer.addView(view);
        }
    }
           

    該方法傳入了标簽的數組,根據标簽的數量進行周遊動态添加,主要步驟如下 :

  • 加載每一項Tab Item的布局,并且擷取Item中的相關控件并且設定資料和資源檔案
  • 判斷目前是否選中項,對于選中和未選中設定不同的字型顔色和資源檔案
  • 給每一項Item添加點選事件,用來切換ViewPager跳轉到具體每一項頁面(Fragment)
  • 最終每一項Tab Item加入到容器中

         上面我們有講到,使用Fragment+ViewPager實作頁面滑動切換,那我們需要一個頁面的自定義擴充卡了,我這邊建立了CNKFixedPagerAdapter該類繼承自FragmengStatePagerAdaper具體實作代碼如下,比較簡單就不詳細講解了:

public class CNKFixedPagerAdapter extends FragmentStatePagerAdapter {
    private String[] titles;
    public void setTitles(String[] titles) {
        this.titles = titles;
    }
    private List<Fragment> fragments;
    public CNKFixedPagerAdapter(FragmentManager fm) {
        super(fm);
    }
 
    @Override
    public Fragment getItem(int position) {
        return this.fragments.get(position);
    }
    @Override
    public int getCount() {
        return this.fragments.size();
    }
 
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment=null;
        try {
           fragment=(Fragment)super.instantiateItem(container,position);
        }catch (Exception e){
 
        }
        return fragment;
    }
 
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
 
    }
 
    public List<Fragment> getFragments(){
        return fragments;
    }
    public void setFragments(List<Fragment> fragments) {
        this.fragments = fragments;
    }
}
           

        然後我們執行個體化 ViewPager 以及自定義擴充卡和顯示的 Fragment 資料綁定即可 :

fragments=new ArrayList<>();
        for(int i=0;i<12;i++){
            OneFragment oneFragment=new OneFragment();
            Bundle bundle=new Bundle();
           bundle.putString("extra",titles[i]);
            oneFragment.setArguments(bundle);
            fragments.add(oneFragment);
        }
 
        mPagerAdater=new CNKFixedPagerAdapter(getChildFragmentManager());
        mPagerAdater.setTitles(titles);
        mPagerAdater.setFragments(fragments);
       info_viewpager.setAdapter(mPagerAdater);  
           

       最後我們不要忘記有一點是:當我們的ViewPager頁面切換的時候我們需要實習改變頂部Tab Item的選中情況以及字型顔色等。是以我們需要給ViewPager添加頁面切換監聽器OnPageChangeListener,然後在回調的onPageSelected()方法中重新設定一下Tab Item的效果。

info_viewpager.setOnPageChangeListener(this);
           
//下面三個回調方法 分别是在ViewPager進行滑動的時候調用
    @Override
    public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels) {
 
    }
    @Override
    public void onPageSelected(int position) {
        //首先設定目前的Item為正常狀态
        View preView=mClassContainer.getChildAt(mCurClassIndex);
       ((TextView)(preView.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
       ((ImageView)(preView.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
        mCurClassIndex=position;
        //設定目前為選中狀态
        View currentItem=mClassContainer.getChildAt(mCurClassIndex);
       ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_blue);
       ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorSelected);
        //這邊移動的距離 是經過計算粗略得出來的
        mScrollX=currentItem.getLeft()-300;
        Log.d("zttjiangqq","mScrollX:" + mScrollX);
        mScrollBar.post(new Runnable() {
            @Override
            public void run() {
               mScrollBar.scrollTo(mScrollX,0);
            }
        });
    }
    @Override
    public void onPageScrollStateChanged(int state) {
    }
           

   上面onPageSelected()方法中我們首先設定原先的Item的顔色為正常未選中狀态,然後設定目前的位置選中以及字型顔色改變,最後讓HorizontalScrollView平移到合适的位置即可:

   3.3.以上我們的核心代碼已經講解完成了,下面我們看一下運作效果:

【FastDev4Android架構開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)(一).前言:   (二).實作原理:   (三).具體實作: (四).最後總結

  3.4.為了大家友善閱讀代碼,我這邊把InfoFragment的全部代碼貼出來:

public class InfoFragment extends Fragment implements ViewPager.OnPageChangeListener{
 
    private View mView;
    ViewPager info_viewpager;
    private List<Fragment> fragments;
    private CNKFixedPagerAdapter mPagerAdater;
    private String[] titles=new String[]{"全部","氪TV","O2O","新硬體","Fun!!","企業服務","Fit&Health","線上教育","網際網路金融","大公司","專欄","新産品"};
    /**
     * 目前選擇的分類
     */
    private int mCurClassIndex=0;
    /**
     * 選擇的分類字型顔色
     */
    private int mColorSelected;
    /**
     * 非選擇的分類字型顔色
     */
    private int mColorUnSelected;
    /**
     * 水準滾動的Tab容器
     */
    private HorizontalScrollView mScrollBar;
    /**
     * 分類導航的容器
     */
    private ViewGroup mClassContainer;
    /**
     * 水準滾動X
     */
    private int mScrollX=0;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if(mView==null){
           mView=inflater.inflate(R.layout.info_fragment_layout,container,false);
            initViews();
            initValidata();
 
        }
        return mView;
    }
    /**
     * 初始化布局控件
     */
    private void initViews(){
       info_viewpager=(ViewPager)mView.findViewById(R.id.info_viewpager);
       mScrollBar=(HorizontalScrollView)mView.findViewById(R.id.horizontal_info);
       mClassContainer=(ViewGroup)mView.findViewById(R.id.linearlayout_container);
 
    }
    private void initValidata(){
        mColorSelected=FDApplication.getInstance().getResources().getColor(R.color.color_selected);
       mColorUnSelected=FDApplication.getInstance().getResources().getColor(R.color.color_unselected);
        //添加Tab标簽
        addScrollView(titles);
        mScrollBar.post(new Runnable() {
            @Override
            public void run() {
                mScrollBar.scrollTo(mScrollX,0);
            }
        });
        fragments=new ArrayList<>();
        for(int i=0;i<12;i++){
            OneFragment oneFragment=new OneFragment();
            Bundle bundle=new Bundle();
           bundle.putString("extra",titles[i]);
            oneFragment.setArguments(bundle);
            fragments.add(oneFragment);
        }
 
        mPagerAdater=new CNKFixedPagerAdapter(getChildFragmentManager());
        mPagerAdater.setTitles(titles);
        mPagerAdater.setFragments(fragments);
       info_viewpager.setAdapter(mPagerAdater);
       info_viewpager.setOnPageChangeListener(this);
    }
    /**
     * 動态添加頂部Tab滑動的标簽
     * @param titles
     */
    private void addScrollView(String[]titles){
        LayoutInflater mLayoutInflater=LayoutInflater.from(FDApplication.getInstance());
        final int count=titles.length;
        for(int i=0;i<count;i++){
            final String title=titles[i];
            final View view=mLayoutInflater.inflate(R.layout.horizontal_item_layout,null);
            final LinearLayout linearLayout=(LinearLayout)view.findViewById(R.id.horizontal_linearlayout_type);
            final ImageView img_type=(ImageView)view.findViewById(R.id.horizontal_img_type);
            final TextView type_name=(TextView)view.findViewById(R.id.horizontal_tv_type);
            type_name.setText(title);
            if(i==mCurClassIndex){
                //已經選中
               type_name.setTextColor(mColorSelected);
               img_type.setImageResource(R.drawable.bottom_line_blue);
            }else {
                //未選中
               type_name.setTextColor(mColorUnSelected);
               img_type.setImageResource(R.drawable.bottom_line_gray);
            }
            final int index=i;
            //點選頂部Tab标簽,動态設定下面的ViewPager頁面
            view.setOnClickListener(newView.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //首先設定目前的Item為正常狀态
                    View currentItem=mClassContainer.getChildAt(mCurClassIndex);
                   ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
                   ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
                    mCurClassIndex=index;
                    //設定點選狀态
                   img_type.setImageResource(R.drawable.bottom_line_blue);
                   type_name.setTextColor(mColorSelected);
                    //跳轉到指定的ViewPager
                   info_viewpager.setCurrentItem(mCurClassIndex);
                }
            });
            mClassContainer.addView(view);
        }
    }
    //下面三個回調方法 分别是在ViewPager進行滑動的時候調用
    @Override
    public void onPageScrolled(int position,float positionOffset, int positionOffsetPixels) {
 
    }
 
    @Override
    public void onPageSelected(int position) {
        //首先設定目前的Item為正常狀态
        View preView=mClassContainer.getChildAt(mCurClassIndex);
       ((TextView)(preView.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorUnSelected);
       ((ImageView)(preView.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_gray);
        mCurClassIndex=position;
        //設定目前為選中狀态
        View currentItem=mClassContainer.getChildAt(mCurClassIndex);
       ((ImageView)(currentItem.findViewById(R.id.horizontal_img_type))).setImageResource(R.drawable.bottom_line_blue);
       ((TextView)(currentItem.findViewById(R.id.horizontal_tv_type))).setTextColor(mColorSelected);
        //這邊移動的距離 是經過計算粗略得出來的
        mScrollX=currentItem.getLeft()-300;
        Log.d("zttjiangqq","mScrollX:" + mScrollX);
        mScrollBar.post(new Runnable() {
            @Override
            public void run() {
               mScrollBar.scrollTo(mScrollX,0);
            }
        });
    }
 
    @Override
    public void onPageScrollStateChanged(int state) {
 
    }
}
           

(四).最後總結

           今天我們通過Fragment+ViewPager+FragmentStatePagerAdapter+HorizontalScrollView實作了仿照網易新聞用戶端(或者36Kr)首頁的頁面滑動和頂部Tab效果。

           本次執行個體代碼因為比較多,代碼全貼比較浪費篇幅,重點在于講解思路了。不過執行個體注釋過的全部代碼已經上傳到Github項目中了。同時歡迎大家去Github站點進行clone或者fork浏覽整個開源快速開發架構項目~

https://github.com/jiangqqlmj/FastDev4Android

尊重原創,轉載請注明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵權必究!

關注我的訂閱号,每天分享移動開發技術(Android/IOS),項目管理以及部落格文章!

【FastDev4Android架構開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)(一).前言:   (二).實作原理:   (三).具體實作: (四).最後總結

關注我的微網誌,可以獲得更多精彩内容

【FastDev4Android架構開發】HorizontalScrollView,Fragment,FragmentStatePagerAdapter打造網易新聞Tab及滑動頁面效果(三十六)(一).前言:   (二).實作原理:   (三).具體實作: (四).最後總結

繼續閱讀