前 言
在我们日常使用的Android应用中,几乎所有的应用都使用到底部Tab标签栏,比如微信、淘宝、支付宝等。而他们实现的方式又大同小异,实现底部Tab标签栏无非有两种方式,一是使用“show()和hide()控制Fragment”方式,这种方式也称之为使用事务的方式,是目前大多数的应用都使用这种方式的,如淘宝、支付宝,二是使用“Fragment+ViewPager”的方式,这种方式是微信所使用的。那么,下面就来看看如何实现吧。
使用show()和hide()控制Fragment的方式
首先先定义一个布局,布局里主要显示底部icon图标和文本信息。如下布局所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_page_bg">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@color/colorWhite"
android:orientation="horizontal"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<RelativeLayout
android:id="@+id/home_layout_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/home_image_view"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_centerHorizontal="true"
android:src="@drawable/comui_tab_home" />
<TextView
android:id="@+id/home_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/home_image_view"
android:layout_centerHorizontal="true"
android:layout_marginTop="3dp"
android:text="首页"
android:textColor="@color/comui_tab"
android:textSize="13sp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/message_layout_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/message_image_view"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_centerHorizontal="true"
android:src="@drawable/comui_tab_message" />
<TextView
android:id="@+id/message_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/message_image_view"
android:layout_centerHorizontal="true"
android:layout_marginTop="3dp"
android:text="消息"
android:textColor="@color/comui_tab"
android:textSize="13sp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_layout_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/mine_image_view"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_centerHorizontal="true"
android:src="@drawable/comui_tab_person" />
<TextView
android:id="@+id/mine_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/mine_image_view"
android:layout_centerHorizontal="true"
android:layout_marginTop="3dp"
android:text="我的"
android:textColor="@color/comui_tab"
android:textSize="13sp" />
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/linearLayout" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_above="@+id/linearLayout"
android:background="@color/comui_tab" />
</RelativeLayout>
然后接着在Activity代码里实现,代码如下:
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home_selected));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mHomeFragment = new HomeFragment();
fm = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.content_layout, mHomeFragment);
fragmentTransaction.commit();
上面的代码是实现应用进入首页后默认要加载的Tab,然后以事务的方式提交。
public void labelSelection(int position) {
FragmentTransaction fragmentTransaction = fm.beginTransaction();
switch (position) {
case 0:
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home_selected));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mMessageView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_message));
message_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
mMineView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_person));
mine_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
hideFragment(mCommonFragmentOne, fragmentTransaction);
hideFragment(mMessageFragment, fragmentTransaction);
hideFragment(mMineFragment, fragmentTransaction);
if (mHomeFragment == null) {
mHomeFragment = new HomeFragment();
fragmentTransaction.add(R.id.content_layout, mHomeFragment);
} else {
mCurrent = mHomeFragment;
fragmentTransaction.show(mHomeFragment);
}
break;
case 1:
mMessageView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_message_selected));
message_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
mMineView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_person));
mine_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
hideFragment(mCommonFragmentOne, fragmentTransaction);
hideFragment(mHomeFragment, fragmentTransaction);
hideFragment(mMineFragment, fragmentTransaction);
if (mMessageFragment == null) {
mMessageFragment = new MessageFragment();
fragmentTransaction.add(R.id.content_layout, mMessageFragment);
} else {
mCurrent = mMessageFragment;
fragmentTransaction.show(mMessageFragment);
}
break;
case 2:
mMineView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_person_selected));
mine_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
mMessageView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_message));
message_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
hideFragment(mCommonFragmentOne, fragmentTransaction);
hideFragment(mMessageFragment, fragmentTransaction);
hideFragment(mHomeFragment, fragmentTransaction);
if (mMineFragment == null) {
mMineFragment = new MineFragment();
fragmentTransaction.add(R.id.content_layout, mMineFragment);
} else {
mCurrent = mMineFragment;
fragmentTransaction.show(mMineFragment);
}
break;
default:
break;
}
fragmentTransaction.commit();
}
上面的代码主要实现当用户切换底部Tab标签后要更换不用颜色的icon和不用颜色的文本以及加载对应的Fragment界面。
为了更好的理解,下面附上Activity完整的代码:
public class Home1Activity extends BaseActivity implements View.OnClickListener {
public static Home1Activity instance;
private FragmentManager fm;
private HomeFragment mHomeFragment;
private Fragment mCommonFragmentOne;
private MessageFragment mMessageFragment;
private MineFragment mMineFragment;
private Fragment mCurrent;
private RelativeLayout mHomeLayout;
private RelativeLayout mMessageLayout;
private RelativeLayout mMineLayout;
private ImageView mHomeView;
private TextView home_text_view;
private ImageView mMessageView;
private TextView message_text_view;
private ImageView mMineView;
private TextView mine_text_view;
private long exitTime = 0;
@Override
protected int getLayoutId() {
instance = this;
return R.layout.activity_home1_layout;
}
@Override
protected void initView() {
mHomeLayout = (RelativeLayout) findViewById(R.id.home_layout_view);
mHomeLayout.setOnClickListener(this);
mMessageLayout = (RelativeLayout) findViewById(R.id.message_layout_view);
mMessageLayout.setOnClickListener(this);
mMineLayout = (RelativeLayout) findViewById(R.id.mine_layout_view);
mMineLayout.setOnClickListener(this);
mHomeView = (ImageView) findViewById(R.id.home_image_view);
home_text_view = (TextView) findViewById(R.id.home_text_view);
mMessageView = (ImageView) findViewById(R.id.message_image_view);
message_text_view = (TextView) findViewById(R.id.message_text_view);
mMineView = (ImageView) findViewById(R.id.mine_image_view);
mine_text_view = (TextView) findViewById(R.id.mine_text_view);
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home_selected));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mHomeFragment = new HomeFragment();
fm = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.content_layout, mHomeFragment);
fragmentTransaction.commit();
}
@Override
protected void initData() {
}
private void hideFragment(Fragment fragment, FragmentTransaction ft) {
if (fragment != null) {
ft.hide(fragment);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.home_layout_view:
labelSelection(0);
break;
case R.id.message_layout_view:
labelSelection(1);
break;
case R.id.mine_layout_view:
labelSelection(2);
break;
default:
break;
}
}
public void labelSelection(int position) {
FragmentTransaction fragmentTransaction = fm.beginTransaction();
switch (position) {
case 0:
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home_selected));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mMessageView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_message));
message_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
mMineView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_person));
mine_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
hideFragment(mCommonFragmentOne, fragmentTransaction);
hideFragment(mMessageFragment, fragmentTransaction);
hideFragment(mMineFragment, fragmentTransaction);
if (mHomeFragment == null) {
mHomeFragment = new HomeFragment();
fragmentTransaction.add(R.id.content_layout, mHomeFragment);
} else {
mCurrent = mHomeFragment;
fragmentTransaction.show(mHomeFragment);
}
break;
case 1:
mMessageView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_message_selected));
message_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
mMineView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_person));
mine_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
hideFragment(mCommonFragmentOne, fragmentTransaction);
hideFragment(mHomeFragment, fragmentTransaction);
hideFragment(mMineFragment, fragmentTransaction);
if (mMessageFragment == null) {
mMessageFragment = new MessageFragment();
fragmentTransaction.add(R.id.content_layout, mMessageFragment);
} else {
mCurrent = mMessageFragment;
fragmentTransaction.show(mMessageFragment);
}
break;
case 2:
mMineView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_person_selected));
mine_text_view.setTextColor(getResources().getColor(R.color.comui_tab_selected));
mHomeView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_home));
home_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
mMessageView.setImageDrawable(getResources().getDrawable(R.drawable.comui_tab_message));
message_text_view.setTextColor(getResources().getColor(R.color.comui_tab));
hideFragment(mCommonFragmentOne, fragmentTransaction);
hideFragment(mMessageFragment, fragmentTransaction);
hideFragment(mHomeFragment, fragmentTransaction);
if (mMineFragment == null) {
mMineFragment = new MineFragment();
fragmentTransaction.add(R.id.content_layout, mMineFragment);
} else {
mCurrent = mMineFragment;
fragmentTransaction.show(mMineFragment);
}
break;
default:
break;
}
fragmentTransaction.commit();
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
ToastUtil.showToast("再按一次退出应用");
exitTime = System.currentTimeMillis();
} else {
// Intent intent = new Intent(Intent.ACTION_MAIN);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// intent.addCategory(Intent.CATEGORY_HOME);
// startActivity(intent);
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
界面运行效果图如下:
使用Fragment+ViewPager的方式
先定义一个xml布局,布局xml代码如下,布局中的AlphaTabsIndicator控件是用到自定义View,它继承于LinearLayout布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_page_bg"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#888" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/mViewPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<net.fkm.bottomtabtest.view.widget.AlphaTabsIndicator
android:id="@+id/alphaIndicator"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorWhite"
android:orientation="horizontal">
<net.fkm.bottomtabtest.view.widget.AlphaTabView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:tabIconNormal="@drawable/comui_tab_home"
app:tabIconSelected="@drawable/comui_tab_home_selected"
app:tabText="首页"
app:tabTextSize="13sp"
app:textColorNormal="@color/comui_tab"
app:textColorSelected="@color/comui_tab_selected" />
<net.fkm.bottomtabtest.view.widget.AlphaTabView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:tabIconNormal="@drawable/comui_tab_message"
app:tabIconSelected="@drawable/comui_tab_message_selected"
app:tabText="消息"
app:tabTextSize="13sp"
app:textColorNormal="@color/comui_tab"
app:textColorSelected="@color/comui_tab_selected" />
<net.fkm.bottomtabtest.view.widget.AlphaTabView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:padding="5dp"
app:tabIconNormal="@drawable/comui_tab_person"
app:tabIconSelected="@drawable/comui_tab_person_selected"
app:tabText="我的"
app:tabTextSize="13sp"
app:textColorNormal="@color/comui_tab"
app:textColorSelected="@color/comui_tab_selected" />
</net.fkm.bottomtabtest.view.widget.AlphaTabsIndicator>
</LinearLayout>
自定义View的AlphaTabsIndicator控件代码如下。
public class AlphaTabsIndicator extends LinearLayout {
private ViewPager mViewPager;
private OnTabChangedListner mListner;
private List<AlphaTabView> mTabViews;
private boolean ISINIT;
/**
* 子View的数量
*/
private int mChildCounts;
/**
* 当前的条目索引
*/
private int mCurrentItem = 0;
public AlphaTabsIndicator(Context context) {
this(context, null);
}
public AlphaTabsIndicator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AlphaTabsIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
post(new Runnable() {
@Override
public void run() {
isInit();
}
});
}
public void setViewPager(ViewPager mViewPager) {
this.mViewPager = mViewPager;
init();
}
public void setOnTabChangedListner(OnTabChangedListner listner) {
this.mListner = listner;
isInit();
}
public AlphaTabView getCurrentItemView() {
isInit();
return mTabViews.get(mCurrentItem);
}
public AlphaTabView getTabView(int tabIndex) {
isInit();
return mTabViews.get(tabIndex);
}
public void removeAllBadge() {
isInit();
for (AlphaTabView alphaTabView : mTabViews) {
alphaTabView.removeShow();
}
}
public void setTabCurrenItem(int tabIndex) {
if (tabIndex < mChildCounts && tabIndex > -1) {
mTabViews.get(tabIndex).performClick();
} else {
throw new IllegalArgumentException("IndexOutOfBoundsException");
}
}
private void isInit() {
if (!ISINIT) {
init();
}
}
private void init() {
ISINIT = true;
mTabViews = new ArrayList<>();
mChildCounts = getChildCount();
if (null != mViewPager) {
if (null == mViewPager.getAdapter()) {
throw new NullPointerException("viewpager的adapter为null");
}
if (mViewPager.getAdapter().getCount() != mChildCounts) {
throw new IllegalArgumentException("子View数量必须和ViewPager条目数量一致");
}
//对ViewPager添加监听
mViewPager.addOnPageChangeListener(new MyOnPageChangeListener());
}
for (int i = 0; i < mChildCounts; i++) {
if (getChildAt(i) instanceof AlphaTabView) {
AlphaTabView tabView = (AlphaTabView) getChildAt(i);
mTabViews.add(tabView);
//设置点击监听
tabView.setOnClickListener(new MyOnClickListener(i));
} else {
throw new IllegalArgumentException("TabIndicator的子View必须是TabView");
}
}
mTabViews.get(mCurrentItem).setIconAlpha(1.0f);
}
private class MyOnPageChangeListener extends ViewPager.SimpleOnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//滑动时的透明度动画
if (positionOffset > 0) {
mTabViews.get(position).setIconAlpha(1 - positionOffset);
mTabViews.get(position + 1).setIconAlpha(positionOffset);
}
//滑动时保存当前按钮索引
mCurrentItem = position;
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
resetState();
mTabViews.get(position).setIconAlpha(1.0f);
mCurrentItem = position;
}
}
private class MyOnClickListener implements OnClickListener {
private int currentIndex;
public MyOnClickListener(int i) {
this.currentIndex = i;
}
@Override
public void onClick(View v) {
//点击前先重置所有按钮的状态
resetState();
mTabViews.get(currentIndex).setIconAlpha(1.0f);
if (null != mListner) {
mListner.onTabSelected(currentIndex);
}
if (null != mViewPager) {
//不能使用平滑滚动,否者颜色改变会乱
mViewPager.setCurrentItem(currentIndex, false);
}
//点击是保存当前按钮索引
mCurrentItem = currentIndex;
}
}
/**
* 重置所有按钮的状态
*/
private void resetState() {
for (int i = 0; i < mChildCounts; i++) {
mTabViews.get(i).setIconAlpha(0);
}
}
private static final String STATE_INSTANCE = "instance_state";
private static final String STATE_ITEM = "state_item";
/**
* @return 当View被销毁的时候,保存数据
*/
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());
bundle.putInt(STATE_ITEM, mCurrentItem);
return bundle;
}
/**
* @param state 用于恢复数据使用
*/
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mCurrentItem = bundle.getInt(STATE_ITEM);
if (null == mTabViews || mTabViews.isEmpty()) {
super.onRestoreInstanceState(bundle.getParcelable(STATE_INSTANCE));
return;
}
//重置所有按钮状态
resetState();
//恢复点击的条目颜色
mTabViews.get(mCurrentItem).setIconAlpha(1.0f);
super.onRestoreInstanceState(bundle.getParcelable(STATE_INSTANCE));
} else {
super.onRestoreInstanceState(state);
}
}
}
然后接着在Activity代码里实现,代码如下:
public class Home2Activity extends BaseActivity {
private AlphaTabsIndicator alphaTabsIndicator;
private long exitTime = 0;
@Override
protected int getLayoutId() {
return R.layout.activity_home2_layout;
}
@Override
protected void initView() {
ViewPager mViewPger = (ViewPager) findViewById(R.id.mViewPager);
Home2Activity.HomeAdapter mainAdapter = new Home2Activity.HomeAdapter(getSupportFragmentManager());
mViewPger.setAdapter(mainAdapter);
mViewPger.addOnPageChangeListener(mainAdapter);
alphaTabsIndicator = (AlphaTabsIndicator) findViewById(R.id.alphaIndicator);
alphaTabsIndicator.setViewPager(mViewPger);
alphaTabsIndicator.getTabView(0).showNumber(10);
alphaTabsIndicator.getTabView(1).showNumber(100);
alphaTabsIndicator.getTabView(2).showPoint();
}
@Override
protected void initData() {
}
private class HomeAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener {
private List<Fragment> fragments = new ArrayList<>();
private String[] titles = {"首页", "消息", "我的"};
public HomeAdapter(FragmentManager fm) {
super(fm);
fragments.add(HomeFragment.newInstance(titles[0]));
fragments.add(MessageFragment.newInstance(titles[1]));
fragments.add(MineFragment.newInstance(titles[2]));
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (0 == position) {
alphaTabsIndicator.getCurrentItemView().removeShow();
} else if (2 == position) {
alphaTabsIndicator.getCurrentItemView().removeShow();
} else if (3 == position) {
alphaTabsIndicator.removeAllBadge();
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if ((System.currentTimeMillis() - exitTime) > 2000) {
ToastUtil.showToast("再按一次退出应用");
exitTime = System.currentTimeMillis();
} else {
// Intent intent = new Intent(Intent.ACTION_MAIN);
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// intent.addCategory(Intent.CATEGORY_HOME);
// startActivity(intent);
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
}
上面代码是使用ViewPager的方式来实现,它支持点击底部按钮或者左右滑动的方式来切换不同的界面。
界面运行效果图如下: