1. 程式人生 > >Android之ViewPagerIndictor三角形指示器

Android之ViewPagerIndictor三角形指示器

話不多說,上效果圖啊
這裡寫圖片描述
根據慕課網裡面寫出來的,可以點選,也可以滑動,不過只能滑動一個
MainActivity類:

package com.example.viewpagerindicator;

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

import com.example.view.ViewPagerIndicator;

import android.app.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.support.v4.view.ViewPager.OnPageChangeListener; import android.view.Menu; import android.view.MenuItem; import android.view.Window; /** * * @author
Administrator * * FragmentActivity is a special activity provided in the Support * Library to handle fragments on system versions older than API level * 11. If the lowest system version you support is API level 11 or * higher, then you can use a regular Activity. */
public class MainActivity extends FragmentActivity { private ViewPager mViewpager; private ViewPagerIndicator mViewPagerIndicator; private List<String> mTitles = Arrays.asList("簡訊1", "收藏2", "推薦3", "簡訊4", "收藏5", "推薦6", "簡訊7", "收藏8", "推薦9"); private List<VpSimpleFragment> mContents = new ArrayList<VpSimpleFragment>();// 裝載ViewPager資料的List /** * FragmentPagerAdapter,見名知意,這個介面卡就是用來實現Fragment在ViewPager裡面進行滑動切換的,因此, * 如果我們想實現Fragment的左右滑動,可以選擇ViewPager和FragmentPagerAdapter實現。 * FragmentPagerAdapter擁有自己的快取策略 * ,當和ViewPager配合使用的時候,會快取當前Fragment以及左邊一個、右邊一個,一共三個Fragment物件。 * 假如有三個Fragment * ,那麼在ViewPager初始化之後,3個fragment都會載入完成,中間的Fragment在整個生命週期裡面只會載入一次 * ,當最左邊的Fragment處於顯示狀態 * ,最右邊的Fragment由於超出快取範圍,會被銷燬,當再次滑到中間的Fragment的時候,最右邊的Fragment會被再次初始化。 */ private FragmentPagerAdapter mAdapter;// ViewPager介面卡 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); initViews(); initDatas(); //動態設定tab mViewPagerIndicator.setVisibleTabCount(4); mViewPagerIndicator.setTabItemTitles(mTitles); mViewpager.setAdapter(mAdapter); mViewPagerIndicator.setViewPager(mViewpager, 0); // mViewpager.setOnPageChangeListener(new OnPageChangeListener() { // // @Override // public void onPageSelected(int position) { // // TODO Auto-generated method stub // // } // // @Override // public void onPageScrolled(int position, float positionOffset, // int positionOffsetPixels) { // // 三角形跟隨ViewPager移動的距離就是: // // tabWidth*positionOffset+position*tabWidth // mViewPagerIndicator.scroll(position, positionOffset); // // } // // @Override // public void onPageScrollStateChanged(int state) { // // TODO Auto-generated method stub // // } // }); } /** * 初始化檢視 */ private void initDatas() { mViewpager = (ViewPager) findViewById(R.id.viewpager); mViewPagerIndicator = (ViewPagerIndicator) findViewById(R.id.indicator); } /** * 初始化資料 */ private void initViews() { // 根據title初始化fragment for (String title : mTitles) { VpSimpleFragment fragment = VpSimpleFragment.newInstance(title); mContents.add(fragment); } // getFragmentManager(); mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public int getCount() { return mContents.size(); } @Override public Fragment getItem(int position) { return mContents.get(position); } }; } }

ViewPagerIndicator類:

package com.example.view;

import java.util.List;

import com.example.viewpagerindicator.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Paint.Style;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;

public class ViewPagerIndicator extends LinearLayout {
    private Paint mPaint;// 繪製三角形的畫筆
    private Path mPath;// 用於繪製三角形的邊
    private int mTriangleWidth;// 三角形的寬
    private int mTriangleHeight;// 三角形的高
    private static final float RADIO_TRIANGLE_WIDTH = 1 / 6F;// 用於設定三角形的寬和tab底邊的比例,用於螢幕適配
    /**
     * 三角形底邊的最大寬度
     */
    private final int DIMENSION_TRIANGLE_WIDTH_MAX = (int) (getScreenWidth()/3*RADIO_TRIANGLE_WIDTH);
    private int mInitTranslationX;// 第一個三角形初始化的偏移位置
    private int mTranslationX;// 移動時候的三角形偏移位置
    private int mTabVisibleCount;// 可見tab的數量
    private static final int COUNT_DEFAULT_TAB = 4;// 預設可見tab為4個
    private List<String> mTitles;// 接收傳遞過來的title
    private static final int COLOR_TEXT_NORMAL = Color.parseColor("#FFFFFF");
    private static final int COLOR_TEXT_HIGHLIGHT = Color.parseColor("#FF4CDA0F");

