Android之實現ViewPagerIndicator

PS:最近一直忙於學習任務,一直沒有時間去寫博客.今天週六,終於有時間了.java

 

學習任務:android

1.打造一個本身的ViewPagerIndicator數組

 

  最近被安排了一大堆的學習任務,感受老闆仍是很好的,讓咱們在業餘時間多提高本身的我的能力,就拿這個ViewPagerIndicator來講吧,當初本身沒有什麼好的實現方案,如今也就學了一發,看了一下Google上的實現方案,針對的狀況比較的多,我這裏就針對一種狀況來講.你們想更深刻的研究能夠去Github上搜索一下Google工程師的實現方式,效果都很棒,有興趣的能夠所有研究一下.我看了一下鴻洋的實現方式,不過他作的是三角形的.我作的是線條狀的,所以作了一些修改.app


  原理仍是比較簡單的,下面是一個ViewPager,上面總體是一個ViewPagerIndicator.而後在最下面畫一個線條當作指示符就能夠了,當ViewPager在滑動的時候,咱們只須要知道偏移的距離,而後從新繪製這條指示符,調用invalidate()函數從新繪製視圖就能夠了。ide

 首先說一下如何畫纔是關鍵,畫出這個指示符其實就是畫出一個實心的矩形,咱們須要知道矩形的長度和寬度,以及在什麼位置進行繪製,通常高度咱們本身是能夠人爲指定的,咱們須要多高就能夠指定成多少單位個dp,最後轉換成px就能夠了,可是寬度呢?寬度其實 = 屏幕寬度 / 顯示的ViewPager的數量,獲取屏幕的寬度仍是比較簡單的,由於咱們已經在xml文件中指定了這個ViewPagerIndicator的寬度,咱們只須要測量一下就可以拿到他的具體寬度.函數

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     mTop = getMeasuredHeight();
     int width = getMeasuredWidth();
     int height = mTop + DensityUtils.dip2px(context, mHeight);
     mWidth = width / mTabVisibleCount;
     setMeasuredDimension(width, height);
 }

  這樣咱們就可以獲取到整個ViewPagerIndicator的高度和寬度,那麼矩形指示符的 mTop(頂部起始位置) =  getMeasureHeight(); 由於指示符也是須要高度的,所以咱們須要從新設置整個ViewPagerIndicator的高度,所以這裏須要調用setMeasuredDimension(width,height)從新設置整個ViewPagerIndicator的高度和寬度。有了這個思路,咱們就可以繪製出這個矩形的指示符。那麼還有一個難題,就是如何設置顯示的數量.總不能在自定義的時候寫死吧.這樣不是很是的靈活,而且在實際項目開發的時候,不一樣的頁面,須要的指示符的數量也是不相同的.所以這裏咱們須要使用一種靈活的方式去設置指示符的顯示數量.學習

 靈活的設置指示符的顯示數量須要自定義視圖屬性,須要在attrs文件中進行聲明.字體

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!--屬性的名字以及對應的類型-->
    <attr name="item_count" format="integer"></attr>
     
    <!--聲明自定義屬性,與上面造成關聯關係-->
    <declare-styleable name="ViewPagerIndicator">
        <attr name="item_count" />
    </declare-styleable>

</resources>

  那麼這東西如何使用呢?首先咱們須要在xml文件中進行設置.this

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    <!--這裏須要引入命名空間-->
    xmlns:rzx="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!--修改顯示數量
       rzx:item_count="咱們想設置的數值",這裏我設置了5個
       前面這個名字是命名空間,能夠隨便設置.  
     -->
   <com.example.totem.myviewpagerindicator.view.ViewPagerIndicator
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        rzx:item_count="4">
        
    </com.example.totem.myviewpagerindicator.view.ViewPagerIndicator>
    
</LinearLayout>

  由於咱們是自定義控件實現的,所以咱們須要在咱們自定義的ViewPagerIndicator中去獲取咱們聲明的顯示數量.而後去指定指示符的顯示寬度.這樣就能夠根據本身定義的顯示數量去指定指示符的寬度了.spa

public ViewPagerIndicator(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    /**
     * 存放自定義視圖屬性的數組容器,須要在attrs文件夾中定義屬性
     * 屬性支持:
     * reference string color dimension boolean integer float fraction enum flag
     * */
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ViewPagerIndicator);
   /**
     * 獲取數組容器中屬性數量
     * */
    mTabVisibleCount = typedArray.getInt(R.styleable.ViewPagerIndicator_item_count, COUNT_DEFAULT_TAB);
    if (mTabVisibleCount <= 0) {
        mTabVisibleCount = COUNT_DEFAULT_TAB;
    }
    /**
     * 調用recycle()函數
     * */
    typedArray.recycle();
}

  獲取過程就須要使用TypedArray去進行獲取,它是存放自定義視圖的一個數組容器,支持的類型在上面已經聲明瞭,我就不進行囉嗦了.obtainStyledAttributes()這個方法顧名思義,獲取Styled中聲明的屬性,那麼從哪裏獲取呢?從咱們的attrs文件夾中獲取,那麼獲取那個值呢?獲取的就是咱們聲明的ViewPagerIndicator名字的屬性值.由於咱們已經指定了顯示的數量,所以他就能夠獲取到咱們指定的數值,若是獲取不到,那麼就會有一個默認值.就這麼簡單,這裏recycle()函數是爲了再次利用typedArray。不調用也沒有什麼問題.

 那麼拿到了顯示數量,以及總體寬度,咱們就能夠去繪製這個矩形指示符了.

