转载自这个项目的github地址:https://github.com/xubinhong/BottomBar

这个底部导航栏的特点:

1.告别xml中的item布局,一切icon、title统统绘制得出;

2.扁平化,由于icon、title都是绘制得出的,所以只需要一个view即可,无需父布局

3.为你处理好碎片切换事务,告别冗余代码,让你从此光速开发

4.不怕需求变动,拔插式体验,增删item,只需修改1行代码

5.源代码十分简单,有助于使用者开发高度适配自身需求的底部

使用方式

1.只需要到给出的github地址中拷贝BottomBar类到你的包下即可,或者自己创建一个类名字叫BottomBar,复制如下代码并导包:

public class BottomBar extends View{private Context context;public BottomBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);this.context = context;}////提供的api 并且根据api做一定的物理基础准备//private int containerId;private List<Class> fragmentClassList = new ArrayList<>();private List<String> titleList = new ArrayList<>();private List<Integer> iconResBeforeList = new ArrayList<>();private List<Integer> iconResAfterList = new ArrayList<>();private List<Fragment> fragmentList = new ArrayList<>();private int itemCount;private Paint paint = new Paint();private List<Bitmap> iconBitmapBeforeList = new ArrayList<>();private List<Bitmap> iconBitmapAfterList = new ArrayList<>();private List<Rect> iconRectList = new ArrayList<>();private int currentCheckedIndex;private int firstCheckedIndex;private int titleColorBefore = Color.parseColor("#999999");private int titleColorAfter = Color.parseColor("#ff5d5e");private int titleSizeInDp = 10;private int iconWidth = 20;private int iconHeight = 20;private int titleIconMargin = 5;public BottomBar setContainer(int containerId) {this.containerId = containerId;return this;}public BottomBar setTitleBeforeAndAfterColor(String beforeResCode, String AfterResCode) {//支持"#333333"这种形式titleColorBefore = Color.parseColor(beforeResCode);titleColorAfter = Color.parseColor(AfterResCode);return this;}public BottomBar setTitleSize(int titleSizeInDp) {this.titleSizeInDp = titleSizeInDp;return this;}public BottomBar setIconWidth(int iconWidth) {this.iconWidth = iconWidth;return this;}public BottomBar setTitleIconMargin(int titleIconMargin) {this.titleIconMargin = titleIconMargin;return this;}public BottomBar setIconHeight(int iconHeight) {this.iconHeight = iconHeight;return this;}public BottomBar addItem(Class fragmentClass, String title, int iconResBefore, int iconResAfter) {fragmentClassList.add(fragmentClass);titleList.add(title);iconResBeforeList.add(iconResBefore);iconResAfterList.add(iconResAfter);return this;}public BottomBar setFirstChecked(int firstCheckedIndex) {//从0开始this.firstCheckedIndex = firstCheckedIndex;return this;}public void build() {itemCount = fragmentClassList.size();//预创建bitmap的Rect并缓存//预创建icon的Rect并缓存for (int i = 0; i < itemCount; i++) {Bitmap beforeBitmap = getBitmap(iconResBeforeList.get(i));iconBitmapBeforeList.add(beforeBitmap);Bitmap afterBitmap = getBitmap(iconResAfterList.get(i));iconBitmapAfterList.add(afterBitmap);Rect rect = new Rect();iconRectList.add(rect);Class clx = fragmentClassList.get(i);try {Fragment fragment = (Fragment) clx.newInstance();fragmentList.add(fragment);} catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();}}currentCheckedIndex = firstCheckedIndex;switchFragment(currentCheckedIndex);invalidate();}private Bitmap getBitmap(int resId) {BitmapDrawable bitmapDrawable = (BitmapDrawable) context.getResources().getDrawable(resId);return bitmapDrawable.getBitmap();}////初始化数据基础//@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);initParam();}private int titleBaseLine;private List<Integer> titleXList = new ArrayList<>();private int parentItemWidth;private void initParam() {if (itemCount != 0) {//单个item宽高parentItemWidth = getWidth() / itemCount;int parentItemHeight = getHeight();//图标边长int iconWidth = dp2px(this.iconWidth);//先指定20dpint iconHeight = dp2px(this.iconHeight);//图标文字marginint textIconMargin = dp2px(((float)titleIconMargin)/2);//先指定5dp,这里除以一半才是正常的margin,不知道为啥,可能是图片的原因//标题高度int titleSize = dp2px(titleSizeInDp);//这里先指定10dppaint.setTextSize(titleSize);Rect rect = new Rect();paint.getTextBounds(titleList.get(0), 0, titleList.get(0).length(), rect);int titleHeight = rect.height();//从而计算得出图标的起始top坐标、文本的baseLineint iconTop = (parentItemHeight - iconHeight - textIconMargin - titleHeight)/2;titleBaseLine = parentItemHeight - iconTop;//对icon的rect的参数进行赋值int firstRectX = (parentItemWidth - iconWidth) / 2;//第一个icon的左for (int i = 0; i < itemCount; i++) {int rectX = i * parentItemWidth + firstRectX;Rect temp = iconRectList.get(i);temp.left = rectX;temp.top = iconTop ;temp.right = rectX + iconWidth;temp.bottom = iconTop + iconHeight;}//标题(单位是个问题)for (int i = 0; i < itemCount; i ++) {String title = titleList.get(i);paint.getTextBounds(title, 0, title.length(), rect);titleXList.add((parentItemWidth - rect.width()) / 2 + parentItemWidth * i);}}}private int dp2px(float dpValue) {float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}////根据得到的参数绘制//@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//这里让view自身替我们画背景 如果指定的话if (itemCount != 0) {//画背景paint.setAntiAlias(false);for (int i = 0; i < itemCount; i++) {Bitmap bitmap = null;if (i == currentCheckedIndex) {bitmap = iconBitmapAfterList.get(i);} else {bitmap = iconBitmapBeforeList.get(i);}Rect rect = iconRectList.get(i);canvas.drawBitmap(bitmap, null, rect, paint);//null代表bitmap全部画出}//画文字paint.setAntiAlias(true);for (int i = 0; i < itemCount; i ++) {String title = titleList.get(i);if (i == currentCheckedIndex) {paint.setColor(titleColorAfter);} else {paint.setColor(titleColorBefore);}int x = titleXList.get(i);canvas.drawText(title, x, titleBaseLine, paint);}}}////点击事件:我观察了微博和掌盟,发现down和up都在该区域内才响应//int target = -1;@SuppressLint("ClickableViewAccessibility")@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN :target = withinWhichArea((int)event.getX());break;case MotionEvent.ACTION_UP :if (event.getY() < 0) {break;}if (target == withinWhichArea((int)event.getX())) {//这里触发点击事件switchFragment(target);currentCheckedIndex = target;invalidate();}target = -1;break;}return true;//这里return super为什么up执行不到?是因为return super的值,全部取决于你是否//clickable,当你down事件来临,不可点击,所以return false,也就是说,而且你没//有设置onTouchListener,并且控件是ENABLE的,所以dispatchTouchEvent的返回值//也是false,所以在view group的dispatchTransformedTouchEvent也是返回false,//这样一来,view group中的first touch target就是空的,所以intercept标记位//果断为false,然后就再也进不到循环取孩子的步骤了,直接调用dispatch-// TransformedTouchEvent并传孩子为null,所以直接调用view group自身的dispatch-// TouchEvent了}private int withinWhichArea(int x) { return x/parentItemWidth; }//从0开始////碎片处理代码//private Fragment currentFragment;//注意 这里是只支持AppCompatActivity 需要支持其他老版的 自行修改protected void switchFragment(int whichFragment) {Fragment fragment = fragmentList.get(whichFragment);int frameLayoutId = containerId;if (fragment != null) {FragmentTransaction transaction = ((AppCompatActivity)context).getSupportFragmentManager().beginTransaction();if (fragment.isAdded()) {if (currentFragment != null) {transaction.hide(currentFragment).show(fragment);} else {transaction.show(fragment);}} else {if (currentFragment != null) {transaction.hide(currentFragment).add(frameLayoutId, fragment);} else {transaction.add(frameLayoutId, fragment);}}currentFragment = fragment;transaction.commit();}}
}

