国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android 底部導航控件實例代碼

2020-01-02 06:59:32
字體:
來源:轉載
供稿:網友

一、先給大家展示下最終效果

通過以上可以看到,圖一是簡單的使用,圖二、圖三中為結合ViewPager共同使用,而且都可以隨ViewPager的滑動漸變色,不同點是圖二為選中非選中兩張圖片,圖三的選中非選中是一張圖片只是做了顏色變化。

二、 需求

我們希望做可以做成這樣的,可以在xml布局中引入控件并綁定數據,在代碼中設置監聽回調,并且配置使用要非常簡單!

三、需求分析

根據我們多年做不明確需求項目的經驗,以上需求還算明確。那么我們可以采用在LinearLayout添加子View控件,這個子View控件就是我們自定義的每個tab條目,當然對LinearLayout要設置權重。

需求大致明確之后就先設計每個條目的子View控件,這個子View控件是一個可以切換狀態變化的,一張、兩張都可以切換狀態(參考圖一、圖三)。那么這個View要可以設置底部顯示的文字,設置選中時顏色、未選中時顏色、選中時圖片、未選中時圖片、文字大小、設置是否有指示點、設置指示點大小、設置指示點圖片等等。

四、Tab條目接口

通過需求分析,我們可以定義如下的Tab子View操作接口:

仔細的朋友會發現,為什么在接口中沒有設置選中圖片以及設置非選中時圖片,那是因為這個屬性不是通用的,在不同的實現中再去定義。

五、Tab條目實現

類的繼承關系及說明:

類圖如下所示:

在TabViewBase中主要的方法就是測量,其他的都是對接口的簡單實現。

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 得到繪制icon的寬 int bitmapWidth = Math.min(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - mTextBound.height()); int left = getMeasuredWidth() / 2 - bitmapWidth / 2; int top = (getMeasuredHeight() - mTextBound.height()) / 2 - bitmapWidth / 2; // 設置icon的繪制范圍 mIconRect = new Rect(left, top, left + bitmapWidth, top + bitmapWidth); // 設置指示點的范圍 int indicatorRadius = mIndicatorSize / 2; int tabRealHeight = bitmapWidth + mTextBound.height(); mIndicatorRect = new Rect(left + tabRealHeight* 4/5 - indicatorRadius, top, left+tabRealHeight* 4/5 + indicatorRadius, top + mIndicatorSize); } 

在以上代碼中可以看到,測量文字的高度,用控件的高度減去文字的高度和控件的寬度對比,取較小的為圖片的大小,也就是設置的圖片要為正方形,否則會產生變形。

看下普通兩張圖片切換的TabView的繪制:

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); setupTargetBitmap(canvas); drawIndicator(canvas); if(null != mText) { drawTargetText(canvas); } } /** * 繪制圖標圖片 * @param canvas */ private void setupTargetBitmap(Canvas canvas) { canvas.drawBitmap(isSelected ? mSelectedIconBitmap : mUnselectedIconBitmap, null, mIconRect, null); } /** * 繪制指示點 * @param canvas */ protected void drawIndicator(Canvas canvas) { if(isIndicateDisplay) { canvas.drawBitmap(mIndicatorBitmap, null, mIndicatorRect, null); } } /** * 繪制文字 * @param canvas */ protected void drawTargetText(Canvas canvas) { mTextPaint.setColor(isSelected ? mSelectedColor : mUnselectedColor); canvas.drawText(mText, mIconRect.left + mIconRect.width() / 2 - mTextBound.width() / 2, mIconRect.bottom + mTextBound.height(), mTextPaint); }

可以看到非常的簡單,就是繪制圖標圖片以及繪制指示點,在繪制圖標圖片時判斷當前條目是否在選中狀態,根據是否選中來繪制不同的圖片,在繪制指示點的時候首先判斷下是否設置了顯示指示點。如果有底部文字,那么久繪制底部文字。
在ViewPager兩張圖片圖片的時,我們再把效果圖拿過來觀察下:

