Android開発ノート(19)下部ラベルバーTabBar


下部ラベルページ実装の考え方
現在のアプリでは、ページの下部にタブバーTabbarが表示され、異なる欄のページを切り替えることが多い.Tabbarの起源はiOSで、iOSのTabbarは自動的にページの下にありますが、Androidが引っ越してきたときに変更しました.持参したTabbarはページの上にあり、ユーザーの使用習慣には向いていません.そのため、Androidで下部ラベルバーを実現するには、下部適合処理を追加しなければなりません.適合構想は基本的に下部にボタンを並べて、異なるボタンをクリックすることによって、異なるActivityページにジャンプします.具体的な実現方式は,ブロガーが現在3つ発見している:1,TabActivityを用いる.ここで、レイアウトファイルにTabHost、TabWidget、およびRadioButtonを設定し、コードファイルにTabActivity、TabSpec、およびComponentButtonを適用します.
2、Activity Groupを使用します.いくつかのコラムのトップページActivityをActivity Groupに入れ、クリックイベントに応じてどのActivityに切り替えるかを選択します.
3、FragmentActivityとFragmentを使う.いくつかのコラムのトップページFragmentをFragmentActivityに入れ、クリックイベントに自動的に応答します.
このうちTabActivityはActivity Groupから継承されており、現在AndroidはTabActivityとActivity Groupが廃棄されていると主張しており、FragmentとFragmentManagerの代替を推奨しています.実装コードを見ると,TabActivityとActivity Groupの両方の方式のコード量が多く,FragmentActivity方式のコードは簡潔に見えるので,ブロガーも第3の方式を推奨している.
次に、3つの方法のコード実装について簡単に説明します.
TabActivity方式の呼び出しコードは以下の通りである.
import android.app.TabActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TabHost;

@SuppressWarnings("deprecation")
public class TestTabActivity extends TabActivity implements OnCheckedChangeListener {
	private static final String TAG = "TestTabActivity";
	private Bundle mBundle = new Bundle();
	
	private TabHost tab_host;
	private CompoundButton first_button;
	private CompoundButton second_button;
	private CompoundButton third_button;
	private String FIRST_TAG = "first";
	private String SECOND_TAG = "second";
	private String THIRD_TAG = "third";

	private TabHost.TabSpec getNewTab(String spec, int label, int icon, Intent intent) {
		return tab_host
				.newTabSpec(spec)
				.setIndicator(getString(label), getResources().getDrawable(icon))
				.setContent(intent);
	}

	private void setButtonCheck(CompoundButton button) {
		if (button.equals(first_button)) {
			button.setChecked(true);
			second_button.setChecked(false);
			third_button.setChecked(false);
		} else if (button.equals(third_button)) {
			button.setChecked(true);
			second_button.setChecked(false);
			first_button.setChecked(false);
		} else if (button.equals(second_button)) {
			button.setChecked(true);
			first_button.setChecked(false);
			third_button.setChecked(false);
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_test_tab);
		mBundle.putString("tag", TAG);
		tab_host = getTabHost();
		tab_host.addTab(getNewTab(FIRST_TAG, R.string.menu_first,
				R.drawable.tab_first_selector, new Intent(this, FirstActivity.class).putExtras(mBundle)));
		tab_host.addTab(getNewTab(SECOND_TAG, R.string.menu_second,
				R.drawable.tab_second_selector, new Intent(this, SecondActivity.class).putExtras(mBundle)));
		tab_host.addTab(getNewTab(THIRD_TAG, R.string.menu_third,
				R.drawable.tab_third_selector, new Intent(this, ThirdActivity.class).putExtras(mBundle)));

		 first_button = ((CompoundButton) findViewById(R.id.rbtn_tab_first));
		 first_button.setOnCheckedChangeListener(this);
		 second_button = ((CompoundButton) findViewById(R.id.rbtn_tab_second));
		 second_button.setOnCheckedChangeListener(this);
		 third_button = ((CompoundButton) findViewById(R.id.rbtn_tab_third));
		 third_button.setOnCheckedChangeListener(this);

		tab_host.setCurrentTabByTag(FIRST_TAG);
		setButtonCheck(first_button);
	}