2.xml中

<com.example.bottombar.BottomBarandroid:background="#FFFFFF"android:id="@+id/bottom_bar"android:layout_width="match_parent"android:layout_height="46dp"android:layout_gravity="bottom" />

3.java代码中

BottomBar bottomBar = findViewById(R.id.bottom_bar);
bottomBar.setContainer(R.id.fl_container).setTitleBeforeAndAfterColor("#999999", "#ff5d5e").addItem(Fragment1.class,"首页",R.drawable.item1_before,R.drawable.item1_after).addItem(Fragment2.class,"订单",R.drawable.item2_before,R.drawable.item2_after).addItem(Fragment3.class,"我的",R.drawable.item3_before,R.drawable.item3_after).build();

设置了容器frame layout

设置了字体选中前后的颜色

增加了item,并且给item绑定碎片,设定选中前后的drawable以及文本

就这么简单的代码,就搞定了一切!效果如下:

image.png

而如果你正常写一个底部导航栏是怎样的?

1.item布局,你还得精心设置半天

2.底部title布局,引入若干

3.title布局放到主布局中

4.java代码中要通过findViewById找到所有item

5.给所有item设置点击事件

6.点击事件内作碎片的切换

7.当你如果要增加一个item的时候,前面的又要大幅度修改,而且代码冗余程度极高

