Android   发布时间:2022-04-28  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Android 底部导航控件实例代码大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

一、先给大家展示下最终效果

Android 底部导航控件实例代码

Android 底部导航控件实例代码

Android 底部导航控件实例代码

通过以上可以看到,图一是简单的使用,图二、图三中为结合ViewPager共同使用,而且都可以随ViewPager的滑动渐变色,不同点是图二为选中非选中两张图片,图三的选中非选中是一张图片只是做了颜色变化。

二、 需求

我们希望做可以做成这样的,可以在xml布局中引入控件并绑定数据,在代码中设置监听回调,并且配置使用要非常简单!

三、需求分析

根据我们多年做不明确需求项目的经验,以上需求还算明确。那么我们可以采用在LinearLayout添加子View控件,这个子View控件就是我们自定义的每个tab条目,当然对LinearLayout要设置权重。

需求大致明确之后就先设计每个条目的子View控件,这个子View控件是一个可以切换状态变化的,一张、两张都可以切换状态(参图一、图三)。那么这个View要可以设置底部显示文字,设置选中时颜色、未选中时颜色、选中时图片、未选中时图片文字大小、设置是否有指示点、设置指示点大小、设置指示点图片等等。

四、Tab条目接口

通过需求分析,我们可以定义如下的Tab子View操作接口:

Android 底部导航控件实例代码

仔细的朋友会发现,为什么在接口中没有设置选中图片以及设置非选中时图片,那是因为这个属性不是通用的,在不同的实现中再去定义。

五、Tab条目实现

Android 底部导航控件实例代码

类的继承关系及说明:

类图如下所示:

Android 底部导航控件实例代码

在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,left+tabRealHeight* 4/5 + inDicatorRadius,top + mInDicatorSizE); 
} 

在以上代码中可以看到,测量文字的高度,用控件的高度减去文字的高度和控件的宽度对比,取较小的为图片的大小,也就是设置的图片要为正方形,否则会产生变形。

看下普通两张图片切换的TabView的绘制:

Android 底部导航控件实例代码

@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,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两张图片图片的时,我们再把效果图拿过来观察下:

Android 底部导航控件实例代码

这里有两种模式,即随着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,mPaint); 
} 
/** 
* 绘制选中图标 
* @param canvas 
* @param alpha 
*/ 
private void drawTargetBitmap(Canvas canvas,int alpha) { 
mPaint.setAntiAlias(true); 
mPaint.setDither(true); 
mPaint.setAlpha(alpha); 
canvas.drawBitmap(mSELEcteDiconBitmap,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,mTextPaint); 
} 
/** 
* 画选中文字 
* @param canvas 
* @param alpha 
*/ 
private void drawTargetText(Canvas canvas,int alpha) { 
mTextPaint.setColor(mSELEctedColor); 
mTextPaint.setAlpha(alpha); 
canvas.drawText(mText,mTextPaint); 
}

代码中的mAlpha是ViewPager滚动的百分比,然后分别绘制选中以及未选中的图标和文本,但是绘制的时候设置的透明度不同,这样就会有一个渐变的效果

在ViewPager单张图片图片的时,我们再把效果图拿过来观察下:

Android 底部导航控件实例代码

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,mPaint); 
} 
private void drawsourceText(Canvas canvas,mTextPaint); 
} 
private void drawTargetText(Canvas canvas,mTextPaint); 
}

绘制的过程大致与两张图片相同,不同点就是在绘制图片的时候Paint设置 Xfermode,来控制颜色的渐变。

OK,Tab条目的自定义view搞定之后剩下的就简单多了。

六、定义属性

接下来就是封装继承自LinearLayout的整体控件,先来定义下属性

Android 底部导航控件实例代码

可以看到tabIcons为单张图片渐变效果特殊的,tabSELEcteDicons和tabUnSELEcteDicon为两张图标切换效果特殊的。

七、 控件编写

由于三中样式有公共的部分,我们进行积累抽取。类图结构如下:

Android 底部导航控件实例代码

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); 
// 图标及文字 
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 底部导航控件实例代码就到此结束了,希望对大家有所帮助!

大佬总结

以上是大佬教程为你收集整理的Android 底部导航控件实例代码全部内容,希望文章能够帮你解决Android 底部导航控件实例代码所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。