天天看點

ViewPager+Indicator(如何寫indicator)

一:概述

這個教程好像網上挺多的。viewpager用fragment作為每一個item,但是似乎好像indicator似乎都有點不太滿意,下面的滑動條是直接跳動的,不是類似動畫滑動的。現在就說說這個滑動條。然後再說說其他的東西(比如activity傳資料給fragment啊。這些)。

先看看效果圖:

ViewPager+Indicator(如何寫indicator)

二:源碼解讀

我們先講講viewpager吧。

private MyPageAdapter adapter ;
private ViewPager pager;
private List<MyFragment> listFragment ;
...
//這裡初始化個fragment
  for(int i =  ;i <  ;i ++){
            Bundle bundle = new Bundle();
            bundle.putString("who","hello world "+ i);
            MyFragment fragment = MyFragment.getInstance(bundle);
            listFragment.add(fragment);
  }
           

通過activity傳資料給fragment,直接調用fr.setArguments(bundle);就可以了。bundle就是資料集,然後在fragment中直接調用Bundle bundle = getArguments();就可以獲得了。

public static MyFragment getInstance(Bundle bundle){
        MyFragment fr = new MyFragment();
        fr.setArguments(bundle);
        return fr;
 }
           

然後為viewpager添加擴充卡。注意其中的getPageTitle()方法,這個方法的傳回值就可以作為indicator中的值。

class MyPageAdapter extends FragmentPagerAdapter {

        public MyPageAdapter(FragmentManager fm) {
            super(fm);
        }
        @Override
        public Fragment getItem(int position) {
            return listFragment.get(position);
        }

        @Override
        public long getItemId(int position) {
            return super.getItemId(position);
        }

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

        @Override
        public CharSequence getPageTitle(int position) {
            return "title - "+position;
        }
    }
           

然後看看TestIndicatorActivity這個activity實作了MyIndicator.CustomeTextViewInter 接口

主要實作了兩個方法,用來讓開發者可以直接在代碼中自定義indicator,被選擇或者沒有被選擇的效果

selected():被選擇的效果。

unSelect():沒有被選擇的效果。

兩個都隻是簡單的修改了下背景顔色而已。(你們可以實作自己想實作的,比如加粗等等。)

@Override
    public TextView selected(TextView tv) {
        tv.setBackgroundColor(Color.parseColor("#8FFF58"));
        return tv;
    }

    @Override
    public TextView unSelect(TextView tv) {
        tv.setBackgroundColor(Color.parseColor("#FFFFFF"));
        return tv;
    }
           

我們隻需要寫下兩句代碼就可以使用indicator。

//自定義被選擇,沒被選擇的接口
indicator.setCustomeTextViewInter(this);
...
indicator.setViewPager(pager);
           

好了我們詳細說說indicator的實作方法。

我寫了個attr檔案(好像叫自定義屬性檔案)

<resources>
    <declare-styleable name="MyIndicator">
        <attr name="lineHeight" format="dimension"/>
        <attr name="lineColor" format="color"/>
    </declare-styleable>
</resources>
           

然後在布局檔案中調用如下就可以

記得在Android studio 布局檔案的根元素中加入

xmlns:custom=”http://schemas.android.com/apk/res-auto”

<com.example.tongmin.testproject.MyIndicator
        android:layout_width="match_parent"
        android:layout_height="50dp"
        custom:lineColor="#5680FF"
        custom:lineHeight="5dp"
        android:id="@+id/myindicator"
  />
           

使用下面的代碼就可以讀取其中的資料了。

TypedArray a = context.getTheme()
 .obtainStyledAttributes(attrs, R.styleable.MyIndicator, defStyleAttr, );

 lineHeight = a.getDimension(R.styleable.MyIndicator_lineHeight, );
 lineColor = a.getColor(R.styleable.MyIndicator_lineColor, Color.BLACK);
 a.recycle();
           

然後整個indicator時繼承自 HorizontalScrollView ,因為都是橫着滑動的。

然後看看下面代碼。注釋也看看

//首先将自帶的橫向的滑動條禁用掉。
this.setHorizontalScrollBarEnabled(false);
//因為scrollview隻能有一個子元素,就添加個LinearLayout
linearContain = new LinearLayout(context);
linearContain.setOrientation(LinearLayout.HORIZONTAL);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
this.addView(linearContain, params);
           

然後看看setViewPager()這個方法,将viewpager設定進來後做了一些初始化工作。

setheader就是将有多少個fragment,然後就初始化多少個indicator的item。

public void setViewPager(ViewPager viewPager) {
        this.viewPager = viewPager;
        this.viewPager.addOnPageChangeListener(this);
        setHeader();
        changeTextView();
 }
           

這個就是添加一個個item的具體的方法。其中添加的其實是textview。