    public ViewPagerIndicator(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    public ViewPagerIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 獲取可見tab的數量
        TypedArray attributes = context.obtainStyledAttributes(attrs,
                R.styleable.ViewPagerIndicator);
        mTabVisibleCount = attributes.getInt(
                R.styleable.ViewPagerIndicator_visible_tab_count,
                COUNT_DEFAULT_TAB);
        if (mTabVisibleCount < 0) {
            mTabVisibleCount = COUNT_DEFAULT_TAB;
        }
        // 用完必須釋放
        attributes.recycle();

        // 初始化畫筆
        mPaint = new Paint();
        // 防止邊緣鋸齒
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.parseColor("#ffffff"));
        mPaint.setStyle(Style.FILL);
        mPaint.setPathEffect(new CornerPathEffect(3));
    }

    public ViewPagerIndicator(Context context) {
        this(context, null);
    }

    /**
     * 繪製三角形
     * 繪製VIew本身的內容,通過呼叫View.onDraw(canvas)函式實現,繪製自己的孩子通過dispatchDraw(canvas)實現
     * 
     * 畫完背景後,draw過程會呼叫onDraw(Canvas canvas)方法,然後就是dispatchDraw(Canvas canvas)方法,
     * dispatchDraw
     * ()主要是分發給子元件進行繪製,我們通常定製元件的時候重寫的是onDraw()方法。值得注意的是ViewGroup容器元件的繪製
     * ,當它沒有背景時直接呼叫的是dispatchDraw
     * ()方法,而繞過了draw()方法,當它有背景的時候就呼叫draw()方法,而draw()方法裡包含了
     * dispatchDraw()方法的呼叫。因此要在ViewGroup上繪製東西的時候往往重寫的是
     * dispatchDraw()方法而不是onDraw()方法,或者自定製一個Drawable,重寫它的draw(Canvas c)和
     * getIntrinsicWidth(),getIntrinsicHeight()方法,然後設為背景
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

        /**
         * save:用來儲存Canvas的狀態。save之後,可以呼叫Canvas的平移、放縮、旋轉、錯切、裁剪等操作。
         * 
         * restore:用來恢復Canvas之前儲存的狀態。防止save後對Canvas執行的操作對後續的繪製有影響。
         * 
         * save和restore要配對使用(restore可以比save少,但不能多),如果restore呼叫次數比save多,會引發Error。
         */
        canvas.save();

        canvas.translate(mInitTranslationX + mTranslationX, getHeight() + 2);
        canvas.drawPath(mPath, mPaint);

