Android 教你打造炫酷的ViewPagerIndicator 不僅僅是高仿MIUI


目錄(?)[+]

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/42160391 ,本文出自:【張鴻洋的博客】


我參加了博客之星評選,如果你喜歡我的博客,求投票~~http://vote.blog.csdn.net/blogstar2014/selection?username=lmj623565791#content

1、概述

哈,今天給大家帶來一個ViewPagerIndicator的製作,相信大家在做tabIndicator的時候,大多數人都用過TabPageIndicator,並且很多知名APP都使用過這個開源的指示器。大家有沒有想過如何自己去實現這樣的一個指示器,並且代碼會有多複雜呢~~~今天,我就帶領大家來從無到有的實現這樣一個指示器,當然了,不準備一模一樣,搞得沒有創新似的,再看標題,跟MIUI相關,所以我們準備做一個特性與TabPageIndicator一致的,但是樣子和MIUI的Tab一樣的~~

首先仿MIUI比較簡單,大家看看效果圖:



但是呢,MIUI中所有的Tab的數量基本維持在兩個到四個,但是呢,我們可能欄目比較多,假設我們10來個Tab咋辦,總不能把屏幕均分10份吧,這我5.3的視力我也不能接受~~所以我們需要做類似TabPageIndicator特性,顯示出幾個,然後剩下的,可以Tab跟隨ViewPager滑動時聯動,效果圖是這樣的:

擦,多看一會,是聯動的,上面的Tab也是支持點擊的~

其實學完這個,如果你衷與下面是下劃線的,簡單修改幾行代碼就o了~不管你信或不信,反正我是信了。

ok,實現前感謝下這位哥們,博客地址:http://blog.csdn.net/qibin0506/article/details/42046559  ,沒看到這篇博客,也不會有此篇了(目測博主羣裏的)。

2、實現前的分析

對於這樣的指示器,我們首先分析下如何製作。

內容區域我們基本不用考慮,ViewPager+FragmentPagerAdapter即可。

主要是頂部的Tab區域:

首先雖然是自定義控件,但是我們只需要採用組合的方式就行:

控件的選擇:外層佈局我準備使用LinearLayout,設置個方向水平就行了,內部的標題呢,默認的話,我決定使用TextView。

自定義屬性:因爲我們可視的Tab屬性應該是可以由用戶來定製的,所以我們對外公佈一個自定義的屬性,由用戶來設置,每個TextView的width即ScreenWidth/mVisibleTab。

三角形的TabIndicator繪製:不管是繪製三角形還是下劃線的指示器,我們肯定是在Tab的外層佈局中進行繪製,那我們初始化時繪製一個三角形,最終中dispatchDraw中,根 據三角形的位置,直接繪製。

三角形指示器的位置:位置的y座標比較容易計算,這裏不贅述。主要是x座標,因爲x座標跟隨ViewPager移動,那麼我們如何獲得移動的距離呢?有個PageChangeListener 裏面有個onPageScrolled方法,這個方法回調positionOffset和positionOffsetPixels,我們可以跟隨這個進行控制x的位置。

LinearLayout的聯動,當前Tab如果是移動到可見Tab的最後一個,我們依然是根據onPageScrolled提供的positionOffset,讓我們的Linearlayout進行scrollXTo~~

好了,該分析的都分析了~~這裏需要說明,自定義控件有時候組合已經有的控件實現的效果也是棒棒噠~~既然有合適的,何必自己去從無到有呢~~

3、使用方式

在編寫代碼前,還是先貼下使用方式,讓大家先有個感官上的認識,然後再根據這個認識,去探索代碼中的實現過程~~

1、佈局文件

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     xmlns:zhy="http://schemas.android.com/apk/res/com.example.demo_zhy_mms_miui"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:background="#ffffffff"  
  7.     android:orientation="vertical" >  
  8.   
  9.     <com.example.demo_zhy_mms_miui.ViewPagerIndicator  
  10.         android:id="@+id/id_indicator"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="45dp"  
  13.         android:background="@drawable/title_bar_bg_one_row"  
  14.         android:orientation="horizontal"  
  15.         zhy:item_count="3" >  
  16.         
  17.     </com.example.demo_zhy_mms_miui.ViewPagerIndicator>  
  18.   
  19.     <android.support.v4.view.ViewPager  
  20.         android:id="@+id/id_vp"  
  21.         android:layout_width="match_parent"  
  22.         android:layout_height="0dp"  
  23.         android:layout_weight="1" >  
  24.     </android.support.v4.view.ViewPager>  
  25.   
  26. </LinearLayout>  