如果你对里面icon、title的位置不满意,有更多的api供你选择

setTitleSize,以dp为单位

setIconWidth,图标宽度

setIconHeight,图标高度

setTitleIconMargin,标题图标间距

setFirstChecked,设置第一个默认选中item

由于源代码简单,易于阅读,开发者更可以自行修改源码

底部导航栏设计思路

根据api中获取的参数,计算出icon、title的精确位置,并在onDraw中绘制

在onTouchEvent里,根据触摸点,获知点击区域,响应Icon、title的更改事件以及碎片的切换事件

另一个BottomBar的实战应用可以看:

https://blog.csdn.net/qq_36523667/article/details/79983010

再次证明了,BottomBar,实在太方便啦!

Android最好用的底部导航栏相关推荐

  1. android导航栏自动弹出,解决android 显示内容被底部导航栏遮挡的问题

    描述: 由于产品需求,要求含有EditText的界面全屏显示,最好的解决方式是使用AndroidBug5497Workaround.assistActivity(this) 的方式来解决,但是华为和魅 ...

  2. (2019年10月更新) Android 最全的底部导航栏实现方法

    本文(争取做到)Android 最全的底部导航栏实现方法. 现在写了4个主要方法. 官方方法. 官方的 BottomNavigationActivity 使用Android studio 新建一个工程 ...

  3. Android ------ Android X 的BottomNavigationView底部导航栏

    有小伙伴问到我BottomNavigationView底部导航的问题,分享一下,底部导航栏的使用比较常见,目前常用的APP几乎都是使用底部导航栏将内容分类. 这个Android 的底部导航栏 Bott ...

  4. Android 设置应用的底部导航栏(虚拟按键)背景颜色

    对于有些Android手机的底部虚拟键,进行设置颜色,其实很简单,利用系统提供的Api一步代码就可以搞定,只支持Android5.0以上的系统 //设置底部导航栏颜色if (Build.VERSION ...

  5. Android最好用的底部导航栏开发

    https://www.cnblogs.com/yelanggu/p/9516429.html 这里我选择用第三个的底部导航栏

  6. android 虚拟键背景,Android 设置应用的底部导航栏(虚拟按键)背景颜色

    Android手机机型种类繁多,但是虚拟按键也就是底部的导航栏,不外乎两种设计方式,一种是作为虚拟按键设计到屏幕内部,一种是作为系统按键设计到屏幕外面. 对于按键在屏幕内部的机型,因为虚拟按键也是屏幕 ...

  7. Android 高仿新浪微博底部导航栏,实现双击首页Tab,页面的ListView滚动、刷新

    现在很多APP,如微信.QQ.微博等等,它们的主页面都无一例外的选择使用底部Tab导航, 通过这种方式,可以很好的把页面层级分化,很好的提高用户体验.相信,很多Android开发者,都使用到过这种经典 ...

  8. Android开源底部导航,一个开源JPTabBar for Android,炫酷的底部导航栏

    JPTabBar TabBar这个名字相信很多学过一点IOS程序员都知道它是用来干嘛的,但本人也并非擅长开发IOS程序员,只是略懂略懂....这是一个很强大的TabBar,可满足很多需求.用起来也非常 ...

  9. 转载:Android (争取做到)最全的底部导航栏实现方法

    原文出处 标题:Android (争取做到)最全的底部导航栏实现方法 作者:野狼谷 原文链接:Android (争取做到)最全的底部导航栏实现方法 - 野狼谷 - 博客园 前言 本文(争取做到)And ...

