This commit is contained in:
2025-11-18 15:25:59 +08:00
parent 790f719050
commit 69f2eaf336
1227 changed files with 89317 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/build
+29
View File
@@ -0,0 +1,29 @@
apply plugin: 'com.android.library'
android {
namespace 'me.majiajie.pagerbottomtabstrip'
compileSdkVersion 29
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.2.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
}
//apply from: 'upload_to_maven_central.gradle'
+17
View File
@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\MJJ\Develop\SDK/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
@@ -0,0 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="me.majiajie.pagerbottomtabstrip">
<application android:label="@string/app_name" />
</manifest>
@@ -0,0 +1,25 @@
package me.majiajie.pagerbottomtabstrip;
import androidx.viewpager.widget.ViewPager;
interface BottomLayoutController {
/**
* 方便适配ViewPager页面切换<p>
* 注意:ViewPager页面数量必须等于导航栏的Item数量
*
* @param viewPager {@link ViewPager}
*/
void setupWithViewPager(ViewPager viewPager);
/**
* 向下移动隐藏导航栏
*/
void hideBottomLayout();
/**
* 向上移动显示导航栏
*/
void showBottomLayout();
}
@@ -0,0 +1,128 @@
package me.majiajie.pagerbottomtabstrip;
import android.graphics.drawable.Drawable;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
public interface ItemController {
/**
* 设置选中项
*
* @param index 顺序索引
*/
void setSelect(int index);
/**
* 设置选中项,并可以控制是否回调监听事件
*
* @param index 顺序索引
* @param listener true:假如存在监听事件{@link OnTabItemSelectedListener},就会调用相关的回调方法。false:不会触发监听事件
*/
void setSelect(int index, boolean listener);
/**
* 设置导航按钮上显示的圆形消息数字,通过顺序索引。
*
* @param index 顺序索引
* @param number 消息数字
*/
void setMessageNumber(int index, int number);
/**
* 设置显示无数字的消息小原点
*
* @param index 顺序索引
* @param hasMessage true显示
*/
void setHasMessage(int index, boolean hasMessage);
/**
* 导航栏按钮点击监听
*
* @param listener {@link OnTabItemSelectedListener}
*/
void addTabItemSelectedListener(OnTabItemSelectedListener listener);
/**
* 导航栏按钮点击监听(只有选中事件)
*
* @param listener {@link SimpleTabItemSelectedListener}
*/
void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener);
/**
* 设置标题
*
* @param index 顺序索引
* @param title 标题文字
*/
void setTitle(int index, String title);
/**
* 设置未选中状态下的图标
*
* @param index 顺序索引
* @param drawable 图标资源
*/
void setDefaultDrawable(int index, Drawable drawable);
/**
* 设置选中状态下的图标
*
* @param index 顺序索引
* @param drawable 图标资源
*/
void setSelectedDrawable(int index, Drawable drawable);
/**
* 获取当前选中项索引
*
* @return 索引
*/
int getSelected();
/**
* 获取导航按钮总数
*
* @return 总数
*/
int getItemCount();
/**
* 获取导航按钮文字
*
* @param index 顺序索引
* @return 文字
*/
String getItemTitle(int index);
/**
* 移除指定的导航项.需要注意,不能移除当前选中的导航项
*
* @return 移除是否成功
*/
boolean removeItem(int index);
/**
* 添加一个材料设计样式的TabItem.注意,只对<code>material()</code>构建的导航栏有效
*
* @param index 顺序索引
* @param defaultDrawable 未选中状态的图标资源
* @param selectedDrawable 选中状态的图标资源
* @param title 标题
* @param selectedColor 选中状态的颜色
*/
void addMaterialItem(int index, Drawable defaultDrawable, Drawable selectedDrawable, String title, int selectedColor);
/**
* 添加一个自定义样式的TabItem.注意,只对<code>custom()</code>构建的导航栏有效
*
* @param index 顺序索引
* @param item 自定义的Item
*/
void addCustomItem(int index, BaseTabItem item);
}
@@ -0,0 +1,20 @@
package me.majiajie.pagerbottomtabstrip;
/**
* <p>模式选择。</p>
* <p>采用组合的形式,比如要两种效果可以这样做:</p>
* MaterialMode.HIDE_TEXT | MaterialMode.CHANGE_BACKGROUND_COLOR
*/
public final class MaterialMode {
/**
* 隐藏文字内容,只在选中时显示文字
*/
public static final int HIDE_TEXT = 0X1;
/**
* 开启导航栏背景变换。点击不同项切换不同背景颜色
*/
public static final int CHANGE_BACKGROUND_COLOR = 0X2;
}
@@ -0,0 +1,112 @@
package me.majiajie.pagerbottomtabstrip;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.ViewPager;
import me.majiajie.pagerbottomtabstrip.internal.Utils;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
public class NavigationController implements ItemController, BottomLayoutController {
private BottomLayoutController mBottomLayoutController;
private ItemController mItemController;
protected NavigationController(BottomLayoutController bottomLayoutController, ItemController itemController) {
mBottomLayoutController = bottomLayoutController;
mItemController = itemController;
}
@Override
public void setSelect(int index) {
mItemController.setSelect(index);
}
@Override
public void setSelect(int index, boolean listener) {
mItemController.setSelect(index, listener);
}
@Override
public void setMessageNumber(int index, int number) {
mItemController.setMessageNumber(index, number);
}
@Override
public void setHasMessage(int index, boolean hasMessage) {
mItemController.setHasMessage(index, hasMessage);
}
@Override
public void addTabItemSelectedListener(@NonNull OnTabItemSelectedListener listener) {
mItemController.addTabItemSelectedListener(listener);
}
@Override
public void addSimpleTabItemSelectedListener(@NonNull SimpleTabItemSelectedListener listener) {
mItemController.addSimpleTabItemSelectedListener(listener);
}
@Override
public void setTitle(int index,@NonNull String title) {
mItemController.setTitle(index, title);
}
@Override
public void setDefaultDrawable(int index,@NonNull Drawable drawable) {
mItemController.setDefaultDrawable(index, drawable);
}
@Override
public void setSelectedDrawable(int index,@NonNull Drawable drawable) {
mItemController.setSelectedDrawable(index, drawable);
}
@Override
public int getSelected() {
return mItemController.getSelected();
}
@Override
public int getItemCount() {
return mItemController.getItemCount();
}
@Override
public String getItemTitle(int index) {
return mItemController.getItemTitle(index);
}
@Override
public boolean removeItem(int index) {
return mItemController.removeItem(index);
}
@Override
public void addMaterialItem(int index,@NonNull Drawable defaultDrawable,@NonNull Drawable selectedDrawable,@NonNull String title, int selectedColor) {
mItemController.addMaterialItem(index, Utils.newDrawable(defaultDrawable), Utils.newDrawable(selectedDrawable), title, selectedColor);
}
@Override
public void addCustomItem(int index,@NonNull BaseTabItem item) {
mItemController.addCustomItem(index, item);
}
@Override
public void setupWithViewPager(@NonNull ViewPager viewPager) {
mBottomLayoutController.setupWithViewPager(viewPager);
}
@Override
public void hideBottomLayout() {
mBottomLayoutController.hideBottomLayout();
}
@Override
public void showBottomLayout() {
mBottomLayoutController.showBottomLayout();
}
}
@@ -0,0 +1,666 @@
package me.majiajie.pagerbottomtabstrip;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;
import java.util.ArrayList;
import java.util.List;
import me.majiajie.pagerbottomtabstrip.internal.CustomItemVerticalLayout;
import me.majiajie.pagerbottomtabstrip.internal.MaterialItemLayout;
import me.majiajie.pagerbottomtabstrip.internal.MaterialItemVerticalLayout;
import me.majiajie.pagerbottomtabstrip.internal.OldCustomItemLayout;
import me.majiajie.pagerbottomtabstrip.internal.Utils;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.item.MaterialItemView;
import me.majiajie.pagerbottomtabstrip.item.OnlyIconMaterialItemView;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
/**
* 导航视图
*/
public class OldPageNavigationView extends ViewGroup {
private int mTabPaddingTop;
private int mTabPaddingBottom;
private NavigationController mNavigationController;
private ViewPagerPageChangeListener mPageChangeListener;
private ViewPager mViewPager;
private boolean mEnableVerticalLayout;
private OnTabItemSelectedListener mTabItemListener = new OnTabItemSelectedListener() {
@Override
public void onSelected(int index, int old) {
if (mViewPager != null) {
mViewPager.setCurrentItem(index, false);
}
}
@Override
public void onRepeat(int index) {
}
};
public OldPageNavigationView(Context context) {
this(context, null);
}
public OldPageNavigationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public OldPageNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setPadding(0, 0, 0, 0);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.PageNavigationView);
if (a.hasValue(R.styleable.PageNavigationView_NavigationPaddingTop)) {
mTabPaddingTop = a.getDimensionPixelSize(R.styleable.PageNavigationView_NavigationPaddingTop, 0);
}
if (a.hasValue(R.styleable.PageNavigationView_NavigationPaddingBottom)) {
mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.PageNavigationView_NavigationPaddingBottom, 0);
}
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
measureChild(child, widthMeasureSpec,heightMeasureSpec);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}
setMeasuredDimension(maxWidth, maxHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
final int width = r - l;
final int height = b - t;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.layout(0, 0, width, height);
}
}
@Override
public CharSequence getAccessibilityClassName() {
return OldPageNavigationView.class.getName();
}
/**
* 构建 Material Desgin 风格的导航栏
*/
public MaterialBuilder material() {
return new MaterialBuilder();
}
/**
* 构建自定义导航栏
*/
public CustomBuilder custom() {
return new CustomBuilder();
}
/**
* 构建 自定义 的导航栏
*/
public class CustomBuilder {
private List<BaseTabItem> items;
private boolean enableVerticalLayout = false;
private boolean animateLayoutChanges = false;
CustomBuilder() {
items = new ArrayList<>();
}
/**
* 完成构建
*
* @return {@link NavigationController},通过它进行后续操作
* @throws RuntimeException 没有添加导航项时会抛出
*/
public NavigationController build() {
mEnableVerticalLayout = enableVerticalLayout;
//未添加任何按钮
if (items.size() == 0) {
throw new RuntimeException("must add a navigation item");
}
ItemController itemController;
if (enableVerticalLayout) {//垂直布局
CustomItemVerticalLayout verticalItemLayout = new CustomItemVerticalLayout(getContext());
verticalItemLayout.initialize(items, animateLayoutChanges);
verticalItemLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
OldPageNavigationView.this.removeAllViews();
OldPageNavigationView.this.addView(verticalItemLayout);
itemController = verticalItemLayout;
} else {//水平布局
OldCustomItemLayout customItemLayout = new OldCustomItemLayout(getContext());
customItemLayout.initialize(items, animateLayoutChanges);
customItemLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
OldPageNavigationView.this.removeAllViews();
OldPageNavigationView.this.addView(customItemLayout);
itemController = customItemLayout;
}
mNavigationController = new NavigationController(new Controller(), itemController);
mNavigationController.addTabItemSelectedListener(mTabItemListener);
return mNavigationController;
}
/**
* 添加一个导航按钮
*
* @param baseTabItem {@link BaseTabItem},所有自定义Item都必须继承它
* @return {@link CustomBuilder}
*/
public CustomBuilder addItem(BaseTabItem baseTabItem) {
items.add(baseTabItem);
return CustomBuilder.this;
}
/**
* 使用垂直布局
*
* @return {@link CustomBuilder}
*/
public CustomBuilder enableVerticalLayout() {
enableVerticalLayout = true;
return CustomBuilder.this;
}
/**
* 通过{@link NavigationController}动态移除/添加导航项时,显示默认的布局动画
*
* @return {@link CustomBuilder}
*/
public CustomBuilder enableAnimateLayoutChanges() {
animateLayoutChanges = true;
return CustomBuilder.this;
}
}
/**
* 构建 Material Desgin 风格的导航栏
*/
public class MaterialBuilder {
private final int DEFAULT_COLOR = 0x56000000;
private List<MaterialItemViewData> itemDatas;
private int defaultColor;
private int mode;
private int messageBackgroundColor;
private int messageNumberColor;
private boolean enableVerticalLayout = false;
private boolean doTintIcon = true;
private boolean animateLayoutChanges = false;
MaterialBuilder() {
itemDatas = new ArrayList<>();
}
/**
* 完成构建
*
* @return {@link NavigationController},通过它进行后续操作
* @throws RuntimeException 没有添加导航项时会抛出
*/
@NonNull
public NavigationController build() {
mEnableVerticalLayout = enableVerticalLayout;
// 未添加任何按钮
if (itemDatas.isEmpty()) {
throw new RuntimeException("must add a navigation item");
}
// 设置默认颜色
if (defaultColor == 0) {
defaultColor = DEFAULT_COLOR;
}
ItemController itemController;
if (enableVerticalLayout) {//垂直布局
List<BaseTabItem> items = new ArrayList<>();
for (MaterialItemViewData data : itemDatas) {
OnlyIconMaterialItemView materialItemView = new OnlyIconMaterialItemView(getContext());
materialItemView.initialization(data.title, data.drawable, data.checkedDrawable, doTintIcon, defaultColor, data.chekedColor);
//检查是否设置了消息圆点的颜色
if (messageBackgroundColor != 0) {
materialItemView.setMessageBackgroundColor(messageBackgroundColor);
}
//检查是否设置了消息数字的颜色
if (messageNumberColor != 0) {
materialItemView.setMessageNumberColor(messageNumberColor);
}
items.add(materialItemView);
}
MaterialItemVerticalLayout materialItemVerticalLayout = new MaterialItemVerticalLayout(getContext());
materialItemVerticalLayout.initialize(items, animateLayoutChanges, doTintIcon, defaultColor);
materialItemVerticalLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
OldPageNavigationView.this.removeAllViews();
OldPageNavigationView.this.addView(materialItemVerticalLayout);
itemController = materialItemVerticalLayout;
} else {//水平布局
boolean changeBackground = (mode & MaterialMode.CHANGE_BACKGROUND_COLOR) > 0;
List<MaterialItemView> items = new ArrayList<>();
List<Integer> checkedColors = new ArrayList<>();
for (MaterialItemViewData data : itemDatas) {
// 记录设置的选中颜色
checkedColors.add(data.chekedColor);
MaterialItemView materialItemView = new MaterialItemView(getContext());
// 初始化Item,需要切换背景颜色就将选中颜色改成白色
materialItemView.initialization(data.title, data.drawable, data.checkedDrawable, doTintIcon, defaultColor, changeBackground ? Color.WHITE : data.chekedColor);
//检查是否设置了消息圆点的颜色
if (messageBackgroundColor != 0) {
materialItemView.setMessageBackgroundColor(messageBackgroundColor);
}
//检查是否设置了消息数字的颜色
if (messageNumberColor != 0) {
materialItemView.setMessageNumberColor(messageNumberColor);
}
items.add(materialItemView);
}
MaterialItemLayout materialItemLayout = new MaterialItemLayout(getContext());
materialItemLayout.initialize(items, checkedColors, mode, animateLayoutChanges, doTintIcon, defaultColor);
materialItemLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
OldPageNavigationView.this.removeAllViews();
OldPageNavigationView.this.addView(materialItemLayout);
itemController = materialItemLayout;
}
mNavigationController = new NavigationController(new Controller(), itemController);
mNavigationController.addTabItemSelectedListener(mTabItemListener);
return mNavigationController;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @NonNull String title) {
addItem(drawableRes, drawableRes, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param checkedDrawableRes 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @DrawableRes int checkedDrawableRes, @NonNull String title) {
addItem(drawableRes, checkedDrawableRes, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @NonNull String title, @ColorInt int chekedColor) {
addItem(drawableRes, drawableRes, title, chekedColor);
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param checkedDrawableRes 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
* @throws Resources.NotFoundException drawable 资源获取异常
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @DrawableRes int checkedDrawableRes, @NonNull String title, @ColorInt int chekedColor) {
Drawable defaultDrawable = ContextCompat.getDrawable(getContext(), drawableRes);
Drawable checkDrawable = ContextCompat.getDrawable(getContext(), checkedDrawableRes);
if (defaultDrawable == null) {
throw new Resources.NotFoundException("Resource ID " + Integer.toHexString(drawableRes));
}
if (checkDrawable == null) {
throw new Resources.NotFoundException("Resource ID " + Integer.toHexString(checkedDrawableRes));
}
addItem(defaultDrawable, checkDrawable, title, chekedColor);
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull String title) {
addItem(drawable, drawable, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param checkedDrawable 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull Drawable checkedDrawable, @NonNull String title) {
addItem(drawable, checkedDrawable, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull String title, @ColorInt int chekedColor) {
addItem(drawable, drawable, title, chekedColor);
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param checkedDrawable 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull Drawable checkedDrawable, @NonNull String title, @ColorInt int chekedColor) {
MaterialItemViewData data = new MaterialItemViewData();
data.drawable = Utils.newDrawable(drawable);
data.checkedDrawable = Utils.newDrawable(checkedDrawable);
data.title = title;
data.chekedColor = chekedColor;
itemDatas.add(data);
return MaterialBuilder.this;
}
/**
* 设置导航按钮的默认(未选中状态)颜色
*
* @param color 16进制整形表示的颜色,例如红色:0xFFFF0000
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setDefaultColor(@ColorInt int color) {
defaultColor = color;
return MaterialBuilder.this;
}
/**
* 设置消息圆点的颜色
*
* @param color 16进制整形表示的颜色,例如红色:0xFFFF0000
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setMessageBackgroundColor(@ColorInt int color) {
messageBackgroundColor = color;
return MaterialBuilder.this;
}
/**
* 设置消息数字的颜色
*
* @param color 16进制整形表示的颜色,例如红色:0xFFFF0000
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setMessageNumberColor(@ColorInt int color) {
messageNumberColor = color;
return MaterialBuilder.this;
}
/**
* 设置模式(在垂直布局中无效)。默认文字一直显示,且背景色不变。
* 可以通过{@link MaterialMode}选择模式。
* <p>例如:</p>
* {@code MaterialMode.HIDE_TEXT}
* <p>或者多选:</p>
* {@code MaterialMode.HIDE_TEXT | MaterialMode.CHANGE_BACKGROUND_COLOR}
*
* @param mode {@link MaterialMode}
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setMode(int mode) {
MaterialBuilder.this.mode = mode;
return MaterialBuilder.this;
}
/**
* 使用垂直布局
*
* @return {@link MaterialBuilder}
*/
public MaterialBuilder enableVerticalLayout() {
enableVerticalLayout = true;
return MaterialBuilder.this;
}
/**
* 不对图标进行染色
*
* @return {@link MaterialBuilder}
*/
public MaterialBuilder dontTintIcon() {
doTintIcon = false;
return MaterialBuilder.this;
}
/**
* 通过{@link NavigationController}动态移除/添加导航项时,显示默认的布局动画
*
* @return {@link MaterialBuilder}
*/
public MaterialBuilder enableAnimateLayoutChanges() {
animateLayoutChanges = true;
return MaterialBuilder.this;
}
}
/**
* 材料设计的单项视图信息
*/
private static class MaterialItemViewData {
Drawable drawable;
Drawable checkedDrawable;
String title;
@ColorInt
int chekedColor;
}
/**
* 实现控制接口
*/
private class Controller implements BottomLayoutController {
private ObjectAnimator animator;
private boolean hide = false;
@Override
public void setupWithViewPager(ViewPager viewPager) {
if (viewPager == null) {
return;
}
mViewPager = viewPager;
if (mPageChangeListener != null) {
mViewPager.removeOnPageChangeListener(mPageChangeListener);
} else {
mPageChangeListener = new ViewPagerPageChangeListener();
}
if (mNavigationController != null) {
int n = mViewPager.getCurrentItem();
if (mNavigationController.getSelected() != n) {
mNavigationController.setSelect(n);
}
mViewPager.addOnPageChangeListener(mPageChangeListener);
}
}
@Override
public void hideBottomLayout() {
if (!hide) {
hide = true;
getAnimator().start();
}
}
@Override
public void showBottomLayout() {
if (hide) {
hide = false;
getAnimator().reverse();
}
}
private ObjectAnimator getAnimator() {
if (animator == null) {
if (mEnableVerticalLayout) {// 垂直布局向左隐藏
animator = ObjectAnimator.ofFloat(
OldPageNavigationView.this, "translationX", 0, -OldPageNavigationView.this.getWidth());
} else {// 水平布局向下隐藏
animator = ObjectAnimator.ofFloat(
OldPageNavigationView.this, "translationY", 0, OldPageNavigationView.this.getHeight());
}
animator.setDuration(300);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
}
return animator;
}
}
private class ViewPagerPageChangeListener implements ViewPager.OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (mNavigationController != null && mNavigationController.getSelected() != position) {
mNavigationController.setSelect(position);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
private static final String INSTANCE_STATUS = "INSTANCE_STATUS";
private final String STATUS_SELECTED = "STATUS_SELECTED";
@Override
protected Parcelable onSaveInstanceState() {
if (mNavigationController == null) {
return super.onSaveInstanceState();
}
Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE_STATUS, super.onSaveInstanceState());
bundle.putInt(STATUS_SELECTED, mNavigationController.getSelected());
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
int selected = bundle.getInt(STATUS_SELECTED, 0);
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUS));
if (selected != 0 && mNavigationController != null) {
mNavigationController.setSelect(selected);
}
return;
}
super.onRestoreInstanceState(state);
}
}
@@ -0,0 +1,665 @@
package me.majiajie.pagerbottomtabstrip;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;
import me.majiajie.pagerbottomtabstrip.internal.CustomItemLayout;
import me.majiajie.pagerbottomtabstrip.internal.CustomItemVerticalLayout;
import me.majiajie.pagerbottomtabstrip.internal.MaterialItemLayout;
import me.majiajie.pagerbottomtabstrip.internal.MaterialItemVerticalLayout;
import me.majiajie.pagerbottomtabstrip.internal.Utils;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.item.MaterialItemView;
import me.majiajie.pagerbottomtabstrip.item.OnlyIconMaterialItemView;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
/**
* 导航视图
*/
public class PageNavigationView extends ViewGroup {
private int mTabPaddingTop;
private int mTabPaddingBottom;
private NavigationController mNavigationController;
private ViewPagerPageChangeListener mPageChangeListener;
private ViewPager mViewPager;
private boolean mEnableVerticalLayout;
private OnTabItemSelectedListener mTabItemListener = new OnTabItemSelectedListener() {
@Override
public void onSelected(int index, int old) {
if (mViewPager != null) {
mViewPager.setCurrentItem(index, false);
}
}
@Override
public void onRepeat(int index) {
}
};
public PageNavigationView(Context context) {
this(context, null);
}
public PageNavigationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PageNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setPadding(0, 0, 0, 0);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.PageNavigationView);
if (a.hasValue(R.styleable.PageNavigationView_NavigationPaddingTop)) {
mTabPaddingTop = a.getDimensionPixelSize(R.styleable.PageNavigationView_NavigationPaddingTop, 0);
}
if (a.hasValue(R.styleable.PageNavigationView_NavigationPaddingBottom)) {
mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.PageNavigationView_NavigationPaddingBottom, 0);
}
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
measureChild(child, widthMeasureSpec,heightMeasureSpec);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}
setMeasuredDimension(maxWidth, maxHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
final int width = r - l;
final int height = b - t;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.layout(0, 0, width, height);
}
}
@Override
public CharSequence getAccessibilityClassName() {
return PageNavigationView.class.getName();
}
/**
* 构建 Material Desgin 风格的导航栏
*/
public MaterialBuilder material() {
return new MaterialBuilder();
}
/**
* 构建自定义导航栏
*/
public CustomBuilder custom() {
return new CustomBuilder();
}
/**
* 构建 自定义 的导航栏
*/
public class CustomBuilder {
private List<BaseTabItem> items;
private boolean enableVerticalLayout = false;
private boolean animateLayoutChanges = false;
CustomBuilder() {
items = new ArrayList<>();
}
/**
* 完成构建
*
* @return {@link NavigationController},通过它进行后续操作
* @throws RuntimeException 没有添加导航项时会抛出
*/
public NavigationController build() {
mEnableVerticalLayout = enableVerticalLayout;
//未添加任何按钮
if (items.size() == 0) {
throw new RuntimeException("must add a navigation item");
}
ItemController itemController;
if (enableVerticalLayout) {//垂直布局
CustomItemVerticalLayout verticalItemLayout = new CustomItemVerticalLayout(getContext());
verticalItemLayout.initialize(items, animateLayoutChanges);
verticalItemLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
PageNavigationView.this.removeAllViews();
PageNavigationView.this.addView(verticalItemLayout);
itemController = verticalItemLayout;
} else {//水平布局
CustomItemLayout customItemLayout = new CustomItemLayout(getContext());
customItemLayout.initialize(items, animateLayoutChanges);
customItemLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
PageNavigationView.this.removeAllViews();
PageNavigationView.this.addView(customItemLayout);
itemController = customItemLayout;
}
mNavigationController = new NavigationController(new Controller(), itemController);
mNavigationController.addTabItemSelectedListener(mTabItemListener);
return mNavigationController;
}
/**
* 添加一个导航按钮
*
* @param baseTabItem {@link BaseTabItem},所有自定义Item都必须继承它
* @return {@link CustomBuilder}
*/
public CustomBuilder addItem(BaseTabItem baseTabItem) {
items.add(baseTabItem);
return CustomBuilder.this;
}
/**
* 使用垂直布局
*
* @return {@link CustomBuilder}
*/
public CustomBuilder enableVerticalLayout() {
enableVerticalLayout = true;
return CustomBuilder.this;
}
/**
* 通过{@link NavigationController}动态移除/添加导航项时,显示默认的布局动画
*
* @return {@link CustomBuilder}
*/
public CustomBuilder enableAnimateLayoutChanges() {
animateLayoutChanges = true;
return CustomBuilder.this;
}
}
/**
* 构建 Material Desgin 风格的导航栏
*/
public class MaterialBuilder {
private final int DEFAULT_COLOR = 0x56000000;
private List<MaterialItemViewData> itemDatas;
private int defaultColor;
private int mode;
private int messageBackgroundColor;
private int messageNumberColor;
private boolean enableVerticalLayout = false;
private boolean doTintIcon = true;
private boolean animateLayoutChanges = false;
MaterialBuilder() {
itemDatas = new ArrayList<>();
}
/**
* 完成构建
*
* @return {@link NavigationController},通过它进行后续操作
* @throws RuntimeException 没有添加导航项时会抛出
*/
@NonNull
public NavigationController build() {
mEnableVerticalLayout = enableVerticalLayout;
// 未添加任何按钮
if (itemDatas.isEmpty()) {
throw new RuntimeException("must add a navigation item");
}
// 设置默认颜色
if (defaultColor == 0) {
defaultColor = DEFAULT_COLOR;
}
ItemController itemController;
if (enableVerticalLayout) {//垂直布局
List<BaseTabItem> items = new ArrayList<>();
for (MaterialItemViewData data : itemDatas) {
OnlyIconMaterialItemView materialItemView = new OnlyIconMaterialItemView(getContext());
materialItemView.initialization(data.title, data.drawable, data.checkedDrawable, doTintIcon, defaultColor, data.chekedColor);
//检查是否设置了消息圆点的颜色
if (messageBackgroundColor != 0) {
materialItemView.setMessageBackgroundColor(messageBackgroundColor);
}
//检查是否设置了消息数字的颜色
if (messageNumberColor != 0) {
materialItemView.setMessageNumberColor(messageNumberColor);
}
items.add(materialItemView);
}
MaterialItemVerticalLayout materialItemVerticalLayout = new MaterialItemVerticalLayout(getContext());
materialItemVerticalLayout.initialize(items, animateLayoutChanges, doTintIcon, defaultColor);
materialItemVerticalLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
PageNavigationView.this.removeAllViews();
PageNavigationView.this.addView(materialItemVerticalLayout);
itemController = materialItemVerticalLayout;
} else {//水平布局
boolean changeBackground = (mode & MaterialMode.CHANGE_BACKGROUND_COLOR) > 0;
List<MaterialItemView> items = new ArrayList<>();
List<Integer> checkedColors = new ArrayList<>();
for (MaterialItemViewData data : itemDatas) {
// 记录设置的选中颜色
checkedColors.add(data.chekedColor);
MaterialItemView materialItemView = new MaterialItemView(getContext());
// 初始化Item,需要切换背景颜色就将选中颜色改成白色
materialItemView.initialization(data.title, data.drawable, data.checkedDrawable, doTintIcon, defaultColor, changeBackground ? Color.WHITE : data.chekedColor);
//检查是否设置了消息圆点的颜色
if (messageBackgroundColor != 0) {
materialItemView.setMessageBackgroundColor(messageBackgroundColor);
}
//检查是否设置了消息数字的颜色
if (messageNumberColor != 0) {
materialItemView.setMessageNumberColor(messageNumberColor);
}
items.add(materialItemView);
}
MaterialItemLayout materialItemLayout = new MaterialItemLayout(getContext());
materialItemLayout.initialize(items, checkedColors, mode, animateLayoutChanges, doTintIcon, defaultColor);
materialItemLayout.setPadding(0, mTabPaddingTop, 0, mTabPaddingBottom);
PageNavigationView.this.removeAllViews();
PageNavigationView.this.addView(materialItemLayout);
itemController = materialItemLayout;
}
mNavigationController = new NavigationController(new Controller(), itemController);
mNavigationController.addTabItemSelectedListener(mTabItemListener);
return mNavigationController;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @NonNull String title) {
addItem(drawableRes, drawableRes, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param checkedDrawableRes 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @DrawableRes int checkedDrawableRes, @NonNull String title) {
addItem(drawableRes, checkedDrawableRes, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @NonNull String title, @ColorInt int chekedColor) {
addItem(drawableRes, drawableRes, title, chekedColor);
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawableRes 图标资源
* @param checkedDrawableRes 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
* @throws Resources.NotFoundException drawable 资源获取异常
*/
public MaterialBuilder addItem(@DrawableRes int drawableRes, @DrawableRes int checkedDrawableRes, @NonNull String title, @ColorInt int chekedColor) {
Drawable defaultDrawable = ContextCompat.getDrawable(getContext(), drawableRes);
Drawable checkDrawable = ContextCompat.getDrawable(getContext(), checkedDrawableRes);
if (defaultDrawable == null) {
throw new Resources.NotFoundException("Resource ID " + Integer.toHexString(drawableRes));
}
if (checkDrawable == null) {
throw new Resources.NotFoundException("Resource ID " + Integer.toHexString(checkedDrawableRes));
}
addItem(defaultDrawable, checkDrawable, title, chekedColor);
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull String title) {
addItem(drawable, drawable, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param checkedDrawable 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull Drawable checkedDrawable, @NonNull String title) {
addItem(drawable, checkedDrawable, title, Utils.getColorPrimary(getContext()));
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull String title, @ColorInt int chekedColor) {
addItem(drawable, drawable, title, chekedColor);
return MaterialBuilder.this;
}
/**
* 添加一个导航按钮
*
* @param drawable 图标资源
* @param checkedDrawable 选中时的图标资源
* @param title 显示文字内容.尽量简短
* @param chekedColor 选中的颜色
* @return {@link MaterialBuilder}
*/
public MaterialBuilder addItem(@NonNull Drawable drawable, @NonNull Drawable checkedDrawable, @NonNull String title, @ColorInt int chekedColor) {
MaterialItemViewData data = new MaterialItemViewData();
data.drawable = Utils.newDrawable(drawable);
data.checkedDrawable = Utils.newDrawable(checkedDrawable);
data.title = title;
data.chekedColor = chekedColor;
itemDatas.add(data);
return MaterialBuilder.this;
}
/**
* 设置导航按钮的默认(未选中状态)颜色
*
* @param color 16进制整形表示的颜色,例如红色:0xFFFF0000
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setDefaultColor(@ColorInt int color) {
defaultColor = color;
return MaterialBuilder.this;
}
/**
* 设置消息圆点的颜色
*
* @param color 16进制整形表示的颜色,例如红色:0xFFFF0000
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setMessageBackgroundColor(@ColorInt int color) {
messageBackgroundColor = color;
return MaterialBuilder.this;
}
/**
* 设置消息数字的颜色
*
* @param color 16进制整形表示的颜色,例如红色:0xFFFF0000
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setMessageNumberColor(@ColorInt int color) {
messageNumberColor = color;
return MaterialBuilder.this;
}
/**
* 设置模式(在垂直布局中无效)。默认文字一直显示,且背景色不变。
* 可以通过{@link MaterialMode}选择模式。
* <p>例如:</p>
* {@code MaterialMode.HIDE_TEXT}
* <p>或者多选:</p>
* {@code MaterialMode.HIDE_TEXT | MaterialMode.CHANGE_BACKGROUND_COLOR}
*
* @param mode {@link MaterialMode}
* @return {@link MaterialBuilder}
*/
public MaterialBuilder setMode(int mode) {
MaterialBuilder.this.mode = mode;
return MaterialBuilder.this;
}
/**
* 使用垂直布局
*
* @return {@link MaterialBuilder}
*/
public MaterialBuilder enableVerticalLayout() {
enableVerticalLayout = true;
return MaterialBuilder.this;
}
/**
* 不对图标进行染色
*
* @return {@link MaterialBuilder}
*/
public MaterialBuilder dontTintIcon() {
doTintIcon = false;
return MaterialBuilder.this;
}
/**
* 通过{@link NavigationController}动态移除/添加导航项时,显示默认的布局动画
*
* @return {@link MaterialBuilder}
*/
public MaterialBuilder enableAnimateLayoutChanges() {
animateLayoutChanges = true;
return MaterialBuilder.this;
}
}
/**
* 材料设计的单项视图信息
*/
private static class MaterialItemViewData {
Drawable drawable;
Drawable checkedDrawable;
String title;
@ColorInt
int chekedColor;
}
/**
* 实现控制接口
*/
private class Controller implements BottomLayoutController {
private ObjectAnimator animator;
private boolean hide = false;
@Override
public void setupWithViewPager(ViewPager viewPager) {
if (viewPager == null) {
return;
}
mViewPager = viewPager;
if (mPageChangeListener != null) {
mViewPager.removeOnPageChangeListener(mPageChangeListener);
} else {
mPageChangeListener = new ViewPagerPageChangeListener();
}
if (mNavigationController != null) {
int n = mViewPager.getCurrentItem();
if (mNavigationController.getSelected() != n) {
mNavigationController.setSelect(n);
}
mViewPager.addOnPageChangeListener(mPageChangeListener);
}
}
@Override
public void hideBottomLayout() {
if (!hide) {
hide = true;
getAnimator().start();
}
}
@Override
public void showBottomLayout() {
if (hide) {
hide = false;
getAnimator().reverse();
}
}
private ObjectAnimator getAnimator() {
if (animator == null) {
if (mEnableVerticalLayout) {// 垂直布局向左隐藏
animator = ObjectAnimator.ofFloat(
PageNavigationView.this, "translationX", 0, -PageNavigationView.this.getWidth());
} else {// 水平布局向下隐藏
animator = ObjectAnimator.ofFloat(
PageNavigationView.this, "translationY", 0, PageNavigationView.this.getHeight());
}
animator.setDuration(300);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
}
return animator;
}
}
private class ViewPagerPageChangeListener implements ViewPager.OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (mNavigationController != null && mNavigationController.getSelected() != position) {
mNavigationController.setSelect(position);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
private static final String INSTANCE_STATUS = "INSTANCE_STATUS";
private final String STATUS_SELECTED = "STATUS_SELECTED";
@Override
protected Parcelable onSaveInstanceState() {
if (mNavigationController == null) {
return super.onSaveInstanceState();
}
Bundle bundle = new Bundle();
bundle.putParcelable(INSTANCE_STATUS, super.onSaveInstanceState());
bundle.putInt(STATUS_SELECTED, mNavigationController.getSelected());
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
int selected = bundle.getInt(STATUS_SELECTED, 0);
super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATUS));
if (selected != 0 && mNavigationController != null) {
mNavigationController.setSelect(selected);
}
return;
}
super.onRestoreInstanceState(state);
}
}
@@ -0,0 +1,97 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* A simple ViewGroup that aligns all the views inside on a baseline. Note: bottom padding for this
* view will be measured starting from the baseline.
*/
public class BaselineLayout extends ViewGroup {
private int mBaseline = -1;
public BaselineLayout(Context context) {
super(context, null, 0);
}
public BaselineLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public BaselineLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxWidth = 0;
int maxHeight = 0;
int maxChildBaseline = -1;
int maxChildDescent = -1;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
measureChild(child, widthMeasureSpec, heightMeasureSpec);
final int baseline = child.getBaseline();
if (baseline != -1) {
maxChildBaseline = Math.max(maxChildBaseline, baseline);
maxChildDescent = Math.max(maxChildDescent, child.getMeasuredHeight() - baseline);
}
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
childState = childState | child.getMeasuredState();
}
if (maxChildBaseline != -1) {
maxChildDescent = Math.max(maxChildDescent, getPaddingBottom());
maxHeight = Math.max(maxHeight, maxChildBaseline + maxChildDescent);
mBaseline = maxChildBaseline;
}
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(
View.resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
View.resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int parentLeft = getPaddingLeft();
final int parentRight = right - left - getPaddingRight();
final int parentContentWidth = parentRight - parentLeft;
final int parentTop = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
final int childLeft = parentLeft + (parentContentWidth - width) / 2;
final int childTop;
if (mBaseline != -1 && child.getBaseline() != -1) {
childTop = parentTop + mBaseline - child.getBaseline();
} else {
childTop = parentTop;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
@Override
public int getBaseline() {
return mBaseline;
}
}
@@ -0,0 +1,277 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.animation.LayoutTransition;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import androidx.core.view.ViewCompat;
import me.majiajie.pagerbottomtabstrip.ItemController;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
/**
* 存放自定义项的布局
*/
public class CustomItemLayout extends ViewGroup implements ItemController {
private final List<BaseTabItem> mItems = new ArrayList<>();
private final List<OnTabItemSelectedListener> mListeners = new ArrayList<>();
private final List<SimpleTabItemSelectedListener> mSimpleListeners = new ArrayList<>();
private int mSelected = -1;
public CustomItemLayout(Context context) {
this(context, null);
}
public CustomItemLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutTransition(new LayoutTransition());
}
public void initialize(List<BaseTabItem> items, boolean animateLayoutChanges) {
mItems.clear();
mItems.addAll(items);
if (animateLayoutChanges) {
setLayoutTransition(new LayoutTransition());
}
//添加按钮到布局,并注册点击事件
int n = mItems.size();
for (int i = 0; i < n; i++) {
final BaseTabItem tabItem = mItems.get(i);
tabItem.setChecked(false);
this.addView(tabItem);
tabItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(tabItem);
if (index >= 0) {
setSelect(index);
}
}
});
}
//默认选中第一项
mSelected = 0;
mItems.get(0).setChecked(true);
}
/**
* dip转化像素
* @param dipValue
* @return
*/
public static int dip2px(float dipValue){
final float scale = Resources.getSystem().getDisplayMetrics().density;
return (int)(dipValue * scale + 0.5f);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int n = getChildCount();
int visableChildCount = 0;
for (int i = 0; i < n; i++) {
if (getChildAt(i).getVisibility() != GONE) {
visableChildCount++;
}
}
if (visableChildCount == 0) {
return;
}
final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec) / visableChildCount, MeasureSpec.EXACTLY);
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(0, MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingTop()), MeasureSpec.EXACTLY);
for (int i = 0; i < n; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(widthMeasureSpec, childHeightMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int width = right - left;
final int height = bottom - top;
//只支持top、bottom的padding
final int padding_top = getPaddingTop();
final int padding_bottom = getPaddingBottom();
int used = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
child.layout(width - used - child.getMeasuredWidth(), padding_top, width - used, height - padding_bottom);
} else {
child.layout(used, padding_top, child.getMeasuredWidth() + used, height - padding_bottom);
}
used += child.getMeasuredWidth()+dip2px(33);
}
}
@Override
public CharSequence getAccessibilityClassName() {
return CustomItemLayout.class.getName();
}
@Override
public void setSelect(int index) {
setSelect(index, true);
}
@Override
public void setSelect(int index, boolean needListener) {
//重复选择
if (index == mSelected) {
if (needListener) {
for (OnTabItemSelectedListener listener : mListeners) {
mItems.get(mSelected).onRepeat();
listener.onRepeat(mSelected);
}
}
return;
}
//记录前一个选中项和当前选中项
int oldSelected = mSelected;
mSelected = index;
//前一个选中项必须不小于0才有效
if (oldSelected >= 0) {
mItems.get(oldSelected).setChecked(false);
}
mItems.get(mSelected).setChecked(true);
if (needListener) {
//事件回调
for (OnTabItemSelectedListener listener : mListeners) {
listener.onSelected(mSelected, oldSelected);
}
for (SimpleTabItemSelectedListener listener : mSimpleListeners) {
listener.onSelected(mSelected, oldSelected);
}
}
}
@Override
public void setMessageNumber(int index, int number) {
mItems.get(index).setMessageNumber(number);
}
@Override
public void setHasMessage(int index, boolean hasMessage) {
mItems.get(index).setHasMessage(hasMessage);
}
@Override
public void addTabItemSelectedListener(OnTabItemSelectedListener listener) {
mListeners.add(listener);
}
@Override
public void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener) {
mSimpleListeners.add(listener);
}
@Override
public void setTitle(int index, String title) {
mItems.get(index).setTitle(title);
}
@Override
public void setDefaultDrawable(int index, Drawable drawable) {
mItems.get(index).setDefaultDrawable(drawable);
}
@Override
public void setSelectedDrawable(int index, Drawable drawable) {
mItems.get(index).setSelectedDrawable(drawable);
}
@Override
public int getSelected() {
return mSelected;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public String getItemTitle(int index) {
return mItems.get(index).getTitle();
}
@Override
public boolean removeItem(int index) {
if (index == mSelected || index >= mItems.size() || index < 0) {
return false;
}
if (mSelected > index) {
mSelected--;
}
this.removeViewAt(index);
mItems.remove(index);
return true;
}
@Override
public void addMaterialItem(int index, Drawable defaultDrawable, Drawable selectedDrawable, String title, int selectedColor) {
// nothing
}
@Override
public void addCustomItem(int index, final BaseTabItem item) {
item.setChecked(false);
item.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(item);
if (index >= 0) {
setSelect(index);
}
}
});
if (index >= mItems.size()) {
mItems.add(index, item);
this.addView(item);
} else {
mItems.add(index, item);
this.addView(item, index);
}
}
}
@@ -0,0 +1,254 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import me.majiajie.pagerbottomtabstrip.ItemController;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
/**
* Created by mjj on 2017/9/27
*/
public class CustomItemVerticalLayout extends ViewGroup implements ItemController {
private final List<BaseTabItem> mItems = new ArrayList<>();
private final List<OnTabItemSelectedListener> mListeners = new ArrayList<>();
private final List<SimpleTabItemSelectedListener> mSimpleListeners = new ArrayList<>();
private int mSelected = -1;
public CustomItemVerticalLayout(Context context) {
this(context, null);
}
public CustomItemVerticalLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomItemVerticalLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutTransition(new LayoutTransition());
}
public void initialize(List<BaseTabItem> items, boolean animateLayoutChanges) {
mItems.clear();
mItems.addAll(items);
if (animateLayoutChanges) {
setLayoutTransition(new LayoutTransition());
}
//添加按钮到布局,并注册点击事件
int n = mItems.size();
for (int i = 0; i < n; i++) {
final BaseTabItem tabItem = mItems.get(i);
tabItem.setChecked(false);
this.addView(tabItem);
tabItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(tabItem);
if (index >= 0) {
setSelect(index);
}
}
});
}
//默认选中第一项
mSelected = 0;
mItems.get(0).setChecked(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED);
final int childwidthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY);
int totalHeight = getPaddingTop() + getPaddingBottom();
final int n = getChildCount();
for (int i = 0; i < n; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final LayoutParams lp = child.getLayoutParams();
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
getPaddingTop() + getPaddingBottom(), lp.height);
child.measure(childwidthMeasureSpec, childHeightMeasureSpec);
totalHeight += child.getMeasuredHeight();
}
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), totalHeight);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
//只支持top的padding
int used = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.layout(0, used, child.getMeasuredWidth(), used + child.getMeasuredHeight());
used += child.getMeasuredHeight();
}
}
@Override
public CharSequence getAccessibilityClassName() {
return CustomItemVerticalLayout.class.getName();
}
@Override
public void setSelect(int index) {
setSelect(index, true);
}
@Override
public void setSelect(int index, boolean needListener) {
//重复选择
if (index == mSelected) {
if (needListener) {
for (OnTabItemSelectedListener listener : mListeners) {
mItems.get(mSelected).onRepeat();
listener.onRepeat(mSelected);
}
}
return;
}
//记录前一个选中项和当前选中项
int oldSelected = mSelected;
mSelected = index;
//前一个选中项必须不小于0才有效
if (oldSelected >= 0) {
mItems.get(oldSelected).setChecked(false);
}
mItems.get(mSelected).setChecked(true);
if (needListener) {
//事件回调
for (OnTabItemSelectedListener listener : mListeners) {
listener.onSelected(mSelected, oldSelected);
}
for (SimpleTabItemSelectedListener listener : mSimpleListeners) {
listener.onSelected(mSelected, oldSelected);
}
}
}
@Override
public void setMessageNumber(int index, int number) {
mItems.get(index).setMessageNumber(number);
}
@Override
public void setHasMessage(int index, boolean hasMessage) {
mItems.get(index).setHasMessage(hasMessage);
}
@Override
public void addTabItemSelectedListener(OnTabItemSelectedListener listener) {
mListeners.add(listener);
}
@Override
public void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener) {
mSimpleListeners.add(listener);
}
@Override
public void setTitle(int index, String title) {
mItems.get(index).setTitle(title);
}
@Override
public void setDefaultDrawable(int index, Drawable drawable) {
mItems.get(index).setDefaultDrawable(drawable);
}
@Override
public void setSelectedDrawable(int index, Drawable drawable) {
mItems.get(index).setSelectedDrawable(drawable);
}
@Override
public int getSelected() {
return mSelected;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public String getItemTitle(int index) {
return mItems.get(index).getTitle();
}
@Override
public boolean removeItem(int index) {
if (index == mSelected || index >= mItems.size() || index < 0) {
return false;
}
if (mSelected > index) {
mSelected--;
}
this.removeViewAt(index);
mItems.remove(index);
return true;
}
@Override
public void addMaterialItem(int index, Drawable defaultDrawable, Drawable selectedDrawable, String title, int selectedColor) {
// nothing
}
@Override
public void addCustomItem(int index,final BaseTabItem item) {
item.setChecked(false);
item.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(item);
if (index >= 0) {
setSelect(index);
}
}
});
if (index >= mItems.size()) {
mItems.add(index, item);
this.addView(item);
} else {
mItems.add(index, item);
this.addView(item, index);
}
}
}
@@ -0,0 +1,549 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import androidx.core.view.ViewCompat;
import me.majiajie.pagerbottomtabstrip.ItemController;
import me.majiajie.pagerbottomtabstrip.MaterialMode;
import me.majiajie.pagerbottomtabstrip.R;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.item.MaterialItemView;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
/**
* 存放 Material Design 风格按钮的水平布局
*/
public class MaterialItemLayout extends ViewGroup implements ItemController {
private final int DEFAULT_SELECTED = 0;
private final int MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH;
private final int MATERIAL_BOTTOM_NAVIGATION_ITEM_MAX_WIDTH;
private final int MATERIAL_BOTTOM_NAVIGATION_ITEM_MIN_WIDTH;
private final int MATERIAL_BOTTOM_NAVIGATION_ITEM_HEIGHT;
private final List<MaterialItemView> mItems = new ArrayList<>();
private final List<OnTabItemSelectedListener> mListeners = new ArrayList<>();
private final List<SimpleTabItemSelectedListener> mSimpleListeners = new ArrayList<>();
private boolean mItemTintIcon;
private int mItemDefaultColor;
private int[] mTempChildWidths;
private int mItemTotalWidth;
private int mSelected = -1;
private int mOldSelected = -1;
private boolean mHideTitle;
//切换背景颜色时使用
private final int ANIM_TIME = 300;
private Interpolator mInterpolator;
private boolean mChangeBackgroundMode;
private List<Integer> mBackgroundColors;
private List<Oval> mOvals;
private RectF mTempRectF;
private Paint mPaint;
//最后手指抬起的坐标
private float mLastUpX;
private float mLastUpY;
public MaterialItemLayout(Context context) {
this(context, null);
}
public MaterialItemLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final Resources res = getResources();
MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH = res.getDimensionPixelSize(R.dimen.material_bottom_navigation_active_item_max_width);
MATERIAL_BOTTOM_NAVIGATION_ITEM_MAX_WIDTH = res.getDimensionPixelSize(R.dimen.material_bottom_navigation_item_max_width);
MATERIAL_BOTTOM_NAVIGATION_ITEM_MIN_WIDTH = res.getDimensionPixelSize(R.dimen.material_bottom_navigation_item_min_width);
MATERIAL_BOTTOM_NAVIGATION_ITEM_HEIGHT = res.getDimensionPixelSize(R.dimen.material_bottom_navigation_height);
//材料设计规范限制最多只能有5个导航按钮
mTempChildWidths = new int[5];
}
/**
* 初始化方法
*
* @param items 按钮集合
* @param checkedColors 选中颜色的集合
* @param mode {@link MaterialMode}
* @param animateLayoutChanges 是否应用默认的布局动画
* @param doTintIcon item是否需要对图标染色
* @param color item的默认状态颜色
*/
public void initialize(List<MaterialItemView> items, List<Integer> checkedColors, int mode, boolean animateLayoutChanges, boolean doTintIcon, int color) {
if (animateLayoutChanges) {
setLayoutTransition(new LayoutTransition());
}
mItems.clear();
mItems.addAll(items);
mItemTintIcon = doTintIcon;
mItemDefaultColor = color;
//判断是否需要切换背景
if ((mode & MaterialMode.CHANGE_BACKGROUND_COLOR) > 0) {
//初始化一些成员变量
mChangeBackgroundMode = true;
mOvals = new ArrayList<>();
mBackgroundColors = checkedColors;
mInterpolator = new AccelerateDecelerateInterpolator();
mTempRectF = new RectF();
mPaint = new Paint();
//设置默认的背景
setBackgroundColor(mBackgroundColors.get(DEFAULT_SELECTED));
} else {
//设置按钮点击效果
for (int i = 0; i < mItems.size(); i++) {
MaterialItemView v = mItems.get(i);
if (Build.VERSION.SDK_INT >= 21) {
v.setBackground(new RippleDrawable(new ColorStateList(new int[][]{{}}, new int[]{0xFFFFFF & checkedColors.get(i) | 0x56000000}), null, null));
} else {
v.setBackgroundResource(R.drawable.material_item_background);
}
}
}
//判断是否隐藏文字
if ((mode & MaterialMode.HIDE_TEXT) > 0) {
mHideTitle = true;
for (MaterialItemView v : mItems) {
v.setHideTitle(true);
}
}
//添加按钮到布局,并注册点击事件
int n = mItems.size();
for (int i = 0; i < n; i++) {
final MaterialItemView tabItem = mItems.get(i);
tabItem.setChecked(false);
this.addView(tabItem);
tabItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(tabItem);
if (index >= 0) {
setSelect(index, mLastUpX, mLastUpY, true);
}
}
});
}
//默认选中第一项
mSelected = DEFAULT_SELECTED;
mItems.get(DEFAULT_SELECTED).setChecked(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//排除空状态
if (mItems == null || mItems.size() <= 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int count = getChildCount();
final int heightSpec = MeasureSpec.makeMeasureSpec(MATERIAL_BOTTOM_NAVIGATION_ITEM_HEIGHT, MeasureSpec.EXACTLY);
if (mHideTitle) {
final int inactiveCount = count - 1;
final int activeMaxAvailable = width - inactiveCount * MATERIAL_BOTTOM_NAVIGATION_ITEM_MIN_WIDTH;
final int activeWidth = Math.min(activeMaxAvailable, MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH);
final int inactiveMaxAvailable = inactiveCount == 0 ? 0 : (width - activeWidth) / inactiveCount;
final int inactiveWidth = Math.min(inactiveMaxAvailable, MATERIAL_BOTTOM_NAVIGATION_ITEM_MAX_WIDTH);
for (int i = 0; i < count; i++) {
if (i == mSelected) {
mTempChildWidths[i] = (int) ((activeWidth - inactiveWidth) * mItems.get(mSelected).getAnimValue() + inactiveWidth);
} else if (i == mOldSelected) {
mTempChildWidths[i] = (int) (activeWidth - (activeWidth - inactiveWidth) * mItems.get(mSelected).getAnimValue());
} else {
mTempChildWidths[i] = inactiveWidth;
}
}
} else {
final int maxAvailable = width / (count == 0 ? 1 : count);
final int childWidth = Math.min(maxAvailable, MATERIAL_BOTTOM_NAVIGATION_ACTIVE_ITEM_MAX_WIDTH);
for (int i = 0; i < count; i++) {
mTempChildWidths[i] = childWidth;
}
}
mItemTotalWidth = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(MeasureSpec.makeMeasureSpec(mTempChildWidths[i], MeasureSpec.EXACTLY),
heightSpec);
ViewGroup.LayoutParams params = child.getLayoutParams();
params.width = child.getMeasuredWidth();
mItemTotalWidth += child.getMeasuredWidth();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int width = right - left;
final int height = bottom - top;
//只支持top、bottom的padding
final int padding_top = getPaddingTop();
final int padding_bottom = getPaddingBottom();
int used = 0;
if (mItemTotalWidth > 0 && mItemTotalWidth < width) {
used = (width - mItemTotalWidth) / 2;
}
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
child.layout(width - used - child.getMeasuredWidth(), padding_top, width - used, height - padding_bottom);
} else {
child.layout(used, padding_top, child.getMeasuredWidth() + used, height - padding_bottom);
}
used += child.getMeasuredWidth();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mChangeBackgroundMode) {
int width = getWidth();
int height = getHeight();
Iterator<Oval> iterator = mOvals.iterator();
while (iterator.hasNext()) {
Oval oval = iterator.next();
mPaint.setColor(oval.color);
if (oval.r < oval.maxR) {
mTempRectF.set(oval.getLeft(), oval.getTop(), oval.getRight(), oval.getBottom());
canvas.drawOval(mTempRectF, mPaint);
} else {
this.setBackgroundColor(oval.color);
canvas.drawRect(0, 0, width, height, mPaint);
iterator.remove();
}
invalidate();
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
mLastUpX = ev.getX();
mLastUpY = ev.getY();
}
return super.onInterceptTouchEvent(ev);
}
@Override
public CharSequence getAccessibilityClassName() {
return MaterialItemLayout.class.getName();
}
@Override
public void setSelect(int index) {
setSelect(index, true);
}
@Override
public void setSelect(int index, boolean needListener) {
// 不正常的选择项
if (index >= mItems.size() || index < 0) {
return;
}
View v = mItems.get(index);
setSelect(index, v.getX() + v.getWidth() / 2f, v.getY() + v.getHeight() / 2f, needListener);
}
@Override
public void setMessageNumber(int index, int number) {
mItems.get(index).setMessageNumber(number);
}
@Override
public void setHasMessage(int index, boolean hasMessage) {
mItems.get(index).setHasMessage(hasMessage);
}
@Override
public void addTabItemSelectedListener(OnTabItemSelectedListener listener) {
mListeners.add(listener);
}
@Override
public void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener) {
mSimpleListeners.add(listener);
}
@Override
public void setTitle(int index, String title) {
mItems.get(index).setTitle(title);
}
@Override
public void setDefaultDrawable(int index, Drawable drawable) {
mItems.get(index).setDefaultDrawable(drawable);
}
@Override
public void setSelectedDrawable(int index, Drawable drawable) {
mItems.get(index).setSelectedDrawable(drawable);
}
@Override
public int getSelected() {
return mSelected;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public String getItemTitle(int index) {
return mItems.get(index).getTitle();
}
@Override
public boolean removeItem(int index) {
if (index == mSelected || index >= mItems.size() || index < 0) {
return false;
}
if (mSelected > index) {
mSelected--;
}
this.removeViewAt(index);
mItems.remove(index);
if (mChangeBackgroundMode) {
mBackgroundColors.remove(index);
}
return true;
}
@Override
public void addMaterialItem(int index, Drawable defaultDrawable, Drawable selectedDrawable, String title, int selectedColor) {
final MaterialItemView materialItemView = new MaterialItemView(getContext());
materialItemView.initialization(title, defaultDrawable, selectedDrawable, mItemTintIcon, mItemDefaultColor, mChangeBackgroundMode ? Color.WHITE : selectedColor);
materialItemView.setChecked(false);
materialItemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(materialItemView);
if (index >= 0) {
setSelect(index);
}
}
});
if (mHideTitle) {
// 隐藏文字
materialItemView.setHideTitle(true);
}
if (mSelected >= index){
mSelected++;
}
if (index >= mItems.size()) {
if (mChangeBackgroundMode) {
mBackgroundColors.add(selectedColor);
}
mItems.add(materialItemView);
this.addView(materialItemView);
} else {
if (mChangeBackgroundMode) {
mBackgroundColors.add(index, selectedColor);
}
mItems.add(index, materialItemView);
this.addView(materialItemView, index);
}
}
@Override
public void addCustomItem(int index, BaseTabItem item) {
// nothing
}
private void setSelect(int index, float x, float y, boolean needListener) {
//重复选择
if (index == mSelected) {
if (needListener) {
for (OnTabItemSelectedListener listener : mListeners) {
listener.onRepeat(mSelected);
}
}
return;
}
//记录前一个选中项和当前选中项
mOldSelected = mSelected;
mSelected = index;
//切换背景颜色
if (mChangeBackgroundMode) {
addOvalColor(mBackgroundColors.get(mSelected), x, y);
}
//前一个选中项必须不小于0才有效
if (mOldSelected >= 0) {
mItems.get(mOldSelected).setChecked(false);
}
mItems.get(mSelected).setChecked(true);
if (needListener) {
//事件回调
for (OnTabItemSelectedListener listener : mListeners) {
listener.onSelected(mSelected, mOldSelected);
}
for (SimpleTabItemSelectedListener listener : mSimpleListeners) {
listener.onSelected(mSelected, mOldSelected);
}
}
}
/**
* 添加一个圆形波纹动画
*
* @param color 颜色
* @param x X座标
* @param y y座标
*/
private void addOvalColor(int color, float x, float y) {
final Oval oval = new Oval(color, 2, x, y);
oval.maxR = getR(x, y);
mOvals.add(oval);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(oval.r, oval.maxR);
valueAnimator.setInterpolator(mInterpolator);
valueAnimator.setDuration(ANIM_TIME);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
oval.r = (float) valueAnimator.getAnimatedValue();
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
invalidate();
}
});
valueAnimator.start();
}
/**
* 以矩形内一点为圆心画圆,覆盖矩形,求这个圆的最小半径
*
* @param x 横坐标
* @param y 纵坐标
* @return 最小半径
*/
private float getR(float x, float y) {
int width = getWidth();
int height = getHeight();
double r1_square = x * x + y * y;
double r2_square = (width - x) * (width - x) + y * y;
double r3_square = (width - x) * (width - x) + (height - y) * (height - y);
double r4_square = x * x + (height - y) * (height - y);
return (float) Math.sqrt(Math.max(Math.max(r1_square, r2_square), Math.max(r3_square, r4_square)));
}
private class Oval {
int color;
float r;
float x;
float y;
float maxR;
Oval(int color, float r, float x, float y) {
this.color = color;
this.r = r;
this.x = x;
this.y = y;
}
float getLeft() {
return x - r;
}
float getTop() {
return y - r;
}
float getRight() {
return x + r;
}
float getBottom() {
return y + r;
}
}
}
@@ -0,0 +1,261 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import me.majiajie.pagerbottomtabstrip.ItemController;
import me.majiajie.pagerbottomtabstrip.R;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.item.OnlyIconMaterialItemView;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
/**
* 存放 Material Design 风格按钮的垂直布局
*/
public class MaterialItemVerticalLayout extends ViewGroup implements ItemController {
private final int NAVIGATION_ITEM_SIZE;
private final List<BaseTabItem> mItems = new ArrayList<>();
private final List<OnTabItemSelectedListener> mListeners = new ArrayList<>();
private final List<SimpleTabItemSelectedListener> mSimpleListeners = new ArrayList<>();
private int mSelected = -1;
private boolean mItemTintIcon;
private int mItemDefaultColor;
public MaterialItemVerticalLayout(Context context) {
this(context, null);
}
public MaterialItemVerticalLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialItemVerticalLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
NAVIGATION_ITEM_SIZE = getResources().getDimensionPixelSize(R.dimen.material_bottom_navigation_height);
}
public void initialize(List<BaseTabItem> items, boolean animateLayoutChanges, boolean doTintIcon, int color) {
mItems.clear();
mItems.addAll(items);
mItemTintIcon = doTintIcon;
mItemDefaultColor = color;
if (animateLayoutChanges) {
setLayoutTransition(new LayoutTransition());
}
//添加按钮到布局,并注册点击事件
int n = mItems.size();
for (int i = 0; i < n; i++) {
final BaseTabItem tabItem = mItems.get(i);
tabItem.setChecked(false);
this.addView(tabItem);
tabItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(tabItem);
if (index >= 0) {
setSelect(index);
}
}
});
}
//默认选中第一项
mSelected = 0;
mItems.get(0).setChecked(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int n = getChildCount();
final int heightSpec = MeasureSpec.makeMeasureSpec(NAVIGATION_ITEM_SIZE, MeasureSpec.EXACTLY);
final int widthSpec = MeasureSpec.makeMeasureSpec(NAVIGATION_ITEM_SIZE, MeasureSpec.EXACTLY);
for (int i = 0; i < n; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(widthSpec, heightSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
//只支持top的padding
int used = getPaddingTop();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.layout(0, used, child.getMeasuredWidth(), used + child.getMeasuredHeight());
used += child.getMeasuredHeight();
}
}
@Override
public CharSequence getAccessibilityClassName() {
return MaterialItemVerticalLayout.class.getName();
}
@Override
public void setSelect(int index) {
setSelect(index, true);
}
@Override
public void setSelect(int index, boolean needListener) {
//重复选择
if (index == mSelected) {
if (needListener) {
for (OnTabItemSelectedListener listener : mListeners) {
listener.onRepeat(mSelected);
}
}
return;
}
//记录前一个选中项和当前选中项
int oldSelected = mSelected;
mSelected = index;
//前一个选中项必须不小于0才有效
if (oldSelected >= 0) {
mItems.get(oldSelected).setChecked(false);
}
mItems.get(mSelected).setChecked(true);
if (needListener) {
//事件回调
for (OnTabItemSelectedListener listener : mListeners) {
listener.onSelected(mSelected, oldSelected);
}
for (SimpleTabItemSelectedListener listener : mSimpleListeners) {
listener.onSelected(mSelected, oldSelected);
}
}
}
@Override
public void setMessageNumber(int index, int number) {
mItems.get(index).setMessageNumber(number);
}
@Override
public void setHasMessage(int index, boolean hasMessage) {
mItems.get(index).setHasMessage(hasMessage);
}
@Override
public void addTabItemSelectedListener(OnTabItemSelectedListener listener) {
mListeners.add(listener);
}
@Override
public void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener) {
mSimpleListeners.add(listener);
}
@Override
public void setTitle(int index, String title) {
mItems.get(index).setTitle(title);
}
@Override
public void setDefaultDrawable(int index, Drawable drawable) {
mItems.get(index).setDefaultDrawable(drawable);
}
@Override
public void setSelectedDrawable(int index, Drawable drawable) {
mItems.get(index).setSelectedDrawable(drawable);
}
@Override
public int getSelected() {
return mSelected;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public String getItemTitle(int index) {
return mItems.get(index).getTitle();
}
@Override
public boolean removeItem(int index) {
if (index == mSelected || index >= mItems.size() || index < 0) {
return false;
}
if (mSelected > index) {
mSelected--;
}
this.removeViewAt(index);
mItems.remove(index);
return true;
}
@Override
public void addMaterialItem(int index, Drawable defaultDrawable, Drawable selectedDrawable, String title, int selectedColor) {
final OnlyIconMaterialItemView materialItemView = new OnlyIconMaterialItemView(getContext());
materialItemView.initialization(title, defaultDrawable, selectedDrawable, mItemTintIcon, mItemDefaultColor, selectedColor);
materialItemView.setChecked(false);
materialItemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(materialItemView);
if (index >= 0) {
setSelect(index);
}
}
});
if (index >= mItems.size()) {
this.addView(materialItemView);
mItems.add(materialItemView);
} else {
this.addView(materialItemView, index);
mItems.add(index, materialItemView);
}
}
@Override
public void addCustomItem(int index, BaseTabItem item) {
// nothing
}
}
@@ -0,0 +1,266 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.view.ViewCompat;
import java.util.ArrayList;
import java.util.List;
import me.majiajie.pagerbottomtabstrip.ItemController;
import me.majiajie.pagerbottomtabstrip.item.BaseTabItem;
import me.majiajie.pagerbottomtabstrip.listener.OnTabItemSelectedListener;
import me.majiajie.pagerbottomtabstrip.listener.SimpleTabItemSelectedListener;
/**
* 存放自定义项的布局
*/
public class OldCustomItemLayout extends ViewGroup implements ItemController {
private final List<BaseTabItem> mItems = new ArrayList<>();
private final List<OnTabItemSelectedListener> mListeners = new ArrayList<>();
private final List<SimpleTabItemSelectedListener> mSimpleListeners = new ArrayList<>();
private int mSelected = -1;
public OldCustomItemLayout(Context context) {
this(context, null);
}
public OldCustomItemLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public OldCustomItemLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutTransition(new LayoutTransition());
}
public void initialize(List<BaseTabItem> items, boolean animateLayoutChanges) {
mItems.clear();
mItems.addAll(items);
if (animateLayoutChanges) {
setLayoutTransition(new LayoutTransition());
}
//添加按钮到布局,并注册点击事件
int n = mItems.size();
for (int i = 0; i < n; i++) {
final BaseTabItem tabItem = mItems.get(i);
tabItem.setChecked(false);
this.addView(tabItem);
tabItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(tabItem);
if (index >= 0) {
setSelect(index);
}
}
});
}
//默认选中第一项
mSelected = 0;
mItems.get(0).setChecked(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int n = getChildCount();
int visableChildCount = 0;
for (int i = 0; i < n; i++) {
if (getChildAt(i).getVisibility() != GONE) {
visableChildCount++;
}
}
if (visableChildCount == 0) {
return;
}
final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec) / visableChildCount, MeasureSpec.EXACTLY);
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(0, MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingTop()), MeasureSpec.EXACTLY);
for (int i = 0; i < n; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int count = getChildCount();
final int width = right - left;
final int height = bottom - top;
//只支持top、bottom的padding
final int padding_top = getPaddingTop();
final int padding_bottom = getPaddingBottom();
int used = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
child.layout(width - used - child.getMeasuredWidth(), padding_top, width - used, height - padding_bottom);
} else {
child.layout(used, padding_top, child.getMeasuredWidth() + used, height - padding_bottom);
}
used += child.getMeasuredWidth();
}
}
@Override
public CharSequence getAccessibilityClassName() {
return CustomItemLayout.class.getName();
}
@Override
public void setSelect(int index) {
setSelect(index, true);
}
@Override
public void setSelect(int index, boolean needListener) {
//重复选择
if (index == mSelected) {
if (needListener) {
for (OnTabItemSelectedListener listener : mListeners) {
mItems.get(mSelected).onRepeat();
listener.onRepeat(mSelected);
}
}
return;
}
//记录前一个选中项和当前选中项
int oldSelected = mSelected;
mSelected = index;
//前一个选中项必须不小于0才有效
if (oldSelected >= 0) {
mItems.get(oldSelected).setChecked(false);
}
mItems.get(mSelected).setChecked(true);
if (needListener) {
//事件回调
for (OnTabItemSelectedListener listener : mListeners) {
listener.onSelected(mSelected, oldSelected);
}
for (SimpleTabItemSelectedListener listener : mSimpleListeners) {
listener.onSelected(mSelected, oldSelected);
}
}
}
@Override
public void setMessageNumber(int index, int number) {
mItems.get(index).setMessageNumber(number);
}
@Override
public void setHasMessage(int index, boolean hasMessage) {
mItems.get(index).setHasMessage(hasMessage);
}
@Override
public void addTabItemSelectedListener(OnTabItemSelectedListener listener) {
mListeners.add(listener);
}
@Override
public void addSimpleTabItemSelectedListener(SimpleTabItemSelectedListener listener) {
mSimpleListeners.add(listener);
}
@Override
public void setTitle(int index, String title) {
mItems.get(index).setTitle(title);
}
@Override
public void setDefaultDrawable(int index, Drawable drawable) {
mItems.get(index).setDefaultDrawable(drawable);
}
@Override
public void setSelectedDrawable(int index, Drawable drawable) {
mItems.get(index).setSelectedDrawable(drawable);
}
@Override
public int getSelected() {
return mSelected;
}
@Override
public int getItemCount() {
return mItems.size();
}
@Override
public String getItemTitle(int index) {
return mItems.get(index).getTitle();
}
@Override
public boolean removeItem(int index) {
if (index == mSelected || index >= mItems.size() || index < 0) {
return false;
}
if (mSelected > index) {
mSelected--;
}
this.removeViewAt(index);
mItems.remove(index);
return true;
}
@Override
public void addMaterialItem(int index, Drawable defaultDrawable, Drawable selectedDrawable, String title, int selectedColor) {
// nothing
}
@Override
public void addCustomItem(int index, final BaseTabItem item) {
item.setChecked(false);
item.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int index = mItems.indexOf(item);
if (index >= 0) {
setSelect(index);
}
}
});
if (index >= mItems.size()) {
mItems.add(index, item);
this.addView(item);
} else {
mItems.add(index, item);
this.addView(item, index);
}
}
}
@@ -0,0 +1,102 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.content.Context;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import java.util.Locale;
import me.majiajie.pagerbottomtabstrip.R;
public class RoundMessageView extends FrameLayout {
private final View mOval;
private final TextView mMessages;
private int mMessageNumber;
private boolean mHasMessage;
public RoundMessageView(Context context) {
this(context, null);
}
public RoundMessageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundMessageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.round_message_view, this, true);
mOval = findViewById(R.id.oval);
mMessages = findViewById(R.id.msg);
mMessages.setTypeface(Typeface.DEFAULT_BOLD);
mMessages.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 10);
}
public void setMessageNumber(int number) {
mMessageNumber = number;
if (mMessageNumber > 0) {
mOval.setVisibility(View.INVISIBLE);
mMessages.setVisibility(View.VISIBLE);
if (mMessageNumber < 10) {
mMessages.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 10);
} else {
mMessages.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 8);
}
if (mMessageNumber <= 99) {
mMessages.setText(String.format(Locale.ENGLISH, "%d", mMessageNumber));
} else {
mMessages.setText(String.format(Locale.ENGLISH, "%d+", 99));
}
} else {
mMessages.setVisibility(View.INVISIBLE);
if (mHasMessage) {
mOval.setVisibility(View.VISIBLE);
}
}
}
public void setHasMessage(boolean hasMessage) {
mHasMessage = hasMessage;
if (hasMessage) {
mOval.setVisibility(mMessageNumber > 0 ? View.INVISIBLE : View.VISIBLE);
} else {
mOval.setVisibility(View.INVISIBLE);
}
}
public void tintMessageBackground(@ColorInt int color) {
Drawable drawable = Utils.tinting(ContextCompat.getDrawable(getContext(), R.drawable.round), color);
ViewCompat.setBackground(mOval, drawable);
ViewCompat.setBackground(mMessages, drawable);
}
public void setMessageNumberColor(@ColorInt int color) {
mMessages.setTextColor(color);
}
public int getMessageNumber() {
return mMessageNumber;
}
public boolean hasMessage() {
return mHasMessage;
}
}
@@ -0,0 +1,61 @@
package me.majiajie.pagerbottomtabstrip.internal;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
public class Utils {
/**
* Drawable 染色
*
* @param drawable 染色对象
* @param color 颜色
* @return 染色后的资源
*/
public static Drawable tinting(Drawable drawable, int color) {
final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
wrappedDrawable.mutate();
DrawableCompat.setTint(wrappedDrawable, color);
return wrappedDrawable;
}
public static Drawable newDrawable(Drawable drawable) {
Drawable.ConstantState constantState = drawable.getConstantState();
return constantState != null ? constantState.newDrawable() : drawable;
}
/**
* 获取colorPrimary的颜色,需要V7包的支持
*
* @param context 上下文
* @return 0xAARRGGBB
*/
public static int getColorPrimary(Context context) {
Resources res = context.getResources();
int attrRes = res.getIdentifier("colorPrimary", "attr", context.getPackageName());
if (attrRes == 0) {
return 0xFF009688;
}
return ContextCompat.getColor(context, getResourceId(context, attrRes));
}
/**
* 获取自定义属性的资源ID
*
* @param context 上下文
* @param attrRes 自定义属性
* @return resourceId
*/
private static int getResourceId(Context context, int attrRes) {
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(attrRes, typedValue, true);
return typedValue.resourceId;
}
}
@@ -0,0 +1,71 @@
package me.majiajie.pagerbottomtabstrip.item;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* 所有自定义Item都必须继承此类
*/
public abstract class BaseTabItem extends FrameLayout {
public BaseTabItem(@NonNull Context context) {
super(context);
}
public BaseTabItem(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BaseTabItem(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* 设置选中状态
*/
abstract public void setChecked(boolean checked);
/**
* 设置消息数字。注意:一般数字需要大于0才会显示
*/
abstract public void setMessageNumber(int number);
/**
* 设置是否显示无数字的小圆点。注意:如果消息数字不为0,优先显示带数字的圆
*/
abstract public void setHasMessage(boolean hasMessage);
/**
* 设置标题
*/
abstract public void setTitle(String title);
/**
* 设置未选中状态下的图标
*/
abstract public void setDefaultDrawable(Drawable drawable);
/**
* 设置选中状态下的图标
*/
abstract public void setSelectedDrawable(Drawable drawable);
/**
* 获取标题文字
*/
abstract public String getTitle();
/**
* 已选中的状态下再次点击时触发
*/
public void onRepeat() {}
}
@@ -0,0 +1,253 @@
package me.majiajie.pagerbottomtabstrip.item;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import me.majiajie.pagerbottomtabstrip.R;
import me.majiajie.pagerbottomtabstrip.internal.RoundMessageView;
import me.majiajie.pagerbottomtabstrip.internal.Utils;
/**
* 材料设计风格项
*/
public class MaterialItemView extends BaseTabItem {
private final RoundMessageView mMessages;
private final TextView mLabel;
private final ImageView mIcon;
private Drawable mDefaultDrawable;
private Drawable mCheckedDrawable;
private int mDefaultColor;
private int mCheckedColor;
private final float mTranslation;
private final float mTranslationHideTitle;
private final int mTopMargin;
private final int mTopMarginHideTitle;
private boolean mHideTitle;
private boolean mChecked;
private ValueAnimator mAnimator;
private float mAnimatorValue = 1f;
private boolean mIsMeasured = false;
private boolean mTintIcon = true;
public MaterialItemView(@NonNull Context context) {
this(context, null);
}
public MaterialItemView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MaterialItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
final float scale = context.getResources().getDisplayMetrics().density;
mTranslation = scale * 2;
mTranslationHideTitle = scale * 10;
mTopMargin = (int) (scale * 8);
mTopMarginHideTitle = (int) (scale * 16);
LayoutInflater.from(context).inflate(R.layout.item_material, this, true);
mIcon = findViewById(R.id.icon);
mLabel = findViewById(R.id.label);
mMessages = findViewById(R.id.messages);
}
@Override
public CharSequence getAccessibilityClassName() {
return MaterialItemView.class.getName();
}
public void initialization(String title, Drawable drawable, Drawable checkedDrawable, boolean tintIcon, int color, int checkedColor) {
mTintIcon = tintIcon;
mDefaultColor = color;
mCheckedColor = checkedColor;
if (mTintIcon) {
mDefaultDrawable = Utils.tinting(drawable, mDefaultColor);
mCheckedDrawable = Utils.tinting(checkedDrawable, mCheckedColor);
} else {
mDefaultDrawable = drawable;
mCheckedDrawable = checkedDrawable;
}
mLabel.setText(title);
mLabel.setTextColor(color);
mIcon.setImageDrawable(mDefaultDrawable);
mAnimator = ValueAnimator.ofFloat(1f);
mAnimator.setDuration(115L);
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mAnimatorValue = (float) animation.getAnimatedValue();
if (mHideTitle) {
mIcon.setTranslationY(-mTranslationHideTitle * mAnimatorValue);
} else {
mIcon.setTranslationY(-mTranslation * mAnimatorValue);
}
mLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f + mAnimatorValue * 2f);
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mIsMeasured = true;
}
@Override
public void setChecked(boolean checked) {
if (mChecked == checked) {
return;
}
mChecked = checked;
if (mHideTitle) {
mLabel.setVisibility(mChecked ? View.VISIBLE : View.INVISIBLE);
}
if (mIsMeasured) {
// 切换动画
if (mChecked) {
mAnimator.start();
} else {
mAnimator.reverse();
}
} else if (mChecked) { // 布局还未测量时选中,直接转换到选中的最终状态
if (mHideTitle) {
mIcon.setTranslationY(-mTranslationHideTitle);
} else {
mIcon.setTranslationY(-mTranslation);
}
mLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f);
} else { // 布局还未测量并且未选中,保持未选中状态
mIcon.setTranslationY(0);
mLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f);
}
// 切换颜色
if (mChecked) {
mIcon.setImageDrawable(mCheckedDrawable);
mLabel.setTextColor(mCheckedColor);
} else {
mIcon.setImageDrawable(mDefaultDrawable);
mLabel.setTextColor(mDefaultColor);
}
}
@Override
public void setMessageNumber(int number) {
mMessages.setVisibility(View.VISIBLE);
mMessages.setMessageNumber(number);
}
@Override
public void setHasMessage(boolean hasMessage) {
mMessages.setVisibility(View.VISIBLE);
mMessages.setHasMessage(hasMessage);
}
@Override
public void setTitle(String title) {
mLabel.setText(title);
}
@Override
public void setDefaultDrawable(Drawable drawable) {
if (mTintIcon) {
mDefaultDrawable = Utils.tinting(drawable, mDefaultColor);
} else {
mDefaultDrawable = drawable;
}
if (!mChecked) {
mIcon.setImageDrawable(mDefaultDrawable);
}
}
@Override
public void setSelectedDrawable(Drawable drawable) {
if (mTintIcon) {
mCheckedDrawable = Utils.tinting(drawable, mCheckedColor);
} else {
mCheckedDrawable = drawable;
}
if (mChecked) {
mIcon.setImageDrawable(mCheckedDrawable);
}
}
@Override
public String getTitle() {
return mLabel.getText().toString();
}
/**
* 获取动画运行值[0,1]
*/
public float getAnimValue() {
return mAnimatorValue;
}
/**
* 设置是否隐藏文字
*/
public void setHideTitle(boolean hideTitle) {
mHideTitle = hideTitle;
LayoutParams iconParams = (LayoutParams) mIcon.getLayoutParams();
if (mHideTitle) {
iconParams.topMargin = mTopMarginHideTitle;
} else {
iconParams.topMargin = mTopMargin;
}
mLabel.setVisibility(mChecked ? View.VISIBLE : View.INVISIBLE);
mIcon.setLayoutParams(iconParams);
}
/**
* 设置消息圆形的颜色
*/
public void setMessageBackgroundColor(@ColorInt int color) {
mMessages.tintMessageBackground(color);
}
/**
* 设置消息数据的颜色
*/
public void setMessageNumberColor(@ColorInt int color) {
mMessages.setMessageNumberColor(color);
}
}
@@ -0,0 +1,133 @@
package me.majiajie.pagerbottomtabstrip.item;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
import me.majiajie.pagerbottomtabstrip.R;
import me.majiajie.pagerbottomtabstrip.internal.RoundMessageView;
public class NormalItemView extends BaseTabItem {
private ImageView mIcon;
private final TextView mTitle;
private final RoundMessageView mMessages;
private Drawable mDefaultDrawable;
private Drawable mCheckedDrawable;
private int mDefaultTextColor = 0x56000000;
private int mCheckedTextColor = 0x56000000;
private boolean mChecked;
public NormalItemView(Context context) {
this(context, null);
}
public NormalItemView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NormalItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// LayoutInflater.from(context).inflate(R.layout.item_normal, this, true);
LayoutInflater.from(context).inflate(R.layout.item_normal_new, this, true);
mIcon = findViewById(R.id.icon);
mTitle = findViewById(R.id.title);
mMessages = findViewById(R.id.messages);
}
@Override
public CharSequence getAccessibilityClassName() {
return NormalItemView.class.getName();
}
/**
* 方便初始化的方法
*
* @param drawableRes 默认状态的图标
* @param checkedDrawableRes 选中状态的图标
* @param title 标题
*/
public void initialize(@DrawableRes int drawableRes, @DrawableRes int checkedDrawableRes, String title) {
mDefaultDrawable = ContextCompat.getDrawable(getContext(), drawableRes);
mCheckedDrawable = ContextCompat.getDrawable(getContext(), checkedDrawableRes);
mTitle.setText(title);
}
public void initialize(@DrawableRes int drawableRes, @DrawableRes int checkedDrawableRes, String title,int textsize) {
mDefaultDrawable = ContextCompat.getDrawable(getContext(), drawableRes);
mCheckedDrawable = ContextCompat.getDrawable(getContext(), checkedDrawableRes);
mTitle.setText(title);
mTitle.setTextSize(textsize);
}
public void initialize(String title,int textsize) {
mIcon.setVisibility(GONE);
mTitle.setText(title);
mTitle.setTextSize(textsize);
}
@Override
public void setChecked(boolean checked) {
if (checked) {
mIcon.setImageDrawable(mCheckedDrawable);
mTitle.setTextColor(mCheckedTextColor);
} else {
mIcon.setImageDrawable(mDefaultDrawable);
mTitle.setTextColor(mDefaultTextColor);
}
mChecked = checked;
}
@Override
public void setMessageNumber(int number) {
mMessages.setMessageNumber(number);
}
@Override
public void setHasMessage(boolean hasMessage) {
mMessages.setHasMessage(hasMessage);
}
@Override
public void setTitle(String title) {
mTitle.setText(title);
}
@Override
public void setDefaultDrawable(Drawable drawable) {
mDefaultDrawable = drawable;
if (!mChecked) {
mIcon.setImageDrawable(drawable);
}
}
@Override
public void setSelectedDrawable(Drawable drawable) {
mCheckedDrawable = drawable;
if (mChecked) {
mIcon.setImageDrawable(drawable);
}
}
@Override
public String getTitle() {
return mTitle.getText().toString();
}
public void setTextDefaultColor(@ColorInt int color) {
mDefaultTextColor = color;
}
public void setTextCheckedColor(@ColorInt int color) {
mCheckedTextColor = color;
}
}
@@ -0,0 +1,166 @@
package me.majiajie.pagerbottomtabstrip.item;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import me.majiajie.pagerbottomtabstrip.R;
import me.majiajie.pagerbottomtabstrip.internal.RoundMessageView;
import me.majiajie.pagerbottomtabstrip.internal.Utils;
/**
* 只有图标的材料设计项(用于垂直布局)
*/
public class OnlyIconMaterialItemView extends BaseTabItem {
private final RoundMessageView mMessages;
private final ImageView mIcon;
private Drawable mDefaultDrawable;
private Drawable mCheckedDrawable;
private int mDefaultColor;
private int mCheckedColor;
private String mTitle;
private boolean mChecked;
private boolean mTintIcon = true;
public OnlyIconMaterialItemView(@NonNull Context context) {
this(context, null);
}
public OnlyIconMaterialItemView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public OnlyIconMaterialItemView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.item_material_only_icon, this, true);
mIcon = findViewById(R.id.icon);
mMessages = findViewById(R.id.messages);
}
@Override
public CharSequence getAccessibilityClassName() {
return OnlyIconMaterialItemView.class.getName();
}
public void initialization(String title, Drawable drawable, Drawable checkedDrawable, boolean tintIcon, int color, int checkedColor) {
mTitle = title;
mDefaultColor = color;
mCheckedColor = checkedColor;
mTintIcon = tintIcon;
if (mTintIcon) {
mDefaultDrawable = Utils.tinting(drawable, mDefaultColor);
mCheckedDrawable = Utils.tinting(checkedDrawable, mCheckedColor);
} else {
mDefaultDrawable = drawable;
mCheckedDrawable = checkedDrawable;
}
mIcon.setImageDrawable(mDefaultDrawable);
if (Build.VERSION.SDK_INT >= 21) {
setBackground(new RippleDrawable(new ColorStateList(new int[][]{{}}, new int[]{0xFFFFFF & checkedColor | 0x56000000}), null, null));
} else {
setBackgroundResource(R.drawable.material_item_background);
}
}
@Override
public void setChecked(boolean checked) {
if (mChecked == checked) {
return;
}
mChecked = checked;
// 切换颜色
if (mChecked) {
mIcon.setImageDrawable(mCheckedDrawable);
} else {
mIcon.setImageDrawable(mDefaultDrawable);
}
}
@Override
public void setMessageNumber(int number) {
mMessages.setVisibility(View.VISIBLE);
mMessages.setMessageNumber(number);
}
@Override
public void setHasMessage(boolean hasMessage) {
mMessages.setVisibility(View.VISIBLE);
mMessages.setHasMessage(hasMessage);
}
@Override
public void setTitle(String title) {
// no title
}
@Override
public void setDefaultDrawable(Drawable drawable) {
if (mTintIcon) {
mDefaultDrawable = Utils.tinting(drawable, mDefaultColor);
} else {
mDefaultDrawable = drawable;
}
if (!mChecked) {
mIcon.setImageDrawable(mDefaultDrawable);
}
}
@Override
public void setSelectedDrawable(Drawable drawable) {
if (mTintIcon) {
mCheckedDrawable = Utils.tinting(drawable, mCheckedColor);
} else {
mCheckedDrawable = drawable;
}
if (mChecked) {
mIcon.setImageDrawable(mCheckedDrawable);
}
}
@Override
public String getTitle() {
return mTitle;
}
/**
* 设置消息圆形的颜色
*/
public void setMessageBackgroundColor(@ColorInt int color) {
mMessages.tintMessageBackground(color);
}
/**
* 设置消息数据的颜色
*/
public void setMessageNumberColor(@ColorInt int color) {
mMessages.setMessageNumberColor(color);
}
}
@@ -0,0 +1,22 @@
package me.majiajie.pagerbottomtabstrip.listener;
/**
* 导航栏按钮选中事件
*/
public interface OnTabItemSelectedListener {
/**
* 选中导航栏的某一项
*
* @param index 索引导航按钮,按添加顺序排序
* @param old 前一个选中项,如果没有就等于-1
*/
void onSelected(int index, int old);
/**
* 重复选中
*
* @param index 索引导航按钮,按添加顺序排序
*/
void onRepeat(int index);
}
@@ -0,0 +1,16 @@
package me.majiajie.pagerbottomtabstrip.listener;
/**
* 导航栏按钮选中事件
*/
public interface SimpleTabItemSelectedListener {
/**
* 选中导航栏的某一项
*
* @param index 索引导航按钮,按添加顺序排序
* @param old 前一个选中项,如果没有就等于-1
*/
void onSelected(int index, int old);
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#33333333"/>
</shape>
</item>
<item android:drawable="@android:color/transparent"/>
</selector>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!-- <solid android:color="@android:color/holo_red_light"/>-->
<solid android:color="#EB322D"/>
</shape>
@@ -0,0 +1,39 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="@dimen/material_bottom_navigation_margin"
android:layout_marginTop="@dimen/material_bottom_navigation_margin" />
<me.majiajie.pagerbottomtabstrip.internal.BaselineLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:clipToPadding="false"
android:paddingBottom="10dp"
>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="@dimen/material_bottom_navigation_text_size"
tools:text="123" />
</me.majiajie.pagerbottomtabstrip.internal.BaselineLayout>
<me.majiajie.pagerbottomtabstrip.internal.RoundMessageView
android:id="@+id/messages"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:translationX="14dp"
android:visibility="gone" />
</merge>
@@ -0,0 +1,20 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center" />
<me.majiajie.pagerbottomtabstrip.internal.RoundMessageView
android:id="@+id/messages"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="11dp"
android:translationX="14dp"
android:visibility="gone" />
</merge>
@@ -0,0 +1,41 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp" />
<me.majiajie.pagerbottomtabstrip.internal.BaselineLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:clipToPadding="false"
android:paddingBottom="10dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="12sp"
tools:text="123" />
</me.majiajie.pagerbottomtabstrip.internal.BaselineLayout>
<me.majiajie.pagerbottomtabstrip.internal.RoundMessageView
android:id="@+id/messages"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:translationX="14dp" />
</merge>
@@ -0,0 +1,45 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="22dp"
/>
<!-- <me.majiajie.pagerbottomtabstrip.internal.BaselineLayout-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_gravity="bottom|center_horizontal"-->
<!-- android:clipToPadding="false"-->
<!-- android:paddingBottom="24dp">-->
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:maxLines="1"
android:textSize="12sp"
android:paddingBottom="14dp"
tools:text="123" />
<!-- </me.majiajie.pagerbottomtabstrip.internal.BaselineLayout>-->
<me.majiajie.pagerbottomtabstrip.internal.RoundMessageView
android:id="@+id/messages"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="14dp"
android:translationX="16dp"
android:translationY="-1dp"/>
</merge>
@@ -0,0 +1,39 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<ImageView
android:id="@+id/icon"
android:layout_width="33dp"
android:layout_height="33dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:maxLines="1"
android:textSize="14sp"
android:paddingBottom="4dp"
tools:text="123" />
<!-- </me.majiajie.pagerbottomtabstrip.internal.BaselineLayout>-->
<me.majiajie.pagerbottomtabstrip.internal.RoundMessageView
android:id="@+id/messages"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="14dp"
android:translationX="16dp"
android:translationY="-1dp"/>
</merge>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<View
android:id="@+id/oval"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="2dp"
android:background="@drawable/round"
android:visibility="invisible" />
<TextView
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/round"
android:gravity="center"
android:minHeight="20dp"
android:minWidth="20dp"
android:singleLine="true"
android:textColor="@android:color/white"
android:visibility="invisible" />
</merge>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">PagerBottomTabStrip</string>
</resources>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="material_bottom_navigation_active_item_max_width">168dp</dimen>
<dimen name="material_bottom_navigation_active_text_size">14sp</dimen>
<dimen name="material_bottom_navigation_elevation">8dp</dimen>
<dimen name="material_bottom_navigation_height">56dp</dimen>
<dimen name="material_bottom_navigation_item_max_width">96dp</dimen>
<dimen name="material_bottom_navigation_item_min_width">56dp</dimen>
<dimen name="material_bottom_navigation_margin">8dp</dimen>
<dimen name="material_bottom_navigation_shadow_height">1dp</dimen>
<dimen name="material_bottom_navigation_text_size">12sp</dimen>
<declare-styleable name="PageNavigationView">
<attr name="NavigationPaddingTop" format="dimension"/>
<attr name="NavigationPaddingBottom" format="dimension"/>
</declare-styleable>
</resources>
@@ -0,0 +1,22 @@
ext {
libraryName = 'PagerBottomTabStrip'
libraryDescription = 'An navigation bar for Android'
publishedGroupId = 'me.majiajie'
artifact = 'pager-bottom-tab-strip'
siteUrl = 'https://github.com/tyzlmjj/PagerBottomTabStrip'
scmUrl = 'github.com/tyzlmjj/PagerBottomTabStrip.git'
libraryVersion = rootProject.ext.versionName
developerId = 'tyzlmjj'
developerName = 'Ma JiaJie'
developerEmail = 'tyzl931019@gmail.com'
licenseName = 'The Apache Software License, Version 2.0'
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
apply from: 'https://raw.githubusercontent.com/tyzlmjj/Gradle/master/MavenCentral/publish_module.gradle'