多行TabWidget组件

android.widget.TabWidget组件继承自LinearLayout,默认水平布局横向显示多个Tab(改垂直布局似乎就不能正常显示了)。如果要显示多行,每行水平分布若干个Tab,TabWidget是无能为力的。我继承TabWidget实现了一个MultirowTabWidget类,可以显示多行Tab。效果图如下。


多行TabWidget组件_第1张图片

多行TabWidget组件_第2张图片

图1是初始状态,只显示单行Tab,图2是点击了“更多”后显示多行Tab的效果。

具体原理就是加一个maxTabCountOfRow属性用于保存每行最多显示多少个Tab,然后设置垂直布局,加进若干水平布局的LinearLayout进去,最终Tab实际被加进这些LinearLayout里。重写TabWidget的addView方法,每次有新的child加进来,先判断最后一个LinearLayout是否已加满,没加满就把新的Tab加到它里面,加满的话就创建下一个LinearLayout来放Tab。接下来贴一下主要的代码。

MultirowTabWidget.java
package com.thornbird.multirowtab.widget;

import java.lang.reflect.Field;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import android.widget.TabWidget;

public class MultirowTabWidget extends TabWidget {

	private static final String ROW_TAG = "MultirowTabWidgetRow";

	private boolean mMultirow = false;

	private int mMaxTabCountOfRow = 5;

	private Drawable mVerticalDividerDrawable = null;

	public MultirowTabWidget(Context context) {
		super(context);

		initTabWidget();
	}

	public MultirowTabWidget(Context context, AttributeSet attrs) {
		super(context, attrs);

		initTabWidget();
	}

	public MultirowTabWidget(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);