這里有兩種模式,即隨著ViewPager的滾動圖標漸變及普通變化。OK,了解之后我們就能很輕松的來編寫它的繪制了,可以通過繪制兩張圖片,但是在繪制的時候控制它的透明度就可以啦,是不是也很簡單。

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int alpha = (int) Math.ceil((255 * mAlpha)); drawSourceBitmap(canvas, alpha); drawTargetBitmap(canvas, alpha); if(null != mText) { drawSourceText(canvas, alpha); drawTargetText(canvas, alpha); } drawIndicator(canvas); } /** * 繪制未選中圖標 * @param canvas * @param alpha */ private void drawSourceBitmap(Canvas canvas, int alpha) { mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setAlpha(255 - alpha); canvas.drawBitmap(mUnselectedIconBitmap, null, mIconRect, mPaint); } /** * 繪制選中圖標 * @param canvas * @param alpha */ private void drawTargetBitmap(Canvas canvas, int alpha) { mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setAlpha(alpha); canvas.drawBitmap(mSelectedIconBitmap, null, mIconRect, mPaint); } /** * 畫未選中文字 * @param canvas * @param alpha */ private void drawSourceText(Canvas canvas, int alpha) { mTextPaint.setTextSize(mTextSize); mTextPaint.setColor(mUnselectedColor); mTextPaint.setAlpha(255 - alpha); canvas.drawText(mText, mIconRect.left + mIconRect.width() / 2 - mTextBound.width() / 2, mIconRect.bottom + mTextBound.height(), mTextPaint); } /** * 畫選中文字 * @param canvas * @param alpha */ private void drawTargetText(Canvas canvas, int alpha) { mTextPaint.setColor(mSelectedColor); mTextPaint.setAlpha(alpha); canvas.drawText(mText, mIconRect.left + mIconRect.width() / 2 - mTextBound.width() / 2, mIconRect.bottom + mTextBound.height(), mTextPaint); }

代碼中的mAlpha是ViewPager滾動的百分比,然后分別繪制選中以及未選中的圖標和文本,但是繪制的時候設置的透明度不同,這樣就會有一個漸變的效果。

在ViewPager單張圖片圖片的時,我們再把效果圖拿過來觀察下:

private void setupTargetBitmap(int alpha) { mBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Config.ARGB_8888); mCanvas = new Canvas(mBitmap); mPaint = new Paint(); mPaint.setColor(mSelectedColor); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setAlpha(alpha); mCanvas.drawRect(mIconRect, mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setAlpha(255); mCanvas.drawBitmap(mIconBitmap, null, mIconRect, mPaint); } private void drawSourceText(Canvas canvas, int alpha) { mTextPaint.setTextSize(mTextSize); mTextPaint.setColor(mUnselectedColor); mTextPaint.setAlpha(255 - alpha); canvas.drawText(mText, mIconRect.left + mIconRect.width() / 2 - mTextBound.width() / 2, mIconRect.bottom + mTextBound.height(), mTextPaint); } private void drawTargetText(Canvas canvas, int alpha) { mTextPaint.setColor(mSelectedColor); mTextPaint.setAlpha(alpha); canvas.drawText(mText, mIconRect.left + mIconRect.width() / 2 - mTextBound.width() / 2, mIconRect.bottom + mTextBound.height(), mTextPaint); }

繪制的過程大致與兩張圖片相同,不同點就是在繪制圖片的時候Paint設置 Xfermode,來控制顏色的漸變。

OK,Tab條目的自定義View搞定之后剩下的就簡單多了。

六、定義屬性

接下來就是封裝繼承自LinearLayout的整體控件,先來定義下屬性。

可以看到tabIcons為單張圖片漸變效果特殊的,tabSelectedIcons和tabUnselectedIcon為兩張圖標切換效果特殊的。

七、 控件編寫

由于三中樣式有公共的部分,我們進行積累抽取。類圖結構如下:

1. 構造函數初始化自定義屬性

在TabIndicatorBase中初始化自定義屬性

