底部导航BottomTabLayout
2017-05-16 本文已影响104人
徐影魔
虽然已经有了官方的BottomNavigationView,但感觉用不惯,以我对其浅薄的了解(仅仅是在demo中用了下,没为深入源码),觉得BottomNavigationView不够灵活,主要有一下几个原因:
- 不能添加自己写的view作为BottomNavigationView 的item,因为是时候有这样的UI(简书这种还是很常见的)
-
BottomNavigationView 的item不能超过五个,三个以上会有夸张的动画(动画可以用反射关闭,之前看过,简书上就有)
-
好像不能显示消息数量角标
用法:
布局:
<com.nevermore.oceans.widget.BottomTabLayout
android:id="@+id/btl"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize">
<com.nevermore.oceans.widget.BottomTabView
android:layout_width="0dp"
app:tabText="首页"
app:tabIconNormal="@mipmap/ic_launcher_round"
app:tabIconSelected="@mipmap/ic_launcher"
app:tabTextColorNormal="#999999"
app:tabTextColorSelected="#2c8bef"
android:layout_weight="1"
android:layout_height="match_parent"/>
<com.nevermore.oceans.widget.BottomTabView
android:layout_width="0dp"
android:layout_weight="1"
app:tabIconNormal="@mipmap/ic_launcher_round"
app:tabIconSelected="@mipmap/ic_launcher"
app:tabTextColorNormal="#999999"
app:tabTextColorSelected="#2c8bef"
app:tabText="购物车"
android:layout_height="match_parent"/>
<com.nevermore.oceans.widget.BottomTabView
app:tabText="我的"
android:layout_width="0dp"
app:tabIconNormal="@mipmap/ic_launcher_round"
app:tabIconSelected="@mipmap/ic_launcher"
app:tabTextColorNormal="#999999"
app:tabTextColorSelected="#2c8bef"
android:layout_weight="1"
android:layout_height="match_parent"/>
</com.nevermore.oceans.widget.BottomTabLayout>
代码1:
public class MainActivity extends AppCompatActivity {
private TextView tv;
private BottomTabLayout btl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btl = (BottomTabLayout) findViewById(R.id.btl);
tv = (TextView) findViewById(R.id.tv);
//底部每个标签的点击事件
btl.setOnItemTabClickListener(new BottomTabLayout.OnItemTabClickListener() {
@Override
public void onItemTabClick(int position, View itemView) {
if(itemView instanceof BottomTabView){
String tabText = ((BottomTabView) itemView).getTabText();
tv.setText(tabText);
}
//设为被选中
btl.selectItem(itemView);
}
});
//默认选中第一个tab
btl.selectItem(0);
}
}
BottomTabView:
/**
*
* author XuNeverMore
* create on 2017/5/16 0016
* github https://github.com/XuNeverMore
*/
public class BottomTabView extends FrameLayout {
private static final String TAG = "BottomTabView";
private Drawable drawableN;
private Drawable drawableS;
private ImageView tabIcon;
private TextView tabText;
private String text;
private TextView tvCount;
private int textColorNoraml = 0xff000000;
private int textColorSelected = 0xff4caf65;
public BottomTabView(@NonNull Context context) {
this(context, null);
}
public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomTabView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
//统一定制每个tab的布局,以便修改
LayoutInflater.from(context).inflate(R.layout.item_bottom, this, true);
//图标
tabIcon = (ImageView) findViewById(R.id.tab_icon);
//文字
tabText = (TextView) findViewById(R.id.tab_text);
//显示消息数
tvCount = (TextView) findViewById(R.id.tv_count);
//自定义一些属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BottomTabView);
drawableN = typedArray.getDrawable(R.styleable.BottomTabView_tabIconNormal);//正常状态图标
drawableS = typedArray.getDrawable(R.styleable.BottomTabView_tabIconSelected);//选中状态图标
textColorNoraml = typedArray.getColor(R.styleable.BottomTabView_tabTextColorNormal, textColorNoraml);//正常文字颜色
textColorSelected = typedArray.getColor(R.styleable.BottomTabView_tabTextColorSelected, textColorSelected);//选中状态文字颜色
text = typedArray.getString(R.styleable.BottomTabView_tabText);
//默认都显示未选中状态的图标
if (drawableN != null) {
tabIcon.setImageDrawable(drawableN);
}
if (!TextUtils.isEmpty(text)) {
tabText.setText(text);
tabText.setTextColor(textColorNoraml);
}
typedArray.recycle();
}
/**
* 切换 tab 选中状态
* @param select
*/
public void setSelectState(boolean select){
setSelected(select);
tabIcon.setImageDrawable(select?drawableS:drawableN);
tabText.setTextColor(select?textColorSelected:textColorNoraml);
}
/**
* 获取 tab 上的文字
* @return
*/
public String getTabText(){
return tabText.getText().toString().trim();
}
public void setTabText(String text){
tabText.setText(text);
}
/**
* 设置未读消息数量
* @param count
*/
public void setCount(int count){
tvCount.setVisibility(count>0?VISIBLE:GONE);
tvCount.setText(""+count);
}
}
BottomTabView 就是封装了一个布局:
R.layout.item_bottom
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/tab_icon"
android:scaleType="center"
android:layout_marginTop="8dp"
android:layout_centerHorizontal="true"
android:duplicateParentState="true"
android:layout_width="24dp"
android:layout_height="24dp" />
<TextView
android:id="@+id/tab_text"
android:layout_alignParentBottom="true"
android:layout_marginBottom="5dp"
android:text="folder"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:visibility="gone"
android:id="@+id/tv_count"
android:layout_toRightOf="@+id/tab_icon"
android:text="5"
android:layout_marginTop="2dp"
android:layout_marginLeft="-12dp"
android:textColor="#ffffff"
android:background="@drawable/rect_round_red"
android:textSize="12sp"
android:gravity="center"
android:paddingLeft="6dp"
android:paddingTop="1dp"
android:paddingBottom="1dp"
android:paddingRight="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
QQ截图20170516125040.png
这个item 布局可以根据需求自己修改,消息数量默认是gone,可以通过以下方法显示:
public void setCount(int count){
tvCount.setVisibility(count>0?VISIBLE:GONE);
tvCount.setText(""+count);
}
几个自定义属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BottomTabView">
<attr name="tabIconNormal" format="reference"/>
<attr name="tabIconSelected" format="reference"/>
<attr name="tabTextColorNormal" format="color"/>
<attr name="tabTextColorSelected" format="color"/>
<attr name="tabText" format="string"/>
</declare-styleable>
</resources>
BottomTabLayout :
继承LinearLayout,用来管理每个tab的点击、选中,当然tab并不一定是BottomTabView类型的,
可以是自己写的View
/**
*
* author XuNeverMore
* create on 2017/5/16 0016
* github https://github.com/XuNeverMore
*/
public class BottomTabLayout extends LinearLayout {
public BottomTabLayout(Context context) {
this(context, null);
}
public BottomTabLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BottomTabLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOrientation(HORIZONTAL);//水平方向
}
private static final String TAG = "BottomTabLayout";
private View lastSelectedItemView;//上一个被选中的item
/**
* 每个tab 点击事件 类似ListView的OnItemClickListener
*/
public interface OnItemTabClickListener {
void onItemTabClick(int position, View itemView);
}
private OnItemTabClickListener onItemTabClickListener;
public void setOnItemTabClickListener(OnItemTabClickListener onItemTabClickListener) {
this.onItemTabClickListener = onItemTabClickListener;
setListeners();
}
/**
* 为每个tab 设置点击事件
*/
private void setListeners() {
int childCount = getChildCount();
Log.i(TAG, "setListeners: "+childCount);
//为BottmoTabLayout 的每个childView设置点击事件
for (int i = 0; i < childCount; i++) {
final int position = i;
final View child = getChildAt(i);
child.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onItemTabClickListener != null) {
//防止选中的tab被重复点击
if(lastSelectedItemView!=null&&lastSelectedItemView==v){
return;
}
onItemTabClickListener.onItemTabClick(position, v);
}
}
});
}
}
//选中一个tab 并改变其状态
public void selectItem(View itemView){
//让上一个被选中的tab恢复原来状态
if(lastSelectedItemView!=null){
changeItemSelectState(lastSelectedItemView,false);
}
//选中itemView
changeItemSelectState(itemView,true);
//保存itemView 下次切换修改
lastSelectedItemView = itemView;
}
public void selectItem(int position){
if(position<getChildCount()){
View childAt = getChildAt(position);
selectItem(childAt);
}
}
//改变tab 选中状态
private void changeItemSelectState(View view,boolean selected){
if(view instanceof BottomTabView){//如果是tab 是BottomTabView类型的单独设置
((BottomTabView) view).setSelectState(selected);
}else {
view.setSelected(selected);
}
}
}
主要用法就是代码1
//底部每个标签的点击事件
btl.setOnItemTabClickListener(new BottomTabLayout.OnItemTabClickListener() {
@Override
public void onItemTabClick(int position, View itemView) {
if(itemView instanceof BottomTabView){
String tabText = ((BottomTabView) itemView).getTabText();
tv.setText(tabText);
}
//设为被选中
btl.selectItem(itemView);
}
});
因为现在做的项目,要判断登录状态,在未登录状态下,点击要调到登录页面,所以没有象TabLayout那样写一个setUpWithViewPager(viewpager);方法,直接在OnItemTabClickListener里切换就可以了,这样更灵活点。
gs.png虽然更灵活了,但是要多写一句btl.selectItem(itemView); 将你点击的tab设为选中状态
第一次完全用Markdown写东西,瞬间觉得学了一门语言。