		initTabWidget();
	}

	public MultirowTabWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
		super(context, attrs, defStyleAttr, defStyleRes);

		initTabWidget();
	}

	@Override
	public void setGravity(int gravity) {
		super.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
	}

	@Override
	public void setOrientation(int orientation) {
		super.setOrientation(VERTICAL);
	}

	@Override
	public void setShowDividers(int showDividers) {
		super.setShowDividers(SHOW_DIVIDER_MIDDLE);
	}

	@Override
	public void setStripEnabled(boolean stripEnabled) {
		super.setStripEnabled(false);
	}

	@Override
	public void addView(View child) {
		int tabCount = getTabCount();
		int currentRowCount = (int) Math.ceil((double) tabCount / (double) mMaxTabCountOfRow);
		int rowCount = (int) Math.ceil((double) (tabCount + 1) / (double) mMaxTabCountOfRow);
		LinearLayout row = null;
		LayoutParams rlp = null;
		LayoutParams tlp = null;

		if (currentRowCount == rowCount) {
			row = (LinearLayout) getChildAt(this.getChildCount() - 1);
		} else {
			row = new LinearLayout(getContext());
			super.addView(row);
			row.setFocusable(false);
			row.setClickable(false);
			row.setOnClickListener(null);
			row.setOnFocusChangeListener(null);
			row.setDividerDrawable(mVerticalDividerDrawable);
			row.setShowDividers(SHOW_DIVIDER_MIDDLE);
			row.setOrientation(HORIZONTAL);
			row.setTag(ROW_TAG);
			rlp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
			rlp.setMargins(0, 0, 0, 0);
			row.setLayoutParams(rlp);
		}

		super.addView(child);
		removeView(child);
		tlp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
		tlp.setMargins(0, 0, 0, 0);
		row.addView(child, tlp);
	}

	@Override
	public void removeAllViews() {
		int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View view = getChildAt(i);
			Object tag = view.getTag();
			if (tag != null && tag.toString().equals(ROW_TAG)) {
				ViewGroup row = (ViewGroup) getChildAt(i);
				row.removeAllViews();
			}
		}

		super.removeAllViews();
	}

	@Override
	public int getTabCount() {
		int tabCount = 0;
		int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View view = getChildAt(i);
			Object tag = view.getTag();
			if (tag != null && tag.toString().equals(ROW_TAG)) {
				ViewGroup row = (ViewGroup) getChildAt(i);
				tabCount = tabCount + row.getChildCount();
			} else {
				tabCount = tabCount + 1;
			}
		}
		return tabCount;
	}

	@Override
	public View getChildTabViewAt(int index) {
		View tab = null;
		int currentIndex = -1;
		int count = getChildCount();
		for (int i = 0; i < count; i++) {
			View view = getChildAt(i);
			Object tag = view.getTag();
			if (tag != null && tag.toString().equals(ROW_TAG)) {
				ViewGroup row = (ViewGroup) getChildAt(i);
				int tabCount = row.getChildCount();
				for (int j = 0; j < tabCount; j++) {
					currentIndex = currentIndex + 1;
					if (currentIndex == index) {
						tab = row.getChildAt(j);
						break;
					}
				}
			}
			if (tab != null)
				break;
		}
		return tab;
	}

	@Override
	protected int getChildDrawingOrder(int childCount, int i) {
		return i;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		int measuredWidth = getMeasuredWidth();
		int measuredHeight = getExplicitHeight(true);
		setMeasuredDimension(measuredWidth, measuredHeight);

		ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) getLayoutParams();
		if (mlp == null)
			return;

		int bottomMargin = 0;
		if (!mMultirow)
			bottomMargin = getExplicitHeight(false) - getExplicitHeight(true);
		if (mlp.bottomMargin != bottomMargin) {
			mlp.bottomMargin = bottomMargin;
			setLayoutParams(mlp);
		}
	}

	public boolean isMultirow() {
		return mMultirow;
	}

	public void setMultirow(boolean multirow) {
		if (mMultirow == multirow)
			return;

		mMultirow = multirow;
		new Handler().post(new Runnable() {
			@Override
			public void run() {
				setVisibility(INVISIBLE);
				requestLayout();
				playAnim();
			}
		});
	}

	public int getMaxTabCountOfRow() {
		return mMaxTabCountOfRow;
	}

	public void setMaxTabCountOfRow(int maxTabCountOfRow) {
		if (getChildCount() == 0)
			mMaxTabCountOfRow = Math.max(maxTabCountOfRow, 1);
	}

	public Drawable getVerticalDividerDrawable() {
		return mVerticalDividerDrawable;
	}

	public void setVerticalDividerDrawable(Drawable drawable) {
		mVerticalDividerDrawable = drawable;
	}

	public void setVerticalDividerDrawable(int resId) {
		setVerticalDividerDrawable(getResources().getDrawable(resId));
	}

	public int getExplicitHeight(boolean multirow) {
		int measuredHeight = 0;
		Drawable divider = getDividerDrawable();
		int dividerHeight = (divider == null ? 0 : divider.getIntrinsicHeight());
		int count = getChildCount();
		if (!multirow)
			count = 1;
		for (int i = 0; i < count; i++) {
			View view = getChildAt(i);
			if (i > 0 && i < count)
				measuredHeight = measuredHeight + dividerHeight;
			measuredHeight = measuredHeight + view.getMeasuredHeight();
		}
		return measuredHeight;
	}

	public int getSelectedTab() {
		Class<TabWidget> c = TabWidget.class;
		Field f = null;
		int selectedTab = -1;

		try {
			f = c.getDeclaredField("mSelectedTab");
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}

		if (f != null) {
			f.setAccessible(true);
			try {
				Integer value = (Integer) f.get(this);
				if (value != null)
					selectedTab = value.intValue();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
			f.setAccessible(false);
		}

		return selectedTab;
	}

	protected void initTabWidget() {
		setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
		setOrientation(VERTICAL);
		setShowDividers(SHOW_DIVIDER_MIDDLE);
		setStripEnabled(false);
	}
	
	protected void playAnim() {
		clearAnimation();
		if (getChildCount() <= 1 || getParent() == null)
			return;

		int fromValue = 0;
		int toValue = 0;
		if (mMultirow) {
			fromValue = getExplicitHeight(true) - getExplicitHeight(false);
			toValue = 0;
		} else {
			fromValue = getExplicitHeight(false) - getExplicitHeight(true);
			toValue = 0;
		}

		TranslateAnimation anim = new TranslateAnimation(0, 0, fromValue, toValue);
		anim.setDuration(150L);
		anim.setAnimationListener(new TabWidgetAnimationListener(MultirowTabWidget.this));
		startAnimation(anim);
	}


	private static class TabWidgetAnimationListener implements AnimationListener {

		private MultirowTabWidget mTabWidget = null;

		public TabWidgetAnimationListener(MultirowTabWidget tabWidget) {
			mTabWidget = tabWidget;
		}

		@Override
		public void onAnimationStart(Animation animation) {
		}

		@Override
		public void onAnimationEnd(Animation animation) {
			mTabWidget.setVisibility(VISIBLE);
		}

		@Override
		public void onAnimationRepeat(Animation animation) {
		}

	}

}