        canvas.restore();
    }

    /**
     * 設定三角形的大小
     * 
     * onSizeChanged()在控制元件大小發生變化的時候呼叫(例如第一次初始化控制元件的時候) 佈局過程中,
     * 先調onMeasure計算每個child的大小, 然後呼叫onLayout對child進行佈局,
     * onSizeChanged()是在佈局發生變化時的回撥函式,間接回去呼叫onMeasure, onLayout函式重新佈局
     * onSizeChanged的啟動時間在onDraw之前
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);
        // w/3為每個tab的寬度,目前可見為3個
        mTriangleWidth = (int) (w / mTabVisibleCount * RADIO_TRIANGLE_WIDTH);
        //選取最小的那一個作為寬
        mTriangleWidth = Math.min(mTriangleWidth, DIMENSION_TRIANGLE_WIDTH_MAX);
        // 第一個三角形的偏移位置
        mInitTranslationX = w / mTabVisibleCount / 2 - mTriangleWidth / 2;

        initTriangle();
    }

    /**
     * 初始化三角形
     */
    private void initTriangle() {
        // mTriangleHeight = mTriangleWidth / 2;
        // 將三角形角度設定為30度
        mTriangleHeight = (int) (mTriangleWidth / 2 * Math.tan(Math.PI / 6));

        mPath = new Path();
        mPath.moveTo(0, 0);
        mPath.lineTo(mTriangleWidth, 0);
        mPath.lineTo(mTriangleWidth / 2, -mTriangleHeight);
        // 關閉當前輪廓,完成閉合
        mPath.close();
    }

    /**
     * 三角形跟隨ViewPager移動
     * 
     * @param position
     * @param positionOffset
     */
    public void scroll(int position, float positionOffset) {
        int tabWidth = getWidth() / mTabVisibleCount;
        mTranslationX = (int) (tabWidth * (positionOffset + position));

        /**
         * 容器移動,在tab處於移動至最後一個時
         */
        if (position >= (mTabVisibleCount - 2) && positionOffset > 0
                && getChildCount() > mTabVisibleCount&&position<getChildCount()-2) {

            if (mTabVisibleCount != 1) {
                this.scrollTo((position - (mTabVisibleCount - 2)) * tabWidth
                        + (int) (tabWidth * positionOffset), 0);
            } else {
                this.scrollTo(position * tabWidth
                        + (int) (tabWidth * positionOffset), 0);
            }
        }

        // 位置發生改變,要進行重繪
        /**
         * invalidate的意思是“使無效”,其實就是使視窗無效。 使當前的視窗無效的目的就是讓Windows知道這個視窗現在該重新繪製一下了。
         * 所以任何時候當你想 擦除 並 繪製視窗的時候,就可以在別的函式中完成功能程式碼之後Invalidate()一下。OnDraw馬上就會被呼叫了。
         * 但是不要在OnDraw, OnPaint中用
         */
        invalidate();
    }

    /**
     * xml載入完成之後,回撥此方法
     * 
     * 設定每個tab的LayoutParams
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        int childCount = getChildCount();
        if (childCount == 0) {
            return;
        }

        for (int i = 0; i < childCount; i++) {
            View view = getChildAt(i);
            LinearLayout.LayoutParams params = (LayoutParams) view
                    .getLayoutParams();
            params.weight = 0;
            params.width = getScreenWidth() / mTabVisibleCount;
            view.setLayoutParams(params);
        }
        setItemClickEvent();
    }

    /**
     * 獲取螢幕的寬度
     * 
     * @return
     */
    private int getScreenWidth() {
        WindowManager wm = (WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        return outMetrics.widthPixels;
    }

    /**
     * 動態設定tab的數量
     * 
     * @param count
     */
    public void setVisibleTabCount(int count) {
        mTabVisibleCount = count;
    }

    /**
     * 動態設定tab
     * 
     * @param titles
     */
    public void setTabItemTitles(List<String> titles) {
        if (titles != null && titles.size() > 0) {
            this.removeAllViews();
            mTitles = titles;
            for (String title : mTitles) {
                this.addView(generateTextView(title));
            }
            setItemClickEvent();
        }
    }



    /**
     * 根據title建立tab
     * 
     * @param title
     * @return
     */
    private View generateTextView(String title) {
        TextView textView = new TextView(getContext());
        LinearLayout.LayoutParams params = new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        params.width = getScreenWidth() / mTabVisibleCount;
        textView.setText(title);
        textView.setGravity(Gravity.CENTER);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
        textView.setTextColor(COLOR_TEXT_NORMAL);
        textView.setLayoutParams(params);
        return textView;
    }

    // 接收關聯的ViewPager
    private ViewPager mViewPager;

    /**
     * 提供一個介面供外部ViewPager使用
     * 
     * @author Administrator
     * 
     */
    public interface PageOnChangeListener {
        public void onPageScrolled(int position, float positionOffset,
                int positionOffsetPixels);

        public void onPageSelected(int position);

        public void onPageScrollStateChanged(int state);
    }

    public PageOnChangeListener mListener;

    public void setViewPagerOnPageChangeListener(PageOnChangeListener listener) {
        mListener = listener;
    }

    /**
     * 設定關聯的ViewPager
     * 
     * @param viewpager
     * @param position
     */
    public void setViewPager(ViewPager viewpager, int position) {
        mViewPager = viewpager;
        mViewPager.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                if (mListener != null) {
                    mListener.onPageSelected(position);
                }
                highLightTextView(position);
            }

            @Override
            public void onPageScrolled(int position, float positionOffset,
                    int positionOffsetPixels) {
                // 三角形跟隨ViewPager移動的距離就是:
                // tabWidth*positionOffset+position*tabWidth
                scroll(position, positionOffset);

                if (mListener != null) {
                    mListener.onPageScrolled(position, positionOffset,
                            positionOffsetPixels);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                if (mListener != null) {
                    mListener.onPageScrollStateChanged(state);
                }

            }
        });
        mViewPager.setCurrentItem(position);
        highLightTextView(position);
    }
    /**
     * 高亮被點選的tab
     * @param position
     */
    private void highLightTextView(int position){
        resetTextViewColor();
        View view = getChildAt(position);
        if (view instanceof TextView) {
            ((TextView) view).setTextColor(COLOR_TEXT_HIGHLIGHT);
        }
    }
    /**
     * 重置tab文字顏色
     */
    private void resetTextViewColor(){
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            if (view instanceof TextView) {
                ((TextView) view).setTextColor(COLOR_TEXT_NORMAL);
            }
        }
    }
    /**
     * 設定Tab的點選事件
     */
    private void setItemClickEvent(){
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final int j = i;
            View view = getChildAt(i);
            view.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mViewPager.setCurrentItem(j);
                }
            });
        }
    }
}