因為我們放出了一個接口,讓使用者自己實作選擇,沒被選擇的textview。是以需要做一下判斷。

private void addTextView(String text , int position) {
        //判斷使用者是否自定義了textview
        TextView textView = new Text(context,position);
        if (customeTextViewInter != null) {
            textView = customeTextViewInter.unSelect(textView);
            if (textView == null) {
                throw new RuntimeException("customeTextViewInter textview == null");
            }
        }
        textView.setText(text);
        linearContain.addView(textView);
 }
           

其中Text是自定義的TextView,其中的position屬性是記錄我這個textView是第幾個item,因為要添加點選事件,點選跳轉到對應fragment。

然後注意還寫了params.bottomMargin = (int) lineHeight;

這個是為了下面的滑動條騰出空間來。不然把他遮擋了。

private class Text extends TextView {
    int position;
    public Text(Context context , int position) {
         super(context);
         this.position = position;
         this.init(context);
    }
    private void init(Context context) {

      LinearLayout.LayoutParams params = 
        new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,            ViewGroup.LayoutParams.MATCH_PARENT);

            this.setLayoutParams(params);
            int size = dip2px(context, );
            params.bottomMargin = (int) lineHeight;
            this.setPadding(size, , size, );
            this.setGravity(Gravity.CENTER);
            this.setTextSize();
            this.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    viewPager.setCurrentItem(position);
                }
            });
        }
    }
           

好了。現在我們手動為這個viewpager設定一個ViewPager.OnPageChangeListener

監聽viewpager的滑動。

這個接口需要實作三個方法。

1.onPageScrolled()是在滑動的時候回調的一個接口。

a).position是在滑動的時候所在的position(比如我在第一個fragment滑動到第二個fragment的時候這個position就是0,從0開始的啊。)
      b).positionOffset就是目前的滑動在整個viewpager的比例。
      c).positionOffsetPixels。就是這個fragment滑動的總像素數。
           

2.onPageSelected()是在滑動到被選擇的fragment回調的。

a).position就是被選擇的position。(從第一個滑向第二個,這個position就指向第二個)。
           
@Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        changeLinePosition(position, positionOffset);
    }
    @Override
    public void onPageSelected(int position) {
        changeTextView(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }
           

我們觸發滑動條滑動時間就是在onPageScrolled()中,然後修改textview的樣式的時在onPageSelected();

我們看看changeLinePosition();方法

首先找到對應的textview然後算出他的寬度。

currentPosition 就是目前滑動條所在的 X 坐标。

this.scrollTo((int) currentPosition - scroll, 0);

是讓目前HorizontalScrollView滑動到指定位置。不然會出現像滑動條已經到最右邊了。但是scrollview并沒有滾動過去。就看不見了。

看下圖:

ViewPager+Indicator(如何寫indicator)
private void changeLinePosition(int position, float offset) {
        int left = linearContain.getChildAt(position).getLeft();
        int right = linearContain.getChildAt(position).getRight();
        lineWidth = right - left;
        offset = lineWidth * offset;
        currentPosition = left + offset;
        this.scrollTo((int) currentPosition - scroll, );
        invalidate();
 }
           

最後在ondraw()方法中,畫出下面的滑動條就可以了。找到自己的坐标就可以了。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int textHeight = linearContain.getHeight();
        canvas.drawRect(currentPosition, textHeight - lineHeight, 
          currentPosition + lineWidth, textHeight, linePaint);
    }
           

—————————————————-2.14(修改)情人節這天回來改代碼——————————

做了一個小修改,當頂部的标簽頭部不足以撐滿螢幕寬度的時候,那麼幾個标簽就平分螢幕的寬度。

ViewPager+Indicator(如何寫indicator)
@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {


        LinearLayout contain = (LinearLayout) getChildAt();

        int childCount = contain.getChildCount();


        if (contain.getMeasuredWidth() < getMeasuredWidth()) {
            //寬度不夠,讓子控件平分寬度
            contain.setMinimumWidth(getMeasuredWidth());
            int childWidth = getMeasuredWidth() / childCount;
            for (int i = ; i < childCount; ++i) {
                View view = contain.getChildAt(i);
                view.setMinimumWidth(childWidth);
            }
        } else {
            super.onLayout(changed, l, t, r, b);
        }
    }
           

就做了一個簡單的判斷 contain.getMeasuredWidth() < getMeasuredWidth()。

當想修改一個控件的尺寸的時候可以使用兩個方法。

1.setLayoutParams().

但是這個方法會調用 requestLayout() ,如果我們在onLayout中調用的時候會

requestLayout() improperly called by…等。

2.直接調用控件的.setMinimumWidth(),setMinimumHight() 等。

源碼下載下傳

加個好友共同學習(不是公衆号):

ViewPager+Indicator(如何寫indicator)

因為小弟水準有限,如果有寫的有問題,希望指出。

繼續閱讀