private void init(Context context, AttributeSet attrs) { setOrientation(LinearLayout.HORIZONTAL); setGravity(Gravity.CENTER); //Load defaults from resources final Resources res = getResources(); final int defaultSelectedColor = res.getColor(R.color.default_tab_view_selected_color); final int defaultUnselectedColor = res.getColor(R.color.default_tab_view_unselected_color); final float defaultTextSize = res.getDimension(R.dimen.default_tab_view_text_size); final float defaultTabPadding = res.getDimension(R.dimen.default_tab_view_padding); final float defaultIndicatorSize = res.getDimension(R.dimen.default_tab_view_indicator_size); // Styleables from XML TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabIndicator); // 讀取布局中,各個tab使用的文字 if (a.hasValue(R.styleable.TabIndicator_tabLabels)) { mLabels = a.getTextArray(R.styleable.TabIndicator_tabLabels); } mSelectedColor = a.getColor(R.styleable.TabIndicator_tabSelectedColor, defaultSelectedColor); mUnselectedColor = a.getColor(R.styleable.TabIndicator_tabUnselectedColor, defaultUnselectedColor); mTextSize = (int) a.getDimension(R.styleable.TabIndicator_tabTextSize, defaultTextSize); mIndicatorSize = (int) a.getDimension(R.styleable.TabIndicator_TabIndicatorSize, defaultIndicatorSize); mTabPadding = (int) a.getDimension(R.styleable.TabIndicator_tabItemPadding, defaultTabPadding); handleStyledAttributes(a); a.recycle(); initView(); }

由于有些屬性不是公共的,這里定義handleStyleAttributes(a)的抽象方法,在子類中去實現。

2. 初始化View

/** * 初始化控件 */ private void initView() { LayoutParams params = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); params.gravity = Gravity.CENTER; int size = getTabSize(); for (int i = 0; i < size; i++) { final int index = i; T tabItemView = createTabView(); tabItemView.setPadding(mTabPadding, mTabPadding, mTabPadding, mTabPadding); // 圖標及文字 if(null != mLabels) { tabItemView.setText(mLabels[index]); tabItemView.setTextSize(mTextSize); } tabItemView.setSelectedColor(mSelectedColor); tabItemView.setUnselectedColor(mUnselectedColor); tabItemView.setIndicatorSize(mIndicatorSize); setProperties(tabItemView, i); this.addView(tabItemView, params); tabItemView.setTag(index); // CheckedTextView設置索引作為tag,以便后續更改顏色、圖片等 mCheckedList.add(tabItemView); // 將CheckedTextView添加到list中,便于操作 tabItemView.setOnClickListener(new OnClickListener() @Override public void onClick(View v) { setTabsDisplay(index); // 設置底部圖片和文字的顯示 if (null != mTabListener) { mTabListener.onTabSelected(index); // tab項被選中的回調事件 } } }); // 初始化 底部菜單選中狀態,默認第一個選中 if (i == 0) { tabItemView.setSelected(true); } else { tabItemView.setSelected(false); } } }

生成Tab條目以及設置特殊的屬性都通過抽象方法的方式交給子類去完成。

/** * 生成TabView * @return */ protected abstract T createTabView(); /** * 設置特殊屬性 * @param t */ protected abstract void setProperties(T t, int index); 

3. 子類實現

由于這里都比較簡單,我們選取其中一個簡單的雙圖標圖片來說明:

@Override protected void handleStyledAttributes(TypedArray a) { // 讀取布局中,各個tab使用的圖標 int selectedIconsResId = a.getResourceId(R.styleable.TabIndicator_tabSelectedIcons, 0); TypedArray ta = getContext().getResources().obtainTypedArray(selectedIconsResId); int len = ta.length(); mSelectedDrawableIds = new int[len]; for(int i = 0; i < len; i++) { mSelectedDrawableIds[i] = ta.getResourceId(i, 0); } int unselectedIconsResId = a.getResourceId(R.styleable.TabIndicator_tabUnselectedIcons, 0); ta = getContext().getResources().obtainTypedArray(unselectedIconsResId); len = ta.length(); mUnselectedDrawableIds = new int[len]; for(int i = 0; i < len; i++) { mUnselectedDrawableIds[i] = ta.getResourceId(i, 0); } ta.recycle(); }

這里讀取了xml中配置的選中及未選中圖標

生成TabView

@Override protected TabView createTabView() { return new TabView(getContext()); } 

設置特殊屬性

@Override protected void setProperties(TabView tabView, int index) { tabView.setSelectedIcon(mSelectedDrawableIds[index]); tabView.setUnselectedIcon(mUnselectedDrawableIds[index]); } 

獲取條目個數

@Override protected int getTabSize() { return mSelectedDrawableIds.length; }

八、源碼及示例

給大家提供一個github的地址: Android-TabIndicator
另外,歡迎 star or f**k me on github!

九、一行引入庫

如果您的項目使用 Gradle 構建, 只需要在您的build.gradle文件添加下面一行到 dependencies :
compile 'com.kevin:tabindicator:1.0.1'

關于小編給大家分享的Android 底部導航控件實例代碼就到此結束了,希望對大家有所幫助!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宁乡县| 双柏县| 古浪县| 金乡县| 崇义县| 永平县| 金平| 长海县| 左贡县| 泽库县| 化德县| 黑山县| 衡阳县| 民和| 介休市| 友谊县| 屯留县| 会宁县| 康乐县| 西林县| 永泰县| 信丰县| 绥阳县| 集贤县| 高密市| 莫力| 民县| 那曲县| 新疆| 乐业县| 营山县| 玛曲县| 如皋市| 绵阳市| 方山县| 河东区| 黄陵县| 灵璧县| 宣武区| 湖南省| 安国市|