通用的底部导航栏
可自主设置底部导航栏的消息数及提醒
用CV大法即可
都说“所有贴代码不贴效果图的都是耍流氓”,效果图献上
下载链接:https://share.weiyun.com/5WwrtkH
public class MainActivity extends AppCompatActivity {
private ViewPager mVpContent;
private BottomBarLayout mBottomBarLayout;
private List mFragmentList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initListener();
}
private void initView() {
mVpContent = (ViewPager) findViewById(R.id.vp_content);
mBottomBarLayout = (BottomBarLayout) findViewById(R.id.bbl);
}
private void initData() {
TabFragment homeFragment = new TabFragment();
Bundle bundle1 = new Bundle();
bundle1.putString(TabFragment.CONTENT,"第一个页面");
homeFragment.setArguments(bundle1);
mFragmentList.add(homeFragment);
TabFragment videoFragment = new TabFragment();
Bundle bundle2 = new Bundle();
bundle2.putString(TabFragment.CONTENT,"第二个页面");
videoFragment.setArguments(bundle2);
mFragmentList.add(videoFragment);
TabFragment microFragment = new TabFragment();
Bundle bundle3 = new Bundle();
bundle3.putString(TabFragment.CONTENT,"第三个页面");
microFragment.setArguments(bundle3);
mFragmentList.add(microFragment);
TabFragment meFragment = new TabFragment();
Bundle bundle4 = new Bundle();
bundle4.putString(TabFragment.CONTENT,"第四个页面");
meFragment.setArguments(bundle4);
mFragmentList.add(meFragment);
}
private void initListener() {
mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager(),mFragmentList));
mBottomBarLayout.setViewPager(mVpContent);
mBottomBarLayout.setUnread(0,20);//设置第一个页签的未读数为20
mBottomBarLayout.setUnread(1,101);//设置第二个页签的未读数
mBottomBarLayout.showNotify(2);//设置第三个页签显示提示的小红点
mBottomBarLayout.setMsg(3,"NEW");//设置第四个页签显示NEW提示文字
}
}
class MyAdapter extends FragmentStatePagerAdapter {
private List mFragmentList;
public MyAdapter(FragmentManager fm, List fragmentList) {
super(fm);
this.mFragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
}
public class TabFragment extends Fragment {
public static final String CONTENT = "content";
private TextView mTextView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle bundle) {
mTextView = new TextView(getActivity());
mTextView.setGravity(Gravity.CENTER);
String content = getArguments().getString(CONTENT);
mTextView.setText(content);
return mTextView;
}
}
public class BottomBarItem extends LinearLayout {
private Context mContext;
private int mIconNormalResourceId;//普通状态图标的资源id
private int mIconSelectedResourceId;//选中状态图标的资源id
private String mText;//文本
private int mTextSize = 12;//文字大小 默认为12sp
private int mTextColorNormal = 0xFF999999; //描述文本的默认显示颜色
private int mTextColorSelected = 0xFF46C01B; //述文本的默认选中显示颜色
private int mWhiteColor = 0xFFFFFFFF; //白色
private int mMarginTop = 0;//文字和图标的距离,默认0dp
private boolean mOpenTouchBg = false;// 是否开启触摸背景,默认关闭
private Drawable mTouchDrawable;//触摸时的背景
private int mIconWidth;//图标的宽度
private int mIconHeight;//图标的高度
private int mItemPadding;//BottomBarItem的padding
private ImageView mImageView;
private TextView mTvUnread;
private TextView mTvNotify;
private TextView mTvMsg;
private TextView mTextView;
private int mUnreadTextSize = 10; //未读数默认字体大小10sp
private int mMsgTextSize = 6; //消息默认字体大小6sp
private int unreadNumThreshold = 99;//未读数阈值
private int mUnreadTextColor;//未读数字体颜色
private Drawable mUnreadTextBg;
private int mMsgTextColor;
private Drawable mMsgTextBg;
private Drawable mNotifyPointBg;
public BottomBarItem(Context context) {
this(context, null);
}
public BottomBarItem(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomBarItem(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.BottomBarItem);
initAttrs(ta); //初始化属性
ta.recycle();
checkValues();//检查值是否合法
init();//初始化相关操作
}
private void initAttrs(TypedArray ta) {
mIconNormalResourceId = ta.getResourceId(R.styleable.BottomBarItem_iconNormal, -1);
mIconSelectedResourceId = ta.getResourceId(R.styleable.BottomBarItem_iconSelected, -1);
mText = ta.getString(R.styleable.BottomBarItem_itemText);
mTextSize = ta.getDimensionPixelSize(R.styleable.BottomBarItem_itemTextSize, UIUtils.sp2px(mContext, mTextSize));
mTextColorNormal = ta.getColor(R.styleable.BottomBarItem_textColorNormal, mTextColorNormal);
mTextColorSelected = ta.getColor(R.styleable.BottomBarItem_textColorSelected, mTextColorSelected);
mMarginTop = ta.getDimensionPixelSize(R.styleable.BottomBarItem_itemMarginTop, UIUtils.dip2Px(mContext, mMarginTop));
mOpenTouchBg = ta.getBoolean(R.styleable.BottomBarItem_openTouchBg, mOpenTouchBg);
mTouchDrawable = ta.getDrawable(R.styleable.BottomBarItem_touchDrawable);
mIconWidth = ta.getDimensionPixelSize(R.styleable.BottomBarItem_iconWidth, 0);
mIconHeight = ta.getDimensionPixelSize(R.styleable.BottomBarItem_iconHeight, 0);
mItemPadding = ta.getDimensionPixelSize(R.styleable.BottomBarItem_itemPadding, 0);
mUnreadTextSize = ta.getDimensionPixelSize(R.styleable.BottomBarItem_unreadTextSize, UIUtils.sp2px(mContext, mUnreadTextSize));
mUnreadTextColor = ta.getColor(R.styleable.BottomBarItem_unreadTextColor, 0xFFFFFFFF);
mUnreadTextBg = ta.getDrawable(R.styleable.BottomBarItem_unreadTextBg);
mMsgTextSize = ta.getDimensionPixelSize(R.styleable.BottomBarItem_msgTextSize, UIUtils.sp2px(mContext, mMsgTextSize));
mMsgTextColor = ta.getColor(R.styleable.BottomBarItem_msgTextColor, 0xFFFFFFFF);
mMsgTextBg = ta.getDrawable(R.styleable.BottomBarItem_msgTextBg);
mNotifyPointBg = ta.getDrawable(R.styleable.BottomBarItem_notifyPointBg);
unreadNumThreshold = ta.getInteger(R.styleable.BottomBarItem_unreadThreshold,99);
}
/**
* 检查传入的值是否完善
*/
private void checkValues() {
if (mIconNormalResourceId == -1) {
throw new IllegalStateException("您还没有设置默认状态下的图标,请指定iconNormal的图标");
}
if (mIconSelectedResourceId == -1) {
throw new IllegalStateException("您还没有设置选中状态下的图标,请指定iconSelected的图标");
}
if (mOpenTouchBg && mTouchDrawable == null) {
//如果有开启触摸背景效果但是没有传对应的drawable
throw new IllegalStateException("开启了触摸效果,但是没有指定touchDrawable");
}
if (mUnreadTextBg == null){
mUnreadTextBg = getResources().getDrawable(R.drawable.shape_unread);
}
if (mMsgTextBg == null){
mMsgTextBg = getResources().getDrawable(R.drawable.shape_msg);
}
if (mNotifyPointBg == null){
mNotifyPointBg = getResources().getDrawable(R.drawable.shape_notify_point);
}
}
private void init() {
setOrientation(VERTICAL);
setGravity(Gravity.CENTER);
View view = initView();
mImageView.setImageResource(mIconNormalResourceId);
if (mIconWidth != 0 && mIconHeight != 0) {
//如果有设置图标的宽度和高度,则设置ImageView的宽高
FrameLayout.LayoutParams imageLayoutParams = (FrameLayout.LayoutParams) mImageView.getLayoutParams();
imageLayoutParams.width = mIconWidth;
imageLayoutParams.height = mIconHeight;
mImageView.setLayoutParams(imageLayoutParams);
}
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);//设置底部文字字体大小
mTvUnread.setTextSize(TypedValue.COMPLEX_UNIT_PX, mUnreadTextSize);//设置未读数的字体大小
mTvUnread.setTextColor(mUnreadTextColor);//设置未读数字体颜色
mTvUnread.setBackground(mUnreadTextBg);//设置未读数背景
mTvMsg.setTextSize(TypedValue.COMPLEX_UNIT_PX, mMsgTextSize);//设置提示文字的字体大小
mTvMsg.setTextColor(mMsgTextColor);//设置提示文字的字体颜色
mTvMsg.setBackground(mMsgTextBg);//设置提示文字的背景颜色
mTvNotify.setBackground(mNotifyPointBg);//设置提示点的背景颜色
mTextView.setTextColor(mTextColorNormal);//设置底部文字字体颜色
mTextView.setText(mText);//设置标签文字
LayoutParams textLayoutParams = (LayoutParams) mTextView.getLayoutParams();
textLayoutParams.topMargin = mMarginTop;
mTextView.setLayoutParams(textLayoutParams);
if (mOpenTouchBg) {
//如果有开启触摸背景
setBackground(mTouchDrawable);
}
addView(view);
}
@NonNull
private View initView() {
View view = View.inflate(mContext, R.layout.item_bottom_bar, null);
if (mItemPadding != 0) {
//如果有设置item的padding
view.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
}
mImageView = (ImageView) view.findViewById(R.id.iv_icon);
mTvUnread = (TextView) view.findViewById(R.id.tv_unred_num);
mTvMsg = (TextView) view.findViewById(R.id.tv_msg);
mTvNotify = (TextView) view.findViewById(R.id.tv_point);
mTextView = (TextView) view.findViewById(R.id.tv_text);
return view;
}
public ImageView getImageView() {
return mImageView;
}
public TextView getTextView() {
return mTextView;
}
public void setIconNormalResourceId(int mIconNormalResourceId) {
this.mIconNormalResourceId = mIconNormalResourceId;
}
public void setIconSelectedResourceId(int mIconSelectedResourceId) {
this.mIconSelectedResourceId = mIconSelectedResourceId;
}
public void setStatus(boolean isSelected) {
mImageView.setImageDrawable(getResources().getDrawable(isSelected ? mIconSelectedResourceId : mIconNormalResourceId));
mTextView.setTextColor(isSelected ? mTextColorSelected : mTextColorNormal);
}
private void setTvVisiable(TextView tv) {
//都设置为不可见
mTvUnread.setVisibility(GONE);
mTvMsg.setVisibility(GONE);
mTvNotify.setVisibility(GONE);
tv.setVisibility(VISIBLE);//设置为可见
}
public int getUnreadNumThreshold() {
return unreadNumThreshold;
}
public void setUnreadNumThreshold(int unreadNumThreshold) {
this.unreadNumThreshold = unreadNumThreshold;
}
/**
* 设置未读数
*
* @param unreadNum 小于等于{@link #unreadNumThreshold}则隐藏,
* 大于0小于{@link #unreadNumThreshold}则显示对应数字,
* 超过{@link #unreadNumThreshold}
* 显示{@link #unreadNumThreshold}+
*/
public void setUnreadNum(int unreadNum) {
setTvVisiable(mTvUnread);
if (unreadNum <= 0) {
mTvUnread.setVisibility(GONE);
} else if (unreadNum <= unreadNumThreshold) {
mTvUnread.setText(String.valueOf(unreadNum));
} else {
mTvUnread.setText(String.format(Locale.CHINA, "%d+", unreadNumThreshold));
}
}
public void setMsg(String msg) {
setTvVisiable(mTvMsg);
mTvMsg.setText(msg);
}
public void hideMsg() {
mTvMsg.setVisibility(GONE);
}
public void showNotify() {
setTvVisiable(mTvNotify);
}
public void hideNotify() {
mTvNotify.setVisibility(GONE);
}
}
public class BottomBarLayout extends LinearLayout implements ViewPager.OnPageChangeListener {
private static final String STATE_INSTANCE = "instance_state";
private static final String STATE_ITEM = "state_item";
private ViewPager mViewPager;
private int mChildCount;//子条目个数
private List mItemViews = new ArrayList<>();
private int mCurrentItem;//当前条目的索引
private boolean mSmoothScroll;
public BottomBarLayout(Context context) {
this(context, null);
}
public BottomBarLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomBarLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.BottomBarLayout);
mSmoothScroll = ta.getBoolean(R.styleable.BottomBarLayout_smoothScroll,false);
ta.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
init();
}
@Override
public void setOrientation(int orientation) {
super.setOrientation(orientation);
}
public void setViewPager(ViewPager viewPager) {
this.mViewPager = viewPager;
init();
}
private void init() {
mChildCount = getChildCount();
if (mViewPager != null) {
if (mViewPager.getAdapter().getCount() != mChildCount) {
throw new IllegalArgumentException("LinearLayout的子View数量必须和ViewPager条目数量一致");
}
}
for (int i = 0; i < mChildCount; i++) {
if (getChildAt(i) instanceof BottomBarItem) {
BottomBarItem bottomBarItem = (BottomBarItem) getChildAt(i);
mItemViews.add(bottomBarItem);
//设置点击监听
bottomBarItem.setOnClickListener(new MyOnClickListener(i));
} else {
throw new IllegalArgumentException("BottomBarLayout的子View必须是BottomBarItem");
}
}
mItemViews.get(mCurrentItem).setStatus(true);//设置选中项
if (mViewPager != null){
mViewPager.setOnPageChangeListener(this);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
resetState();
mItemViews.get(position).setStatus(true);
if (onItemSelectedListener != null){
onItemSelectedListener.onItemSelected(getBottomItem(position),mCurrentItem,position);
}
mCurrentItem = position;//记录当前位置
}
@Override
public void onPageScrollStateChanged(int state) {
}
private class MyOnClickListener implements OnClickListener {
private int currentIndex;
public MyOnClickListener(int i) {
this.currentIndex = i;
}
@Override
public void onClick(View v) {
//回调点击的位置
if(mViewPager != null){
//有设置viewPager
if (currentIndex == mCurrentItem){
//如果还是同个页签,使用setCurrentItem不会回调OnPageSelecte(),所以在此处需要回调点击监听
if (onItemSelectedListener != null) {
onItemSelectedListener.onItemSelected(getBottomItem(currentIndex),mCurrentItem,currentIndex);
}
}else{
mViewPager.setCurrentItem(currentIndex, mSmoothScroll);
}
}else{
//没有设置viewPager
if (onItemSelectedListener != null) {
onItemSelectedListener.onItemSelected(getBottomItem(currentIndex),mCurrentItem,currentIndex);
}
updateTabState(currentIndex);
}
}
}
private void updateTabState(int position){
resetState();
mCurrentItem = position;
mItemViews.get(mCurrentItem).setStatus(true);
}
/**
* 重置当前按钮的状态
*/
private void resetState() {
if (mCurrentItem < mItemViews.size()){
mItemViews.get(mCurrentItem).setStatus(false);
}
}
public void setCurrentItem(int currentItem) {
if (mViewPager != null){
mViewPager.setCurrentItem(currentItem,mSmoothScroll);
}else{
updateTabState(currentItem);
}
}
/**
* 设置未读数
* @param position 底部标签的下标
* @param unreadNum 未读数
*/
public void setUnread(int position,int unreadNum){
mItemViews.get(position).setUnreadNum(unreadNum);
}
/**
* 设置提示消息
* @param position 底部标签的下标
* @param msg 未读数
*/
public void setMsg(int position,String msg){
mItemViews.get(position).setMsg(msg);
}
/**
* 隐藏提示消息
* @param position 底部标签的下标
*/
public void hideMsg(int position){
mItemViews.get(position).hideMsg();
}
/**
* 显示提示的小红点
* @param position 底部标签的下标
*/
public void showNotify(int position){
mItemViews.get(position).showNotify();
}
/**
* 隐藏提示的小红点
* @param position 底部标签的下标
*/
public void hideNotify(int position){
mItemViews.get(position).hideNotify();
}
public int getCurrentItem() {
return mCurrentItem;
}
public void setSmoothScroll(boolean smoothScroll) {
this.mSmoothScroll = smoothScroll;
}
public BottomBarItem getBottomItem(int position){
return mItemViews.get(position);
}
/**
* @return 当View被销毁的时候,保存数据
*/
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());
bundle.putInt(STATE_ITEM, mCurrentItem);
return bundle;
}
/**
* @param state 用于恢复数据使用
*/
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mCurrentItem = bundle.getInt(STATE_ITEM);
//重置所有按钮状态
resetState();
//恢复点击的条目颜色
mItemViews.get(mCurrentItem).setStatus(true);
super.onRestoreInstanceState(bundle.getParcelable(STATE_INSTANCE));
} else {
super.onRestoreInstanceState(state);
}
}
private OnItemSelectedListener onItemSelectedListener;
public interface OnItemSelectedListener {
void onItemSelected(BottomBarItem bottomBarItem, int previousPosition, int currentPosition);
}
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
this.onItemSelectedListener = onItemSelectedListener;
}
}
public class UIUtils {
/**
* dip-->px
*/
public static int dip2Px(Context context, int dip) {
// px/dip = density;
// density = dpi/160
// 320*480 density = 1 1px = 1dp
// 1280*720 density = 2 2px = 1dp
float density = context.getResources().getDisplayMetrics().density;
int px = (int) (dip * density + 0.5f);
return px;
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param spValue
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}
activity_main.xml
item_bottom_bar.xml
attr.xml
colors.xml
#3F51B5
#303F9F
#FF4081
#F3F5F4
#515051
#D33D3C
#00ff00
#00ff00
#ff0000
#DDDDDD
#FFFFFF
当需要禁止viewpager滑动时
/**
* 可以控制滑动的ViewPager
*/
public class MyViewPager extends ViewPager {
private boolean mIsCanScroll = false;
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent event) {
if (this.mIsCanScroll) {
return super.onTouchEvent(event);
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.mIsCanScroll) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setCanScroll(boolean isCanScroll) {
this.mIsCanScroll = isCanScroll;
}
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item, false);
}
}
在MainActivity设置mVpContent.setCanScroll(false);即可
别忘了ViewPager将强转成MyViewPager
新增常用的方法:
点击返回提示 “再按一次退出程序” ,2秒内在按一次,退出APP
/**
* 管理activity stack
*/
public class AppManager {
private static Stack mActivityStack;
//存放Class信息Map
private static Map> mClassMap = new HashMap<>();
public AppManager() {
}
/**
* 单一实例
*/
public static AppManager getInstance() {
return SingleApp.INSTANCE;
}
public static class SingleApp {
public static AppManager INSTANCE = new AppManager();
}
/**
* 获取map中的Class
* @param className
* @return
*/
public Class> getClassFromMap(String className){
return mClassMap.get(className);
}
/**
* 添加Activity到堆栈
*/
public void addActivity(Activity activity) {
if (mActivityStack == null) {
mActivityStack = new Stack();
}
mActivityStack.add(activity);
//加入到map
mClassMap.put(activity.getClass().getSimpleName(),activity.getClass());
}
/**
* 移除Activity
*/
public void removeActivity(Activity activity) {
mActivityStack.remove(activity);
//从map中移除
mClassMap.remove(activity.getClass().getSimpleName());
}
/**
* 获取指定的Activity
*/
public Activity getActivity(Class> cls) {
if (mActivityStack != null)
for (Activity activity : mActivityStack) {
if (activity.getClass().equals(cls)) {
return activity;
}
}
return null;
}
/**
* 获取当前显示Activity(堆栈中最后一个传入的activity)
*/
public Activity getLastActivity() {
Activity activity = mActivityStack.lastElement();
return activity;
}
/**
* 获取所有Activity
*/
public Stack getAllActivityStacks() {
return mActivityStack;
}
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
if (!activity.isFinishing()) {
activity.finish();
mActivityStack.remove(activity);
}
}
}
/**
* 结束指定类名的Activity
*/
public void finishActivity(Class> cls) {
for (Activity activity : mActivityStack) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
break;
}
}
}
/**
* 结束除当前传入以外所有Activity
*/
public void finishOthersActivity(Class> cls) {
if (mActivityStack != null)
for (Activity activity : mActivityStack) {
if (!activity.getClass().equals(cls)) {
activity.finish();
}
}
}
/**
* 结束所有Activity
*/
public void finishAllActivity() {
if (mActivityStack != null)
for (Activity activity : mActivityStack) {
activity.finish();
}
mActivityStack.clear();
}
/**
* 退出应用程序
*/
public void AppExit() {
try {
finishAllActivity();
android.os.Process.killProcess(android.os.Process.myPid());// 杀死该应用进程
System.exit(0);
} catch (Exception e) {
}
}
}
在MainActivity中入栈
AppManager.getInstance().addActivity(this);
然后复写方法
/**
* 退出程序逻辑
*
* @param keyCode
* @param event
* @return
*/
private long lastBackTime = 0;
private static final long TIME_INTERVAL = 2 * 1000;
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
long currentBackTime = System.currentTimeMillis();
Toast.makeText(MainActivity.this,"再按一次退出程序",Toast.LENGTH_SHORT).show();
//双击退出程序
if (currentBackTime - lastBackTime > TIME_INTERVAL) {
lastBackTime = currentBackTime;
} else {
//退出
AppManager.getInstance().AppExit();
}
return true;
}
return super.onKeyDown(keyCode, event);
}