1. 程式人生 > >Android自定義Viewpager指示器PagerIndicator-仿微博頭條效果

Android自定義Viewpager指示器PagerIndicator-仿微博頭條效果

平時工作之餘,喜歡看看新聞,手機難免會裝了幾個新聞閱讀類的app。新聞類的app風格大致一致,可以選擇不同欄目,欄目可以切換。最近就在用微博頭條,感覺介面挺清新的。而且它使用的PagerIndicator挺好看的。昨晚居然準時下班了,趁著早就實現了下。今天用部落格好好記錄下

上圖

圖片描述

效果分析

1 每個tab都包含色塊和文字,而且文字的顯示個數不同

2 文字:由未選中到被選中的文字顏色從黑色變成白色;由選中到未被選中的文字顏色從白色變成黑色

3 色塊:由未選中到被選中的色塊顯示高度越來越大;由選中到未被選中的色塊顯示高度越來越小

4 讓處於中間的tab自動滑動到中間顯示

實現思路

1 tab實現

1.1 自定義UpDownView,該View繼承RelativeLayout

1.2 文字使用TextView顯示,色塊使用ImageView顯示

public UpDownView(Context context, AttributeSet attrs) {
    super(context, attrs);
    imageView = new ImageView(context);
    // 設定ImageView的大小為每個父View(tab)的大小
    LayoutParams imageParams = new LayoutParams(
                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    addView(imageView, imageParams);
    textView = new
TextView(context); // 設定TextView的文字居中顯示 textView.setGravity(Gravity.CENTER); // 設定TextView的高度為每個父View(tab)的高度 LayoutParams textParams = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); textParams.addRule(RelativeLayout.CENTER_IN_PARENT); textView.setPadding(40
, 10, 40, 10); addView(textView, textParams); }

1.3 ImageView顯示設定

// 當控制元件的寬高發生變化時回撥該方法,可在這裡獲取控制元件的高度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    height = textView.getMeasuredHeight();
    // 通過setTranslationY讓ImageView開始時只顯示總高度的0.2f
    imageView.setTranslationY((int) (height * 0.8f));
    LayoutParams imageParams = (LayoutParams) imageView
                    .getLayoutParams();
    imageParams.width = textView.getMeasuredWidth();
}

1.4 該View對外提供的方法

// 設定ImageView的顏色
public void setImageColor(int color) {
    imageView.setImageResource(color);
}
// 設定文字
public void setText(String text) {
    textView.setText(text);
}
// 設定文字的顏色
public void setTextColor(int color) {
    textView.setTextColor(color);
}
// 顯示色塊
public void show() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView,
            "translationY", 0);
    objectAnimator.setDuration(300);
    objectAnimator.start();
}
// 設定ImageView的偏移量,即改變色塊顯示的高度
public void setTranslation(float direction) {
    imageView.setTranslationY(direction);
}
// 隱藏色塊
public void hide() {
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView,
            "translationY", (int) (height * 0.8f));
    objectAnimator.setDuration(300);
    objectAnimator.start();
}
// 主要用於獲取當前色塊所在的位置
public int getmIndex() {
    return mIndex;
}

2 PagerIndicator指示器的實現

2.1 指示器可以裝載很多的tab,而且支援橫向滑動,於是可以選擇繼承HorizontalScrollView

2.2 由於HorizontalScrollView只能有一個子View,所以在初始化的時候,為該View新增一個預設的子View,當需要新增tab時,將其新增到預設的子View中即可。

public PagerIndicatorTwo(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    // 設定橫向滾動條不顯示
    setHorizontalScrollBarEnabled(false);
    // 新增一個LinearLayout作為預設的子View
    linearLayout = new LinearLayout(context);
    linearLayout.setOrientation(LinearLayout.HORIZONTAL);
    LayoutParams liLayoutParams = new LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    addView(linearLayout, liLayoutParams);
}

2.3 與ViewPager繫結

public void setmViewPager(ViewPager viewPager) {
    this.mViewPager = viewPager;
    if (mViewPager.getAdapter() == null) {
        throw new NullPointerException();
    }
    // 該PagerIndicator實現了OnPageChangeListener介面,當ViewPager發生變化時,會回撥OnPageChangeListener相應的方法,通過這些方法,可以完成指示器相應的變化
    this.mViewPager.setOnPageChangeListener(this);
}