	@Override
	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		if (isChecked == true) {
			setButtonCheck(buttonView);
			if (buttonView.equals(first_button)) {
				tab_host.setCurrentTabByTag(FIRST_TAG);
			} else if (buttonView.equals(second_button)) {
				tab_host.setCurrentTabByTag(SECOND_TAG);
			} else if (buttonView.equals(third_button)) {
				tab_host.setCurrentTabByTag(THIRD_TAG);
			}
		}
	}

}

この方式の核心はgetNewTab関数であり,ラベルのテキストとアイコン,およびそのラベルに対応するジャンプページを設定できる.クリックイベントが発生すると、TabHostのsetCurrentTabByTag関数が呼び出され、特定のジャンプページにナビゲートされます.
Activity Group方式の呼び出しコードは以下の通りです.
import android.app.ActivityGroup;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

@SuppressWarnings("deprecation")
public class TestGroupActivity extends ActivityGroup implements OnClickListener {
	private static final String TAG = "TestGroupActivity";
	private Bundle mBundle = new Bundle();

	private LinearLayout layout_container, layout_first, layout_second, layout_third;
	private ImageView img_first, img_second, img_third;
	private TextView txt_first, txt_second, txt_third;
	public static final int PAGE_FIRST = 1001;
	public static final int PAGE_SECOND = 1002;
	public static final int PAGE_XINXI = 1003;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_test_group);
		layout_container = (LinearLayout) findViewById(R.id.layout_container);
		layout_first = (LinearLayout) findViewById(R.id.layout_first);
		layout_second = (LinearLayout) findViewById(R.id.layout_second);
		layout_third = (LinearLayout) findViewById(R.id.layout_third);
		img_first = (ImageView) findViewById(R.id.img_first);
		img_second = (ImageView) findViewById(R.id.img_second);
		img_third = (ImageView) findViewById(R.id.img_third);
		txt_first = (TextView) findViewById(R.id.txt_first);
		txt_second = (TextView) findViewById(R.id.txt_second);
		txt_third = (TextView) findViewById(R.id.txt_third);

		layout_first.setOnClickListener(this);
		layout_second.setOnClickListener(this);
		layout_third.setOnClickListener(this);
		mBundle.putString("tag", TAG);
		
		changeContainerView(PAGE_FIRST);
	}

	private void changeContainerView(int page) {
		switch (page) {
		case PAGE_FIRST:
			layout_first.setBackgroundResource(R.drawable.ic_tabbar_bg_click);
			layout_second.setBackgroundResource(R.drawable.ic_tabbar_bg_normal);
			layout_third.setBackgroundResource(R.drawable.ic_tabbar_bg_normal);
			img_first.setBackgroundResource(R.drawable.ic_tabbar_first_pressed);
			img_second.setBackgroundResource(R.drawable.ic_tabbar_second_normal);
			img_third.setBackgroundResource(R.drawable.ic_tabbar_third_normal);
			txt_first.setTextColor(getResources().getColor(R.color.tab_text_selected));
			txt_second.setTextColor(getResources().getColor(R.color.tab_text_normal));
			txt_third.setTextColor(getResources().getColor(R.color.tab_text_normal));
			toActivity("item1", new Intent(this, FirstActivity.class).putExtras(mBundle));
			return;
		case PAGE_SECOND:
			layout_first.setBackgroundResource(R.drawable.ic_tabbar_bg_normal);
			layout_second.setBackgroundResource(R.drawable.ic_tabbar_bg_click);
			layout_third.setBackgroundResource(R.drawable.ic_tabbar_bg_normal);
			img_first.setBackgroundResource(R.drawable.ic_tabbar_first_normal);
			img_second.setBackgroundResource(R.drawable.ic_tabbar_second_pressed);
			img_third.setBackgroundResource(R.drawable.ic_tabbar_third_normal);
			txt_first.setTextColor(getResources().getColor(R.color.tab_text_normal));
			txt_second.setTextColor(getResources().getColor(R.color.tab_text_selected));
			txt_third.setTextColor(getResources().getColor(R.color.tab_text_normal));
			toActivity("item1", new Intent(this, SecondActivity.class).putExtras(mBundle));
			return;
		case PAGE_XINXI:
			layout_first.setBackgroundResource(R.drawable.ic_tabbar_bg_normal);
			layout_second.setBackgroundResource(R.drawable.ic_tabbar_bg_normal);
			layout_third.setBackgroundResource(R.drawable.ic_tabbar_bg_click);
			img_first.setBackgroundResource(R.drawable.ic_tabbar_first_normal);
			img_second.setBackgroundResource(R.drawable.ic_tabbar_second_normal);
			img_third.setBackgroundResource(R.drawable.ic_tabbar_third_pressed);
			txt_first.setTextColor(getResources().getColor(R.color.tab_text_normal));
			txt_second.setTextColor(getResources().getColor(R.color.tab_text_normal));
			txt_third.setTextColor(getResources().getColor(R.color.tab_text_selected));
			toActivity("item1", new Intent(this, ThirdActivity.class).putExtras(mBundle));
			return;
		default:
			return;
		}
	}

	@Override
	public void onClick(View view) {
		switch (view.getId()) {
		case R.id.layout_first:
			changeContainerView(PAGE_FIRST);
			return;
		case R.id.layout_second:
			changeContainerView(PAGE_SECOND);
			return;
		case R.id.layout_third:
			changeContainerView(PAGE_XINXI);
			return;
		default:
			return;
		}
	}
	
	private void toActivity(String label, Intent intent) {
		layout_container.removeAllViews();
		View view = getLocalActivityManager().startActivity(label, intent).getDecorView();
		layout_container.addView(view);
	}

}