main_tab_item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/main_tab_selector"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="@dimen/main_tab_image_padding">

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="@dimen/main_tab_image_size"
        android:layout_height="@dimen/main_tab_image_size"
        android:contentDescription="@string/imageview_description"
        android:focusable="false"
        android:scaleType="centerInside" />

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="@dimen/main_tab_text_height"
        android:gravity="center_vertical"
        android:textSize="@dimen/main_tab_textview_text_size"
        android:textColor="@color/main_tab_text_selector" />

</LinearLayout>


activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.thornbird.multirowtab.MainActivity" >

    <android.support.v4.app.FragmentTabHost
        android:id="@+id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <FrameLayout
                android:id="@+id/realtabcontent"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1" />

            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_weight="0" />

            <com.thornbird.multirowtab.widget.MultirowTabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/main_tab_background" />
        </LinearLayout>
    </android.support.v4.app.FragmentTabHost>

</RelativeLayout>


MainActivity.java
package com.thornbird.multirowtab;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.TabHost.OnTabChangeListener;
import android.widget.TabHost.TabSpec;

import com.thornbird.multirowtab.widget.MultirowTabWidget;
import com.thornbird.multirowtab.R;

public class MainActivity extends FragmentActivity {

	private static Class<?> sFragments[] = {FragmentPage.class, FragmentPage.class,
			FragmentPage.class, FragmentPage.class, FragmentPage.class, FragmentPage.class,
			FragmentPage.class, FragmentPage.class};

	private static int sTabImages[] = {R.drawable.main_tab_cart_selector,
			R.drawable.main_tab_mail_selector,
			R.drawable.main_tab_search_selector,
			R.drawable.main_tab_more_selector,
			R.drawable.main_tab_user_selector,
			R.drawable.main_tab_history_selector,
			R.drawable.main_tab_chart_selector,
			R.drawable.main_tab_settings_selector};

	private static String sTabIds[] = {"tab1", "tab2", "tab3", "tab4", "tab5", "tab6", "tab7", "tab8"};

	private static String sTabTexts[] = {"购物车", "信息", "搜索", "更多", "账户", "历史", "统计", "设置"};

	private LayoutInflater mLayoutInflater;
	private FragmentTabHost mTabHost;
	private MultirowTabWidget mTabWidget;

	private int mLastTabId = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_main);
		findViews();
		init();
		setListeners();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	protected void findViews() {
		setContentView(R.layout.activity_main);

		mLayoutInflater = LayoutInflater.from(this);
		mTabHost = (FragmentTabHost)findViewById(R.id.tabhost);
		mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
		mTabWidget = (MultirowTabWidget) mTabHost.getTabWidget();
	}

	protected void init() {
		mTabWidget.setMaxTabCountOfRow(4);
		mTabWidget.setDividerDrawable(R.drawable.tab_divider_horizontal);
		mTabWidget.setVerticalDividerDrawable(R.drawable.tab_divider_vertical);

		int count = sFragments.length;
		for (int i = 0; i < count; i++) {
			String tag = sTabIds[i];
			TabSpec tabSpec = mTabHost.newTabSpec(tag).setIndicator(getTabItemView(i));
			mTabHost.addTab(tabSpec, sFragments[i], null);
		}
	}

	protected void setListeners() {
		mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
			@Override
			public void onTabChanged(String tabId) {
				if (tabId.equals(sTabIds[3])) {
					mTabHost.setCurrentTab(mLastTabId);
					mTabWidget.setMultirow(!mTabWidget.isMultirow());
				} else {
					mLastTabId = mTabHost.getCurrentTab();
				}
			}
		});
	}

	@SuppressLint("InflateParams")
	private View getTabItemView(int index) {
		View view = mLayoutInflater.inflate(R.layout.main_tab_item_view, null);
		ImageView imageView = (ImageView) view.findViewById(R.id.imageview);
		imageView.setImageResource(sTabImages[index]); 
		TextView textView = (TextView) view.findViewById(R.id.textview);
		textView.setText(sTabTexts[index]);
		return view;
	}

}


MultirowTabWidget类只适用于Tab显示在底部的FragmentTabHost或TabHost中,为了实现单行与多行间切换的动画,我设置MultirowTabWidget的底边边距用来显示单行或多行。其实最初的版本我是控制MultirowTabWidget中除第一个LinearLayout之外的LinearLayout隐藏或显示来显示单行与多行,那样的话应该能放到顶部,但加切换动画就麻烦点了。

附件中的MultirowTab.zip是源码

你可能感兴趣的:(android)