2.4 設定tab

public void setIndicators(List<String> indicators) {
    this.indicators.clear();
    this.indicators.addAll(indicators);
    linearLayout.removeAllViews();
    // 建立對應個數的UpDownView,並新增到linearLayout中去
    for (int i = 0; i < indicators.size(); i++) {
        UpDownView upDownView = new UpDownView(context);
        upDownView.setGravity(Gravity.CENTER);
        upDownView.setImageColor(colors[i % 3]);
        // 設定每個tab所在的位置
        upDownView.mIndex = i;
        upDownView.setText(indicators.get(i));
        // 為每個tab增加點選事件
        upDownView.setOnClickListener(onTadClickListener);

        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.MATCH_PARENT);
        linearLayout.addView(upDownView, params);
    }
    requestLayout();
}

2.5 tab點選事件

private OnClickListener onTadClickListener = new OnClickListener() {
    @Override
    public void onClick(View view) {
        tabClick = true;
        UpDownView upDownView = (UpDownView) view;
        // 獲取到tab所在的位置,呼叫ViewPager的setCurrentItem切換頁面
        mViewPager.setCurrentItem(upDownView.mIndex);
    }
};

2.6 onPageScrolled的回撥(即滑動ViewPager產生的變化處理):該方法有三個引數,通過這三個引數的變化,改變每個tab對應的變化即可;這裡有個學習點,就是顏色的變化可以通過ArgbEvaluator這個類實現

leftDownView = (UpDownView) linearLayout.getChildAt(arg0);
rightDownView = (UpDownView) linearLayout.getChildAt(arg0 + 1);
// 色條變化
leftDownView.setTranslation(arg1 * leftDownView.height * 0.8f);
rightDownView.setTranslation((1 - arg1) * rightDownView.height
        * 0.8f);
// 文字顏色變化
int leftColor = (Integer) new ArgbEvaluator().evaluate(arg1,
        Color.WHITE, Color.BLACK);
leftDownView.setTextColor(leftColor);
int rightColor = (Integer) new ArgbEvaluator().evaluate(1 - arg1,
        Color.WHITE, Color.BLACK);
rightDownView.setTextColor(rightColor);

2.7 自動滑動到中間位置

// 當某一項被選中時會回撥onPageSelected方法,在這裡我們可以讓該PagerIndicator自動滾動:這裡主要涉及到移動距離的計算
public void changePager(final int last) {
    final UpDownView lastChild = (UpDownView) linearLayout.getChildAt(last);
    int[] location = new int[2];
    lastChild.getLocationOnScreen(location);
    final int scrollPos = location[0]
            - (getMeasuredWidth() - lastChild.getMeasuredWidth()) / 2;
    smoothScroll(scrollPos, new AnimatorListenerAdapter() {
    });

}

3 外邊呼叫OnPageChangeListener問題

由於該PagerIndicator需要根據OnPageChangeListener的回撥變化,所以需要在PagerIndicator中實現該介面,並設定給ViewPager。這就產生個問題,假如專案在使用ViewPager時也需要根據OnPageChangeListener的回撥進行某些處理該怎麼處理。這個問題可以這樣子處理

3.1 在專案中,不呼叫ViewPager.setOnPageChangeListener,而是PagerIndicator.setOnPageChangeListener,即將回調設定給PagerIndicator,這樣就可以正常監聽該介面了

3.2 看內部處理

// 將傳遞進來的OnPageChangeListener物件儲存到onPageChangeListener中
private OnPageChangeListener onPageChangeListener;

public void setOnPageChangeListener(
        OnPageChangeListener onPageChangeListener) {
    this.onPageChangeListener = onPageChangeListener;
}
// 當PagerIndicator中的onPageScrollStateChanged方法被呼叫時,我們手動呼叫傳進來的onPageChangeListener的onPageScrollStateChanged(arg0)方法,其他回撥方法一樣
@Override
public void onPageScrollStateChanged(int arg0) {
    if (mViewPager.getAdapter().getCount() != indicators.size()) {
        return;
    }
    onPageChangeListener.onPageScrollStateChanged(arg0);
}

總結

至此,一個仿微博頭條效果的PagerIndicator就實現了。當然我們可以自定義一些屬性,例如文字顏色和大小,ImageView的顏色等,用於設定想要的一些顯示。提高該View的靈活性