最新文章

  1. 模型压缩95%:Lite Transformer,MIT韩松等人
  2. 上海中考-哪些区是“地狱模式”?——2019上海中考数据观
  3. 从零开始学习docker(十五)Swarm mode 介绍
  4. 回溯法(深度优先)剪枝和分支限界法(宽度优先)剪枝对比:01背包问题
  5. 专访中科创达王璠:怎样做好嵌入式人工智能的算法开发?
  6. 黄金连分数(java大法好)
  7. Climbing Stairs
  8. JavaScript——使用对话框
  9. 通过Docker或虚拟机快速搭建私有云平台--使用Nextcloud和ONLYOFFICE创建您的WebOffice云办公系统
  10. [LeetCode]Remove Nth Node From End of List
  11. Replication的犄角旮旯(三)--聊聊@bitmap
  12. python持久化数据_Python数据持久化-mysql篇
  13. 马斯克回怼世粮署:能公开60亿美元花在哪 就立即卖股票捐款
  14. linux 进程占用cpu查看工具,Linux下如何查看某一进程的CPU占用率
  15. java canvas 动态画图_canvas前端动图如何实现
  16. nginx做代理服务
  17. Linux之Shell管理脚本(一)
  18. Win9x 与 WinME 磁盘共享密码破解实战
  19. 成语答题小程序源码安装 开源的成语答题小程序
  20. 能力开放平台(个人体验心得)

热门文章

  1. BJFU_2022Web前端开发_CSS样式汇总
  2. python常用画图(分段折线图、多变量柱状图、扇形图、堆积折线图、百分比堆积柱状图)工具代码汇总
  3. 1.现实设置上月、本月电表读数,显示上月、本月电表读数,计算并显示本月用电数,假设每度电的价格1.2元,计算并显示本月电费的功能。
  4. Nginx+Springboot+Security+CAS 整合方案-XML 实现SSO客户端
  5. 最新深度学习环境搭建:win10+Anaconda+PyCharm+python3.7+tensorflow-gpu1.15+keras2.3.1+CUDA10.0+CUDNN7.6.5
  6. Blazor实战——Known框架增删改查导
  7. Windows系统FTP服务器设置
  8. mysql中information_schema说明
  9. 《Kubernetes部署篇:基于docker使用kubespray工具离线部署高可用K8S集群(国内专网方案)》
  10. 文献解读|拷贝数增加临床意义分析指导手册