一:概述
這個教程好像網上挺多的。viewpager用fragment作為每一個item,但是似乎好像indicator似乎都有點不太滿意,下面的滑動條是直接跳動的,不是類似動畫滑動的。現在就說說這個滑動條。然後再說說其他的東西(比如activity傳資料給fragment啊。這些)。
先看看效果圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISN1ADMwAjMxIDMzETM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
二:源碼解讀
我們先講講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并沒有滾動過去。就看不見了。
看下圖:
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(修改)情人節這天回來改代碼——————————
做了一個小修改,當頂部的标簽頭部不足以撐滿螢幕寬度的時候,那麼幾個标簽就平分螢幕的寬度。
@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() 等。
源碼下載下傳
加個好友共同學習(不是公衆号):
因為小弟水準有限,如果有寫的有問題,希望指出。