2015-05-11 11:31:09 1639瀏覽
我記得在前面,我寫了一篇Android 微信6.1 tab欄圖標(biāo)和字體顏色漸變的實現(xiàn),如果大家僅僅認(rèn)為這篇文章的功能只是模仿微信顏色漸變效果,那就大錯特錯了!認(rèn)真閱讀了這篇文章的朋友,應(yīng)該知道,這里面代碼可用作 app 通用的底部欄導(dǎo)航,通過它能快速的實現(xiàn)類似微信6.0版本以底部導(dǎo)航的 app 整體架構(gòu),并且在 MainActivity 中需要編寫的代碼非常簡潔。如果有興趣的朋友可以去看看。
今天這篇 blog的內(nèi)容同樣可以拿來做 app 的整體架構(gòu),但與前面那篇 blog 不同,不同之處是前面那篇文章所講的內(nèi)容可用作底部導(dǎo)航,而這篇 blog 的內(nèi)容,是用作頂部導(dǎo)航,老版本的微信就是此效果,ok,來看看效果圖
根據(jù)效果圖,不難分析,可以通過自定義 ViewGroup 來實現(xiàn),但這樣代碼量偏多,看了我的前面 blog 的朋友應(yīng)該清楚,這里最好的實現(xiàn)方式是通過重寫 LinearLayout,相比通過 ViewGroup 來實現(xiàn),省略了測量onMeasure()和布局onLayout()方法的實現(xiàn),因為這些LinearLayout已經(jīng)幫我們實現(xiàn)好了,而我們真正要做的就是為 LinearLayout 填充內(nèi)容,填充內(nèi)容可大致可分為以下四個步驟:
1、填充每個 item 的內(nèi)容
2、繪制每個 item 之間的分割線
3、繪制底部線條
4、繪制指示器的內(nèi)容
1、先把需要用的屬性定義出來
需要的屬性
1、頁卡指示器的顏色
2、分割線的顏色
3、底部線條的顏色
4、頁卡指示器的高度
5、分割線距離上下邊距的距離
6、分割線的寬度
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="indicatorColor" format="color"/>
<attr name="dividerColor" format="color"/>
<attr name="bottomLineColor" format="color"/>
<attr name="dividerMargin" format="dimension"/>
<attr name="indicatorHeight" format="dimension"/>
<attr name="bottomLineHeight" format="dimension"/>
<attr name="dividerWidth" format="dimension"/>
<declare-styleable name="SlidingTabLayout">
<attr name="indicatorColor"/>
<attr name="dividerColor"/>
<attr name="bottomLineColor"/>
<attr name="dividerMargin"/>
<attr name="indicatorHeight"/>
<attr name="bottomLineHeight"/>
<attr name="dividerWidth"/>
</declare-styleable>
</resources>
2、代碼中獲取屬性,并附上相應(yīng)的默認(rèn)值
/*默認(rèn)的頁卡顏色*/
private final int DEFAULT_INDICATOR_COLOR = 0xffff00ff;
/*默認(rèn)分割線的顏色*/
private final int DEFAULT_DIVIDER_COLOR = 0xff000000;
/*默認(rèn)title字體的大小*/
private final int DEFAULT_TEXT_SIZE = 16;
/*默認(rèn)padding*/
private final int DEFAULT_TEXT_PADDING = 16;
/*divider默認(rèn)的寬度*/
private final int DEFAULT_DIVIDER_WIDTH = 1;
/*indicator 的高度*/
private final int DEFAULT_INDICATOR_HEIGHT = 5;
/*底部線條的高度默認(rèn)值*/
private final int DEFAULT_BOTTOM_LINE_HEIGHT = 2;
/*分割線距離上下邊緣的距離默認(rèn)為8*/
private final int DEFAULT_DIVIDER_MARGIN = 8;
/*底部線條的顏色默認(rèn)值*/
private final int DEFAULT_BOTTOM_LINE_COLOR = 0xff000000;
/*獲取TypedArray*/
TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.SlidingTabLayout);
/*獲取自定義屬性的個數(shù)*/
int N = typedArray.getIndexCount();
Log.v("zgy","=========getIndexCount========="+N) ;
for (int i = 0; i < N; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.SlidingTabLayout_indicatorColor:
/*獲取頁卡顏色值*/
mIndicatorColor = typedArray.getColor(attr, DEFAULT_INDICATOR_COLOR);
break;
case R.styleable.SlidingTabLayout_dividerColor:
/*獲取分割線顏色的值*/
mDividerColor = typedArray.getColor(attr, DEFAULT_DIVIDER_COLOR);
break;
case R.styleable.SlidingTabLayout_bottomLineColor:
/*獲取底部線條顏色的值*/
mBottomLineColor = typedArray.getColor(attr, DEFAULT_BOTTOM_LINE_COLOR);
break;
case R.styleable.SlidingTabLayout_dividerMargin:
/*獲取分割線的距離上線邊距的距離*/
mDividerMargin = (int) typedArray.getDimension(attr, DEFAULT_DIVIDER_MARGIN * getResources().getDisplayMetrics().density);
Log.v("zgy","=========mDividerMargin========="+mDividerMargin) ;
break;
case R.styleable.SlidingTabLayout_indicatorHeight:
/*獲取頁卡的高度*/
mIndicatorHeight = (int) typedArray.getDimension(attr, DEFAULT_INDICATOR_HEIGHT * getResources().getDisplayMetrics().density);
break;
case R.styleable.SlidingTabLayout_bottomLineHeight:
/*獲取底部線條的高度*/
mBottomLineHeight = (int) typedArray.getDimension(attr, DEFAULT_BOTTOM_LINE_HEIGHT * getResources().getDisplayMetrics().density);
break;
case R.styleable.SlidingTabLayout_dividerWidth:
/*獲取分割線的寬度*/
mDividerWidth = (int) typedArray.getDimension(attr, DEFAULT_DIVIDER_WIDTH * getResources().getDisplayMetrics().density);
break;
}
}
/*釋放TypedArray*/
typedArray.recycle();
這里說一個細(xì)節(jié),我經(jīng)常在這里獲取屬性的時候習(xí)慣性的把
case R.styleable.SlidingTabLayout_indicatorColor:
寫成
case R.attr.indicatorColor:
而且這里不會報錯,不過結(jié)果可想而知,無法獲取我們設(shè)置的屬性內(nèi)容,這里如果有跟我犯同樣錯誤的朋友記得注意一下。
3、為 LinearLayout 填充內(nèi)容
/**
* 設(shè)置viewPager,初始化SlidingTab,
* 在這個方法中為SlidingLayout設(shè)置
* 內(nèi)容,
*
* @param viewPager
*/
public void setViewPager(ViewPager viewPager) {
/*先移除所以已經(jīng)填充的內(nèi)容*/
removeAllViews();
/* viewPager 不能為空*/
if (viewPager == null) {
throw new RuntimeException("ViewPager不能為空");
}
mViewPager = viewPager;
mViewPager.setOnPageChangeListener(new InternalViewPagerChange());
//填充內(nèi)容
populateTabLayout();
}
/**
* 填充layout,設(shè)置其內(nèi)容
*/
private void populateTabLayout() {
final PagerAdapter adapter = mViewPager.getAdapter();
final OnClickListener tabOnClickListener = new TabOnClickListener();
mItemName = (TabItemName) adapter;
for (int i = 0; i < adapter.getCount(); i++) {
TextView textView = createDefaultTabView(getContext());
textView.setOnClickListener(tabOnClickListener);
textView.setText(mItemName.getTabName(i));
addView(textView);
}
}
根據(jù)ViewPager中頁面的個數(shù),填充相應(yīng)的 tab。
4、繪制相應(yīng)的內(nèi)容
繪制內(nèi)容,肯定在 onDraw()方法中
繪制底部線條
canvas.drawRect(0,height - mBottomLineHeight,getWidth(),height,mBottomPaint);
繪制分割線
for (int i = 0; i < getChildCount() - 1; i++) {
View child = getChildAt(i);
canvas.drawLine(child.getRight(), mDividerMargin,child.getRight(), height - mDividerMargin,mDividerPaint);
}
重點(diǎn):繪制滑動頁卡
/*當(dāng)前頁面的View tab*/
View selectView = getChildAt(mSelectedPosition);
/*計算開始繪制的位置*/
int left = selectView.getLeft();
/*計算結(jié)束繪制的位置*/
int right = selectView.getRight();
if (mSelectionOffset > 0) {
View nextView = getChildAt(mSelectedPosition + 1);
/*如果有偏移量,重新計算開始繪制的位置*/
left = (int) (mSelectionOffset * nextView.getLeft() + (1.0f - mSelectionOffset) * left);
/*如果有偏移量,重新計算結(jié)束繪制的位置*/
right = (int) (mSelectionOffset * nextView.getRight() + (1.0f - mSelectionOffset) * right);
}
/*繪制滑動的頁卡*/
canvas.drawRect(left, height - mIndicatorHeight, right, height, mIndicatorPaint);
為了體現(xiàn)他的簡潔之處,這里把 xml 中的代碼也貼出來
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:zgy="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<demo.slidingtablayout.view.SlidingTabLayout
android:id="@+id/id_tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
zgy:bottomLineColor="#AEAEAE"
zgy:dividerMargin="15dp"
zgy:indicatorColor="#77e69c"
zgy:indicatorHeight="5dp"
zgy:bottomLineHeight="2dp"
android:background="#eeeeee">
</demo.slidingtablayout.view.SlidingTabLayout>
<android.support.v4.view.ViewPager
android:layout_below="@+id/id_tab"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/id_view_pager"/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends ActionBarActivity {
/*viewPager*/
private ViewPager mViewPager ;
/*自定義的 tabLayout*/
private SlidingTabLayout mTabLayout ;
/*每個 tab 的 item*/
private List<PagerItem> mTab = new ArrayList<>() ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.id_view_pager) ;
mTabLayout = (SlidingTabLayout) findViewById(R.id.id_tab) ;
mTab.add(new PagerItem("tab1","FirstPager")) ;
mTab.add(new PagerItem("tab2","SecondPager")) ;
mTab.add(new PagerItem("tab3","ThirdPager")) ;
mTab.add(new PagerItem("tab4","FourthPager")) ;
mViewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
/*需要先為 viewpager 設(shè)置 adapter*/
mTabLayout.setViewPager(mViewPager);
}
private class ViewPagerAdapter extends FragmentPagerAdapter implements SlidingTabLayout.TabItemName{
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return mTab.get(position).createFragment();
}
@Override
public int getCount() {
return mTab.size();
}
@Override
public String getTabName(int position) {
return mTab.get(position).getTitle();
}
}
}