public Rect(int left, int top, int right, int bottom) {
    this.left = left;
    this.top = top;
    this.right = right;
    this.bottom = bottom;
}

  繪製矩形須要傳遞四個參數,top咱們已經獲取到了.起初的ViewPagerIndicator的高度位置.bottom就是最終的ViewPagerIndicator的高度,left的獲取實際上是比較重要的,其實他的位置也很容易獲取.left = (position(當前位置) + offset(偏移量)) * mWidth(指示符的寬度). left1(第一個位置的指示符位置) = (0 + 0) * mWidth。偏移的過程當中,offset是始終改變的.那麼這個left也是隨之變更的.而後不斷的從新繪製.

/**
    * 指示符滾動
    */
public void scroll(int position, float offset) {
    mLeft = (int) ((position + offset) * mWidth);
    /**
     * 從新繪製視圖
     * */
    invalidate();
}

  那麼誰給咱們時刻傳遞這個偏移量是個關鍵.偏移是一個過程,所以若是但願指示符是移動過去的,須要時刻獲取當前的偏移量,這個偏移量就得交給ViewPager了.咱們須要把咱們的ViewPagerIndicator與ViewPager進行綁定,這樣就能可以時刻獲取到偏移量了.這取決於ViewPager的方法.

 /**
   * 設置關聯的ViewPager
   */
public void setViewPager(ViewPager mViewPager, int pos) {
    this.mViewPager = mViewPager;
    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            scroll(position, positionOffset);
        }

        @Override
        public void onPageSelected(int position) {
            resetTextViewColor();
            highLightTextView(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
    mViewPager.setCurrentItem(pos);
    highLightTextView(pos);
}

  ViewPager的滑動監聽可以幫助咱們時刻獲取到偏移量.這裏咱們沒有必要再對外暴露接口什麼的了.若是還想作其餘的操做,咱們只須要封裝方法.讓其執行就能夠了..剩下的就是一些瑣碎的東西,好比說,改變字體的顏色,以及設置相關的點擊事件等等.

/**
     * 高亮文本
     */
    protected void highLightTextView(int position) {
        View view = getChildAt(position);
        if (view instanceof TextView) {
            ((TextView) view).setTextColor(COLOR_SELECT);
        }
    }

    /**
     * 重置文本顏色
     */
    private void resetTextViewColor() {
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            if (view instanceof TextView) {
                ((TextView) view).setTextColor(COLOR_NORMAL);
            }
        }
    }

    /**
     * 設置點擊事件
     */
    public void setItemClickEvent() {
        int cCount = getChildCount();
        for (int i = 0; i < cCount; i++) {
            final int j = i;
            View view = getChildAt(i);
            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mViewPager.setCurrentItem(j);
                }
            });
        }
    }

  核心的地方基本都說完了.小細節問題就是在設置高度的時候別忘了dp和px之間的轉換,不然畫出來的線條其實是有高度誤差的.MainActivity就只須要作一些初始化操做,爲ViewPager添加相關的Fragment就好了.

package com.example.totem.myviewpagerindicator.activity;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.Window;

import com.example.totem.myviewpagerindicator.R;
import com.example.totem.myviewpagerindicator.fragment.FinallyFragment;
import com.example.totem.myviewpagerindicator.fragment.FirstFragment;
import com.example.totem.myviewpagerindicator.fragment.FouthFragment;
import com.example.totem.myviewpagerindicator.fragment.SecondFragment;
import com.example.totem.myviewpagerindicator.fragment.ThirdFragment;
import com.example.totem.myviewpagerindicator.view.ViewPagerIndicator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends FragmentActivity {

    private List<Fragment> mTabContents = new ArrayList<>();
    private FragmentPagerAdapter mAdapter;
    private ViewPager mViewPager;
    private List<String> mDatas = Arrays.asList("1", "2", "3", "4", "5");
    private ViewPagerIndicator mIndicator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        //設置Tab上的標題
        mIndicator.setTabItemTitles(mDatas);
        mViewPager.setAdapter(mAdapter);
        //設置關聯的ViewPager
        mIndicator.setViewPager(mViewPager, 0);
    }

    private void initView() {
        mViewPager = (ViewPager) findViewById(R.id.viewPager);
        mIndicator = (ViewPagerIndicator) findViewById(R.id.Indicator);
    }

    private void initData() {

        FirstFragment firstFragment = new FirstFragment();
        SecondFragment secondFragment = new SecondFragment();
        ThirdFragment thirdFragment = new ThirdFragment();
        FouthFragment fouthFragment = new FouthFragment();
        FinallyFragment finallyFragment = new FinallyFragment();

        mTabContents.add(firstFragment);
        mTabContents.add(secondFragment);
        mTabContents.add(thirdFragment);
        mTabContents.add(fouthFragment);
        mTabContents.add(finallyFragment);

        mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {


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

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

  最後放上一個源代碼分享:http://pan.baidu.com/s/1i56onAX

相關文章
相關標籤/搜索