說到 ViewPager 指示器忘晤,想必大家都不陌生宛蚓,絕大部分應(yīng)用中都有這個。使用頻率非常之高设塔。但系統(tǒng)對它的支持并不好凄吏,自帶的 PagerTabStrip 和 PagerTitleStrip 太弱,很難滿足需求闰蛔。當然也有第三方框架諸如 Jake Wharton 大神的 ViewPagerIndicator , PagerSlidingTabStrip 等痕钢,我曾經(jīng)嘗試著使用它們,但還是被它們的可定制能力給嚇退了序六。
背景
近期交互改版任连,需要在指示器上增加吸附效果,剛開始我有點懵逼例诀,因為之前的指示器只是簡單的使用了 HorizontalScrollView + 橫向 LinearLayout 随抠,向 LinearLayout 里面添加一些 TextView 當做標題,選中的時候只是簡單的改變 TextView 的顏色繁涂,沒有任何動畫拱她,因此實現(xiàn)起來相對簡單(項目前期時間緊迫)。這估計也是大部分應(yīng)用的做法吧扔罪。
考慮到后面如果交互再改版秉沼,那我又會懵逼了,所以干脆自己來打造一個可擴展矿酵、可定制能力 很強 真TM強 的 ViewPager 指示器框架 —— MagicIndicator 唬复。
關(guān)于命名
之所以叫 MagicIndicator,是因為 鴻神 之前搞了一個 MagicViewPager全肮, 我覺得這兩個可以很好的搭配使用盅抚,并且正如大家看到的,它確實比較 Magic倔矾。
如何使用
這期就不打算給大家講原理性文章了妄均,只講如何集成(主要是這兩天都在寫這個柱锹,被媳婦罵慘了,沒時間寫了)丰包,后面我會有一個系列的文章來講這個禁熏,涉及到的內(nèi)容大概有:
MagicIndicator 原理
如何擴展 MagicIndicator 打造任意的切換效果
如何使用 MagicIndicator 來打造主流應(yīng)用(諸如微信主頁的切換)的指示器效果
好,我們開始邑彪。
首先瞧毙,你需要從我的 Github 上將工程代碼 check 下來,直接導(dǎo)入即可運行寄症,里面包含源碼和 demo宙彪。工程中有個 Module 叫做 magicindicator,直接拷到你的項目中即可有巧。包結(jié)構(gòu)如下:
不要忘了添加依賴哦:
dependencies {
compile project(':magicindicator')
}
導(dǎo)入成功后释漆,就可以使用啦。你需要先在布局文件里引入 MagicIndicator:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="net.lucode.hackware.magicindicatordemo.MainActivity">
<net.lucode.hackware.magicindicator.MagicIndicator
android:id="@+id/magic_indicator"
android:layout_width="match_parent"
android:layout_height="@dimen/navigator_common_height"
android:background="#d43d3d" />
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/white" />
</LinearLayout>
再在代碼里面找到它篮迎,并進行簡單設(shè)置:
final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mDataList == null ? 0 : mDataList.size();
}
@Override
public IPagerTitleView getItemView(Context context, final int index) {
ClipPagerTitleView clipPagerTitleView = new ClipPagerTitleView(context);
clipPagerTitleView.setText(mDataList.get(index));
clipPagerTitleView.setTextColor(Color.parseColor("#f2c4c4"));
clipPagerTitleView.setClipColor(Color.WHITE);
clipPagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(index);
}
});
return clipPagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
return null; // 沒有指示器男图,因為title的指示作用已經(jīng)很明顯了
}
});
magicIndicator.setNavigator(commonNavigator);
這樣,你就可以輕松的實現(xiàn)效果圖中 今日頭條 式(效果圖中第一個)切換效果甜橱。
額逊笆,上面這代碼明顯沒有和 ViewPager 相關(guān)聯(lián),因此滑動 ViewPager 時是看不到切換效果的岂傲,我們給 ViewPager 添加一個監(jiān)聽器:
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
magicIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
magicIndicator.onPageSelected(position);
}
@Override
public void onPageScrollStateChanged(int state) {
magicIndicator.onPageScrollStateChanged(state);
}
});
mPager.setCurrentItem(1);
這樣难裆, MagicIndicator 就算成功引入項目啦。
- 注意镊掖,以上代碼明顯沒有和 ViewPager 強關(guān)聯(lián)乃戈,因此在不使用 ViewPager 的情況下,仍然可以使用 MagicIndicator堰乔。只是需要你手動調(diào)用 onPageXXX 系列方法回調(diào)即可偏化。當然,后續(xù)我會提供幫助類來簡化這個過程镐侯。
- 等 MagicIndicator 基本穩(wěn)定侦讨、成型后,我會把它提交到 MavenCenter 和 JCenter 中苟翻,方便大家使用韵卤。
內(nèi)建的指示器
MagicIndicator 目前內(nèi)建了好幾種指示器,基本可以滿足絕大部分需求崇猫,并且每一種指示器都支持通過 插值器(Interpolator) 來微調(diào)效果(如果你還不會 巧用插值器沈条,可以參考我的前一篇博文 Android水波紋特效的簡單實現(xiàn) ),后面我還會不定期的往里面添加更多炫酷的效果诅炉,敬請期待蜡歹。下面演示一下使用內(nèi)建的指示器實現(xiàn)效果圖中的 小尖角 式切換效果:
final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAlwaysScrollToCenter(true);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mDataList == null ? 0 : mDataList.size();
}
@Override
public IPagerTitleView getItemView(Context context, final int index) {
SimplePagerTitleView simplePagerTitleView = new SimplePagerTitleView(context);
simplePagerTitleView.setText(mDataList.get(index));
simplePagerTitleView.setNormalColor(Color.parseColor("#333333"));
simplePagerTitleView.setSelectedColor(Color.parseColor("#e94220"));
simplePagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(index);
}
});
return simplePagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
TriangularPagerIndicator indicator = new TriangularPagerIndicator(context);
indicator.setLineColor(Color.parseColor("#e94220"));
return indicator;
}
});
magicIndicator.setNavigator(commonNavigator);
看到?jīng)]有屋厘,如果你想換一個效果,只需在 getItemView 里返回不同的指示器標題(IPagerTitleView)月而,在 getIndicator 里返回不同的指示器(IPagerIndicator)就可以啦汗洒。
如何擴展
當內(nèi)建的指示器不能滿足你的需求時,你可以輕易的擴展父款,如果你的需求貌似可以使用 HorizontalScrollView + 橫向 LinearLayout 方式實現(xiàn)溢谤,建議繼承 IPagerTitleView 和 IPagerIndicator 即可,如果不行憨攒,那就完全自定義吧世杀,繼承 **IPagerNavigator ** (導(dǎo)航器 —— 我覺得上面的那些效果本質(zhì)是一個導(dǎo)航器,指示器只是包含在導(dǎo)航器中的一個元素而已) 吧肝集。效果圖中的最后一個效果就是如此:
final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
final CircleNavigator circleNavigator = new CircleNavigator(this);
circleNavigator.setCount(mDataList.size());
circleNavigator.setCircleColor(Color.RED);
magicIndicator.setNavigator(circleNavigator);
當然瞻坝,我可不希望每當內(nèi)置指示器達不到你的需求時,你總是去繼承這些個類包晰,為了簡化湿镀,我在最新的代碼里增加了 CommonPagerTitleView炕吸,使用方法如下:
final MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
CommonNavigator commonNavigator = new CommonNavigator(this);
commonNavigator.setAdapter(new CommonNavigatorAdapter() {
@Override
public int getCount() {
return mDataList == null ? 0 : mDataList.size();
}
@Override
public IPagerTitleView getItemView(Context context, final int index) {
CommonPagerTitleView commonPagerTitleView = new CommonPagerTitleView(MainActivity.this);
commonPagerTitleView.setContentView(R.layout.simple_pager_title_layout);
// 初始化
final ImageView titleImg = (ImageView) commonPagerTitleView.findViewById(R.id.title_img);
titleImg.setImageResource(R.mipmap.ic_launcher);
final TextView titleText = (TextView) commonPagerTitleView.findViewById(R.id.title_text);
titleText.setText(mDataList.get(index));
commonPagerTitleView.setOnPagerTitleChangeListener(new CommonPagerTitleView.OnPagerTitleChangeListener() {
@Override
public void onSelected(int index) {
titleText.setTextColor(Color.RED);
}
@Override
public void onDeselected(int index) {
titleText.setTextColor(Color.BLACK);
}
@Override
public void onLeave(int index, float leavePercent, boolean leftToRight) {
titleImg.setScaleX(1.3f + (0.8f - 1.3f) * leavePercent);
titleImg.setScaleY(1.3f + (0.8f - 1.3f) * leavePercent);
}
@Override
public void onEnter(int index, float enterPercent, boolean leftToRight) {
titleImg.setScaleX(0.8f + (1.3f - 0.8f) * enterPercent);
titleImg.setScaleY(0.8f + (1.3f - 0.8f) * enterPercent);
}
});
commonPagerTitleView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPager.setCurrentItem(index);
}
});
return commonPagerTitleView;
}
@Override
public IPagerIndicator getIndicator(Context context) {
return null;
}
});
magicIndicator.setNavigator(commonNavigator);
效果圖:
關(guān)于擴展 MagicIndicator伐憾,后面會有詳細的博文來講解,敬請留意赫模。如果你擴展 MagicIndicator 實現(xiàn)了很炫酷的指示器效果树肃,歡迎共享出來。
更多
MagicIndicator 同樣考慮到了 ViewPager 內(nèi)容變化的情況瀑罗,當你的 ViewPager 內(nèi)容發(fā)生變化時胸嘴,除了調(diào)用 PagerAdapter.notifyDataSetChanged ,還記得先調(diào)用 IPagerNavigator.notifyDataSetChanged 哦:
mDataList.clear();
mDataList.add("歡迎關(guān)注");
mDataList.add("我的博客");
mDataList.add("hackware.lucode.net");
commonNavigator.notifyDataSetChanged();
mAdapter.notifyDataSetChanged();
結(jié)語
如果大家覺得 MagicIndicator 很好斩祭,對你有幫助劣像,歡迎多多 star + fork,關(guān)注摧玫!關(guān)注耳奕!,也請幫忙推廣一下哈诬像。感激不盡屋群。
MagicIndicator 是我計劃長期維護的項目,由于才剛開始坏挠,現(xiàn)在的指示器效果還不是很多芍躏,后續(xù)會不斷加入更多炫酷的效果,如果你想加入我降狠,打造更好的用戶體驗对竣,私信我吧庇楞。
MagicIndicator 疑難解答,交流請加QQ群:373360748否纬。
今天就到此為止吧姐刁。周末愉快!