この方式の核心はtoActivity関数であり、ラベルのテキストとアイコン、およびそのラベルに対応するジャンプページを設定することもできる.toActivity関数では、startActivityメソッドがWindowオブジェクトを返し、このWindowオブジェクトからラベルページの実際のビューgetDecorView(ラベルページのルートビューとして理解できる)を抽出し、このDecorViewをTabbarのビューコンテナに追加することがわかります.
FragmentActivity方式の呼び出しコードは以下の通りです.
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;

@SuppressLint("InflateParams")
public class TestFragmentActivity extends FragmentActivity {
	private static final String TAG = "TestFragmentActivity";
	private Bundle mBundle = new Bundle();
	private FragmentTabHost mTabHost;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_test_fragment);

		mBundle.putString("tag", TAG);
		mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
       	mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
		
		//addTab(  ,   Fragment,     Bundle)
		mTabHost.addTab(getTabView(R.string.menu_first, R.drawable.tabbar_first_selector), FirstFragment.class, mBundle);
		mTabHost.addTab(getTabView(R.string.menu_second, R.drawable.tabbar_second_selector), SecondFragment.class, mBundle);
		mTabHost.addTab(getTabView(R.string.menu_third, R.drawable.tabbar_third_selector), ThirdFragment.class, mBundle);
		//  tabs         
		mTabHost.getTabWidget().setShowDividers(LinearLayout.SHOW_DIVIDER_NONE);
	}
	
	private TabSpec getTabView(int textId, int imgId) {
		String text = getResources().getString(textId);
		Drawable drawable = getResources().getDrawable(imgId);
		//        ,     
		drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
		View tabbar_item = getLayoutInflater().inflate(R.layout.tabbar_item, null);
		TextView tv_item = (TextView) tabbar_item.findViewById(R.id.tv_item);
		tv_item.setText(text);
		tv_item.setCompoundDrawables(null, drawable, null, null);
		TabSpec spec = mTabHost.newTabSpec(text).setIndicator(tabbar_item);
		return spec;
	}
	
}

この方式の核心はgetTabView関数であり、各ラベル項目の具体的なビューをカスタマイズすることができる.FragmentTabHostはクリックイベントを自動的に処理しているため,この方式のコード量は前の2つより大幅に縮小している.
下のラベルページの3つの方法の効果図です
本明細書で使用する下部ラベルページの3つの方法のコードをダウンロードするにはクリックします.