首先我們在佈局文件中聲明下,一個是ViewPagerIndicator ,一個是我們的ViewPager.

2、MainActivity

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.example.demo_zhy_mms_miui;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Arrays;  
  5. import java.util.List;  
  6.   
  7. import android.os.Bundle;  
  8. import android.support.v4.app.Fragment;  
  9. import android.support.v4.app.FragmentActivity;  
  10. import android.support.v4.app.FragmentPagerAdapter;  
  11. import android.support.v4.view.ViewPager;  
  12. import android.view.Window;  
  13.   
  14. public class MainActivity extends FragmentActivity  
  15. {  
  16.     private List<Fragment> mTabContents = new ArrayList<Fragment>();  
  17.     private FragmentPagerAdapter mAdapter;  
  18.     private ViewPager mViewPager;  
  19. //  private List<String> mDatas = Arrays.asList("短信1", "短信2", "短信3", "短信4",  
  20. //          "短信5", "短信6", "短信7", "短信8", "短信9");  
  21.     private List<String> mDatas = Arrays.asList("短信""收藏""推薦");  
  22.   
  23.     private ViewPagerIndicator mIndicator;  
  24.   
  25.     @Override  
  26.     protected void onCreate(Bundle savedInstanceState)  
  27.     {  
  28.         super.onCreate(savedInstanceState);  
  29.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  30.         setContentView(R.layout.vp_indicator);  
  31.   
  32.         initView();  
  33.         initDatas();  
  34.         //設置Tab上的標題  
  35.         mIndicator.setTabItemTitles(mDatas);  
  36.         mViewPager.setAdapter(mAdapter);  
  37.         //設置關聯的ViewPager  
  38.         mIndicator.setViewPager(mViewPager,0);  
  39.   
  40.     }  
  41.   
  42.     private void initDatas()  
  43.     {  
  44.   
  45.         for (String data : mDatas)  
  46.         {  
  47.             VpSimpleFragment fragment = VpSimpleFragment.newInstance(data);  
  48.             mTabContents.add(fragment);  
  49.         }  
  50.   
  51.         mAdapter = new FragmentPagerAdapter(getSupportFragmentManager())  
  52.         {  
  53.             @Override  
  54.             public int getCount()  
  55.             {  
  56.                 return mTabContents.size();  
  57.             }  
  58.   
  59.             @Override  
  60.             public Fragment getItem(int position)  
  61.             {  
  62.                 return mTabContents.get(position);  
  63.             }  
  64.         };  
  65.     }  
  66.   
  67.     private void initView()  
  68.     {  
  69.         mViewPager = (ViewPager) findViewById(R.id.id_vp);  
  70.         mIndicator = (ViewPagerIndicator) findViewById(R.id.id_indicator);  
  71.     }  
  72.   
  73.   
  74. }  

關於我們的ViewPagerIndicator的使用,就兩行:

//設置Tab上的標題
mIndicator.setTabItemTitles(mDatas);

//設置關聯的ViewPager
mIndicator.setViewPager(mViewPager,0);

其他代碼都是初始化ViewPager神馬的~~可見,我們的控件寫好之後使用起來極其簡單~~

好了,大家注意下,佈局文件裏面有個設置可見Tab個數的屬性:zhy:item_count="3"  ;

比如:當item_count=3,而給的TabTitle的List<String>的size也是3的話,就是效果圖1的效果~~~

           當item_count=4,而給的TabTitle的List<String>的size大於4的話,就是效果圖2的效果~~~

其實,我們也支持直接在佈局中書寫我們的Tab,你完全可以不使用mIndicator.setTabItemTitles(mDatas);取而代之,你可以在佈局中定義幾個TextView,固定好文本,樣式什麼的~~其實別的控件我們也是支持的~~~

貼一下Fragment代碼~

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.example.demo_zhy_mms_miui;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v4.app.Fragment;  
  5. import android.view.Gravity;  
  6. import android.view.LayoutInflater;  
  7. import android.view.View;  
  8. import android.view.ViewGroup;  
  9. import android.widget.TextView;  
  10.   
  11. public class VpSimpleFragment extends Fragment  
  12. {  
  13.     public static final String BUNDLE_TITLE = "title";  
  14.     private String mTitle = "DefaultValue";  
  15.   
  16.     @Override  
  17.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  18.             Bundle savedInstanceState)  
  19.     {  
  20.         Bundle arguments = getArguments();  
  21.         if (arguments != null)  
  22.         {  
  23.             mTitle = arguments.getString(BUNDLE_TITLE);  
  24.         }  
  25.   
  26.         TextView tv = new TextView(getActivity());  
  27.         tv.setText(mTitle);  
  28.         tv.setGravity(Gravity.CENTER);  
  29.   
  30.         return tv;  
  31.     }  
  32.   
  33.     public static VpSimpleFragment newInstance(String title)  
  34.     {  
  35.         Bundle bundle = new Bundle();  
  36.         bundle.putString(BUNDLE_TITLE, title);  
  37.         VpSimpleFragment fragment = new VpSimpleFragment();  
  38.         fragment.setArguments(bundle);  
  39.         return fragment;  
  40.     }  
  41. }  

好了,看完使用方式,有木有一點小激動~~

4、自定義ViewPagerIndicator的實現

1、自定義屬性

其實可抽取爲自定義的屬性很多哈~這裏我們就寫了一個,就是tab的數量。你完全可以把指示器顏色,文本顏色神馬可定製的屬性全搞出來~~

我們的控件名稱叫做:ViewPagerIndicator

所以我們在values/attr.xml中這麼寫:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <attr name="item_count" format="integer"></attr>  
  5.   
  6.     <declare-styleable name="ViewPagerIndicator">  
  7.         <attr name="item_count" />  
  8.     </declare-styleable>  
  9.   
  10. </resources>  

定義好了,肯定得用,怎麼用?在哪用?就不用說了吧。上面的用法已經貼過佈局文件了~~記得自定義屬性的命名空間要注意哈~~~

首先看什麼,肯定要有哪些成員變量,和構造裏面做了些什麼~

2、構造方法及成員變量

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. public class ViewPagerIndicator extends LinearLayout  
  2. {  
  3.     /** 
  4.      * 繪製三角形的畫筆 
  5.      */  
  6.     private Paint mPaint;  
  7.     /** 
  8.      * path構成一個三角形 
  9.      */  
  10.     private Path mPath;  
  11.     /** 
  12.      * 三角形的寬度 
  13.      */  
  14.     private int mTriangleWidth;  
  15.     /** 
  16.      * 三角形的高度 
  17.      */  
  18.     private int mTriangleHeight;  
  19.       
  20.     /** 
  21.      * 三角形的寬度爲單個Tab的1/6 
  22.      */  
  23.     private static final float RADIO_TRIANGEL = 1.0f / 6;  
  24.     /** 
  25.      * 三角形的最大寬度 
  26.      */  
  27.     private final int DIMENSION_TRIANGEL_WIDTH = (int) (getScreenWidth() / 3 * RADIO_TRIANGEL);  
  28.       
  29.       
  30.     /** 
  31.      * 初始時,三角形指示器的偏移量 
  32.      */  
  33.     private int mInitTranslationX;  
  34.     /** 
  35.      * 手指滑動時的偏移量 
  36.      */  
  37.     private float mTranslationX;  
  38.   
  39.     /** 
  40.      * 默認的Tab數量 
  41.      */  
  42.     private static final int COUNT_DEFAULT_TAB = 4;  
  43.     /** 
  44.      * tab數量 
  45.      */  
  46.     private int mTabVisibleCount = COUNT_DEFAULT_TAB;  
  47.   
  48.     /** 
  49.      * tab上的內容 
  50.      */  
  51.     private List<String> mTabTitles;  
  52.     /** 
  53.      * 與之綁定的ViewPager 
  54.      */  
  55.     public ViewPager mViewPager;  
  56.       
  57.     /** 
  58.      * 標題正常時的顏色 
  59.      */  
  60.     private static final int COLOR_TEXT_NORMAL = 0x77FFFFFF;  
  61.     /** 
  62.      * 標題選中時的顏色 
  63.      */  
  64.     private static final int COLOR_TEXT_HIGHLIGHTCOLOR = 0xFFFFFFFF;  
  65.   
  66.     public ViewPagerIndicator(Context context)  
  67.     {  
  68.         this(context, null);  
  69.     }  
  70.   
  71.     public ViewPagerIndicator(Context context, AttributeSet attrs)  
  72.     {  
  73.         super(context, attrs);  
  74.   
  75.         // 獲得自定義屬性,tab的數量  
  76.         TypedArray a = context.obtainStyledAttributes(attrs,  
  77.                 R.styleable.ViewPagerIndicator);  
  78.         mTabVisibleCount = a.getInt(R.styleable.ViewPagerIndicator_item_count,  
  79.                 COUNT_DEFAULT_TAB);  
  80.         if (mTabVisibleCount < 0)  
  81.             mTabVisibleCount = COUNT_DEFAULT_TAB;  
  82.         a.recycle();  
  83.   
  84.         // 初始化畫筆  
  85.         mPaint = new Paint();  
  86.         mPaint.setAntiAlias(true);  
  87.         mPaint.setColor(Color.parseColor("#ffffffff"));  
  88.         mPaint.setStyle(Style.FILL);  
  89.         mPaint.setPathEffect(new CornerPathEffect(3));  
  90.   
  91.     }  

看起來成員變量挺多的,其實主要就幾類:

最前面的6個都是和繪製那個三角形相關的,畫筆決定了三角形的樣式(顏色等),Path用於構造這個三角形(其實就是3條線的封閉合),然後就是三角形的寬度什麼的。

接下來的兩個:都帶Translation,肯定是和三角形的位置相關的了~

剩下的就是Tab內容、數量神馬的~~

看看我們構造方法裏面:獲得了自定義屬性,即可見的Tab的數量,初始化了我們的畫筆,這裏設置了setPathEffect,就是爲了畫的線的連接處,有點圓角~~

3、onFinishInflate和onSizeChanged

我們的一些初始化工作,會在這兩個方法裏面做~~尺寸相關的,會在onSizeChanged回調裏面進行設置~

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 設置佈局中view的一些必要屬性;如果設置了setTabTitles,佈局中view則無效 
  3.      */  
  4.     @Override  
  5.     protected void onFinishInflate()  
  6.     {  
  7.         Log.e("TAG""onFinishInflate");  
  8.         super.onFinishInflate();  
  9.   
  10.         int cCount = getChildCount();  
  11.   
  12.         if (cCount == 0)  
  13.             return;  
  14.   
  15.         for (int i = 0; i < cCount; i++)  
  16.         {  
  17.             View view = getChildAt(i);  
  18.             LinearLayout.LayoutParams lp = (LayoutParams) view  
  19.                     .getLayoutParams();  
  20.             lp.weight = 0;  
  21.             lp.width = getScreenWidth() / mTabVisibleCount;  
  22.             view.setLayoutParams(lp);  
  23.         }  
  24.         // 設置點擊事件  
  25.         setItemClickEvent();  
  26.   
  27.     }  

這個其實是獲取在佈局文件中直接寫好Tab的~~如果你在這寫好了,就不需要去調用mIndicator.setTabItemTitles(mDatas);了~~

大家可以下載文末的代碼後,把mIndicator.setTabItemTitles(mDatas);這行代碼註釋進行測試~~不過注意下定義的Tab和ViewPager的頁面數量最好一致。

代碼很簡單,就是獲取ChildView,然後顯示的重置一個寬度爲getScreenWidth() / mTabVisibleCount;接下來設置一下點擊事件。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 設置點擊事件 
  3.      */  
  4.     public void setItemClickEvent()  
  5.     {  
  6.         int cCount = getChildCount();  
  7.         for (int i = 0; i < cCount; i++)  
  8.         {  
  9.             final int j = i;  
  10.             View view = getChildAt(i);  
  11.             view.setOnClickListener(new OnClickListener()  
  12.             {  
  13.                 @Override  
  14.                 public void onClick(View v)  
  15.                 {  
  16.                     mViewPager.setCurrentItem(j);  
  17.                 }  
  18.             });  
  19.         }  
  20.     }  

這個就更簡單了~~就是mViewPager.setCurrentItem(j);

下面看看onSizeChanged

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 初始化三角形的寬度 
  3.      */  
  4.     @Override  
  5.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
  6.     {  
  7.         super.onSizeChanged(w, h, oldw, oldh);  
  8.         mTriangleWidth = (int) (w / mTabVisibleCount * RADIO_TRIANGEL);// 1/6 of  
  9.                                                                         // width  
  10.         mTriangleWidth = Math.min(DIMENSION_TRIANGEL_WIDTH, mTriangleWidth);  
  11.   
  12.         // 初始化三角形  
  13.         initTriangle();  
  14.   
  15.         // 初始時的偏移量  
  16.         mInitTranslationX = getWidth() / mTabVisibleCount / 2 - mTriangleWidth  
  17.                 / 2;  
  18.     }  
  19.       
  20.     /** 
  21.      * 初始化三角形指示器 
  22.      */  
  23.     private void initTriangle()  
  24.     {  
  25.         mPath = new Path();  
  26.   
  27.         mTriangleHeight = (int) (mTriangleWidth / 2 / Math.sqrt(2));  
  28.         mPath.moveTo(00);  
  29.         mPath.lineTo(mTriangleWidth, 0);  
  30.         mPath.lineTo(mTriangleWidth / 2, -mTriangleHeight);  
  31.         mPath.close();  
  32.     }  

onSizeChanged,我們主要是確定三角形的寬度和Path去構造這個三角形。

默認的我們的三角形的底邊的寬度爲,每個Tab寬度的1/6;當然有個上限是 (int) (getScreenWidth() / 3 * RADIO_TRIANGEL);【RADIO_TRIANGEL = 1.0f / 6】

這個其實無所謂,主要爲了屏幕適配,你可以抽取爲自定義屬性讓用戶去設置;

initTriangle()中用Path去構造了一個三角形,這個很簡單了~~

這裏還初始化了mInitTranslationX,因爲一開始顯示的就在第一個Tab的中間位置。

三角形初始化完成了,是不是應該去看看它在哪進行繪製的~~

4、dispatchDraw

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * 繪製指示器 
  3.  */  
  4. @Override  
  5. protected void dispatchDraw(Canvas canvas)  
  6. {  
  7.     canvas.save();  
  8.     // 畫筆平移到正確的位置  
  9.     canvas.translate(mInitTranslationX + mTranslationX, getHeight() + 1);  
  10.     canvas.drawPath(mPath, mPaint);  
  11.     canvas.restore();  
  12.   
  13.     super.dispatchDraw(canvas);  
  14. }  

在繪製子View之前,我們先繪製我們的三角形指示器~~

可以看到,我們通過canvas.translate移動畫布,來把指示器畫到了指定的位置~~當然了,記得save和restore.

看到,我們這裏還有個mTranslationX,這個是動態變化的,後面會介紹~~


三角形繪製完成了,應該到了,跟隨ViewPager移動了把~~當然了,這裏肯定得先綁定ViewPager,不然怎麼跟隨

5、setViewPager 

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. // 設置關聯的ViewPager  
  2. public void setViewPager(ViewPager mViewPager, int pos)  
  3. {  
  4.     this.mViewPager = mViewPager;  
  5.   
  6.     mViewPager.setOnPageChangeListener(new OnPageChangeListener()  
  7.     {  
  8.         @Override  
  9.         public void onPageSelected(int position)  
  10.         {  
  11.             // 設置字體顏色高亮  
  12.             resetTextViewColor();  
  13.             highLightTextView(position);  
  14.   
  15.             // 回調  
  16.             if (onPageChangeListener != null)  
  17.             {  
  18.                 onPageChangeListener.onPageSelected(position);  
  19.             }  
  20.         }  
  21.   
  22.         @Override  
  23.         public void onPageScrolled(int position, float positionOffset,  
  24.                 int positionOffsetPixels)  
  25.         {  
  26.             // 滾動  
  27.             scroll(position, positionOffset);  
  28.   
  29.             // 回調  
  30.             if (onPageChangeListener != null)  
  31.             {  
  32.                 onPageChangeListener.onPageScrolled(position,  
  33.                         positionOffset, positionOffsetPixels);  
  34.             }  
  35.   
  36.         }  
  37.   
  38.         @Override  
  39.         public void onPageScrollStateChanged(int state)  
  40.         {  
  41.             // 回調  
  42.             if (onPageChangeListener != null)  
  43.             {  
  44.                 onPageChangeListener.onPageScrollStateChanged(state);  
  45.             }  
  46.   
  47.         }  
  48.     });  
  49.     // 設置當前頁  
  50.     mViewPager.setCurrentItem(pos);  
  51.     // 高亮  
  52.     highLightTextView(pos);  
  53. }  

很簡單的代碼,我們關聯上ViewPager以後,立刻註冊setOnPageChangeListener,關於指示器的跟隨移動,核心代碼是:onPageScrolled中的

// 滾動
scroll(position, positionOffset);這行後面介紹~

這裏注意下,我們不是把setOnPageChangeListener用了麼,但是用戶可能也需要監聽這個接口,去幹一些事,那麼我們就需要給用戶解決,於是我們自己定義一個類似的接口公佈給用戶:

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 對外的ViewPager的回調接口 
  3.      *  
  4.      * @author zhy 
  5.      *  
  6.      */  
  7.     public interface PageChangeListener  
  8.     {  
  9.         public void onPageScrolled(int position, float positionOffset,  
  10.                 int positionOffsetPixels);  
  11.   
  12.         public void onPageSelected(int position);  
  13.   
  14.         public void onPageScrollStateChanged(int state);  
  15.     }  
  16.   
  17.     // 對外的ViewPager的回調接口  
  18.     private PageChangeListener onPageChangeListener;  
  19.   
  20.     // 對外的ViewPager的回調接口的設置  
  21.     public void setOnPageChangeListener(PageChangeListener pageChangeListener)  
  22.     {  
  23.         this.onPageChangeListener = pageChangeListener;  
  24.     }  

如果用戶需要回調,請使用我們的mIndicator.setOnPageChangeListener,回調的方法和原本的listener一模一樣~~

ps:不要問我,這裏用了mViewPager.setOnPageChangeListener我還想監聽咋辦,以及我設置了mViewPager.setOnPageChangeListener指示器怎麼不動了,請仔細看上文

當然了,還有個高亮文本和重置文本顏色的代碼,其實就是簡單改變下當前選擇的Tab的文本的顏色。

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 高亮文本 
  3.      *  
  4.      * @param position 
  5.      */  
  6.     protected void highLightTextView(int position)  
  7.     {  
  8.         View view = getChildAt(position);  
  9.         if (view instanceof TextView)  
  10.         {  
  11.             ((TextView) view).setTextColor(COLOR_TEXT_HIGHLIGHTCOLOR);  
  12.         }  
  13.   
  14.     }  
  15.   
  16.     /** 
  17.      * 重置文本顏色 
  18.      */  
  19.     private void resetTextViewColor()  
  20.     {  
  21.         for (int i = 0; i < getChildCount(); i++)  
  22.         {  
  23.             View view = getChildAt(i);  
  24.             if (view instanceof TextView)  
  25.             {  
  26.                 ((TextView) view).setTextColor(COLOR_TEXT_NORMAL);  
  27.             }  
  28.         }  
  29.     }  

接下來就到scroll登場了~

6、scroll

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 指示器跟隨手指滾動,以及容器滾動 
  3.      *  
  4.      * @param position 
  5.      * @param offset 
  6.      */  
  7.     public void scroll(int position, float offset)  
  8.     {  
  9.         /** 
  10.          * <pre> 
  11.          *  0-1:position=0 ;1-0:postion=0; 
  12.          * </pre> 
  13.          */  
  14.         // 不斷改變偏移量,invalidate  
  15.         mTranslationX = getWidth() / mTabVisibleCount * (position + offset);  
  16.   
  17.         int tabWidth = getScreenWidth() / mTabVisibleCount;  
  18.   
  19.         // 容器滾動,當移動到倒數最後一個的時候,開始滾動  
  20.         if (offset > 0 && position >= (mTabVisibleCount - 2)  
  21.                 && getChildCount() > mTabVisibleCount)  
  22.         {  
  23.             if (mTabVisibleCount != 1)  
  24.             {  
  25.                 this.scrollTo((position - (mTabVisibleCount - 2)) * tabWidth  
  26.                         + (int) (tabWidth * offset), 0);  
  27.             } else  
  28.             // 爲count爲1時 的特殊處理  
  29.             {  
  30.                 this.scrollTo(  
  31.                         position * tabWidth + (int) (tabWidth * offset), 0);  
  32.             }  
  33.         }  
  34.   
  35.         invalidate();  
  36.     }  

看完之後,有沒有一種,臥槽,就這幾行代碼就實現了,指示器跟隨滾動和我們的Tab跟隨滾動~~

嗯,其實指示器跟隨滾動上面說了,依賴mTranslationX,然後藉着canvas.translate實現的~~也就是說,就一行去確定當前應該的偏移即可。

比如:從第0個Tab滑向第1個Tab:position爲0,offset會0.0~1.0這麼變化~我們的偏移量實際也就是增加 offset * 每個Tab的寬度~

好了,下面說容器滾動,其實容器滾動的x也是 offset * 每個Tab的寬度~;只不過,有個前提就是當前滑動的是可見的倒數第二個到最後一個,所以我們有個判斷:

 position >= (mTabVisibleCount - 2) ; 於是乎,我們在偏移的時候也有:(position - (mTabVisibleCount - 2)) * tabWidth ;如當前恰好是可見的倒數第二個到最後一個,

那麼position - (mTabVisibleCount - 2)爲0,偏移量也就是(tabWidth * offset)~~

當可見爲0的時候,我們需要特殊處理下,也就是我們的else~

最後記得invalidate~~

好了,到此核心的方法介紹完了~~剩下些雜七雜八的~~

7、剩餘的方法

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.      * 設置可見的tab的數量 
  3.      *  
  4.      * @param count 
  5.      */  
  6.     public void setVisibleTabCount(int count)  
  7.     {  
  8.         this.mTabVisibleCount = count;  
  9.     }  
  10.   
  11.     /** 
  12.      * 設置tab的標題內容 可選,可以自己在佈局文件中寫死 
  13.      *  
  14.      * @param datas 
  15.      */  
  16.     public void setTabItemTitles(List<String> datas)  
  17.     {  
  18.         // 如果傳入的list有值,則移除佈局文件中設置的view  
  19.         if (datas != null && datas.size() > 0)  
  20.         {  
  21.             this.removeAllViews();  
  22.             this.mTabTitles = datas;  
  23.   
  24.             for (String title : mTabTitles)  
  25.             {  
  26.                 // 添加view  
  27.                 addView(generateTextView(title));  
  28.             }  
  29.             // 設置item的click事件  
  30.             setItemClickEvent();  
  31.         }  
  32.   
  33.     }  

其實就是你可以在onCreate裏面去設置tab顯示的內容,以及可見的Tab數量,大家猜一猜,如果在佈局和onCreate裏面都寫了數量,哪個有效呢(自己去實驗)~~

記得如果是代碼控制,setVisibleTabCount在setTabItemTitles之前調用。


ok,基本完工了~~~

有興趣的,把三角形改成我們的下劃線指示器玩一玩~~估計改幾行代碼即可~~




源碼點擊下載



建了一個QQ羣,方便大家交流。羣號:423372824

----------------------------------------------------------------------------------------------------------

博主部分視頻已經上線,如果你不喜歡枯燥的文本,請猛戳(初錄,期待您的支持):

1、Android 自定義控件實戰 電商活動中的刮刮卡

2、Android自定義控件實戰  打造Android流式佈局和熱門標籤

3、Android智能機器人“小慕”的實現

4、高仿QQ5.0側滑

5、高仿微信5.2.1主界面及消息提醒

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章