還記得幾個月前剛接觸“百度貼吧”這款應用時,瞬間就被它的懸浮式標題欄以及標簽欄所吸引,雖然當時身邊有個UI的MM說懸浮式一般都是具備隱藏的功能時才設計成這樣的,一般情況下還是用直通式的好,在這裡我也不討論孰優孰劣,截取兩個圖,大家自己品味下。
懸浮式標題欄和標簽欄:(百度貼吧);直通式標題欄和標簽欄:(QQ空間)
本文的目的就是研究下百度貼吧的這個標題欄和標簽欄的實現原理;
1、 懸浮式標題欄的實現
實現原理有兩種:1)在布局文件中設置標題欄的layout_margin屬性;2)給標題欄設置一個9-patch格式的background圖片,在這種圖片上設置上下左右的空白間隔,如下圖所示,背景圖片的top、left和right分別有一個像素的空白,bottom則是兩個像素。
使用第二種方式實現的標題欄的布局文件如下:
- <!-- 標題欄 -->
- <LinearLayout
- Android:id="@+id/home_layout_bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_gravity="top"
- android:background="@drawable/title_bg"
- android:gravity="center" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/home_topbar_bg"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <!-- 我的貼吧 -->
- <ImageButton
- android:id="@+id/home_bt_like"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_gravity="center"
- android:layout_margin="0.0dip"
- android:layout_weight="1.0"
- android:background="@null"
- android:padding="0.0dip"
- android:paddingBottom="2.0dip"
- android:scaleType="center"
- android:src="@drawable/home_bt_like_on" />
-
- <!-- 我的標簽 -->
- <ImageButton
- android:id="@+id/home_bt_mark"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_gravity="center"
- android:layout_margin="0.0dip"
- android:layout_weight="1.0"
- android:background="@null"
- android:padding="0.0dip"
- android:paddingBottom="2.0dip"
- android:scaleType="center"
- android:src="@drawable/home_bt_mark" />
- </LinearLayout>
- </LinearLayout>
其中title_bg就是用來實現懸浮效果的9-patch背景圖片,這裡為了優化視覺上的效果,在背景圖片上又加了一層圖片,也就是home_topbar_bg,加與不加的對比效果如圖:
我的貼吧和我的書簽這兩個按鈕的按下與松開的是通過切換android:background和android:src的圖片資源來實現的,而沒有采用通常的selector方式,代碼中設置按鈕的單擊響應函數,更換按鈕圖片資源的關鍵代碼如下:
- mButtonLike.setBackgroundResource(R.drawable.home_topbar_bt);
- mButtonLike.setImageResource(R.drawable.home_bt_like_on);
- mButtonMark.setBackgroundDrawable(null);
- mButtonMark.setImageResource(R.drawable.home_bt_mark);
2、 懸浮式標簽欄的實現
標簽欄的實現有很多種組合,比較省事的是使用系統提供的TabHost+TabWidget,比較自由的是使用TabHost+RadioGroup,下面使用第二種方式。
標簽欄懸浮效果同樣是通過9-patch背景圖片實現的,如下圖:
標簽欄的實現必須具備三個基本元素,如下圖所示,分別是1)id為@android:id/tabhost的<TabHost>標簽;2)id為@android:id/tabcontent的<FrameLayout>標簽;3)id為@android:id/tabs的<TabWidget>標簽。如下圖所示:
由於這裡我們使用TabHost+RadioGroup的方式,也就是使用RadioGroup代替TabWidget,所以布局文件中TabWidget的可見性設置為gone,緊接上面布局的是RadioGroup的布局:
- <FrameLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginTop="-10.0dip"
- android:background="@drawable/maintab_toolbar_bg"
- android:paddingLeft="7.0dip"
- android:paddingRight="7.0dip" >
-
- <RadioGroup
- android:id="@+id/main_radio"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="horizontal" >
-
- <RadioButton
- android:id="@+id/radio_home"
-
- android:drawableTop="@drawable/tabs_home"
- android:text="@string/bar" />
-
- <RadioButton
- android:id="@+id/radio_mention"
-
- android:drawableTop="@drawable/tabs_sort"
- android:text="@string/remind" />
-
- <RadioButton
- android:id="@+id/radio_person_info"
-
- android:drawableTop="@drawable/tabs_search"
- android:text="@string/person_info" />
-
- <RadioButton
- android:id="@+id/radio_more"
-
- android:drawableTop="@drawable/tabs_more"
- android:text="@string/more" />
- </RadioGroup>
-
- <TextView
- android:id="@+id/message_mention"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|left|center"
- android:layout_marginLeft="115.0dip"
- android:layout_marginTop="-5.0dip"
- android:background="@drawable/message_tips"
- android:gravity="center"
- android:text=""
- android:textColor="#ffffffff"
- android:textSize="13.0sp"
- android:visibility="visible" />
-
- <TextView
- android:id="@+id/message_person"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|left|center"
- android:layout_marginLeft="190.0dip"
- android:layout_marginTop="-5.0dip"
- android:background="@drawable/message_tips"
- android:gravity="center"
- android:text=""
- android:textColor="#ffffffff"
- android:textSize="13.0sp"
- android:visibility="visible" />
- </FrameLayout>
其中
1) maintab_toolbar_bg 是標簽欄的背景圖,實現懸浮效果;
2) RadioButton中的
android:drawableTop="@drawable/tabs_sort"
android:text="@string/remind"
組合可以輕松實現圖片上文字下的效果;
3) FrameLayout中的android:layout_marginTop="-10.0dip"實現標簽欄與上方id為tabcontent的FrameLayout部分重疊的效果(10.0dip);
4)兩個TextView和FrameLayout配合實現下圖中標簽右上角的消息個數提示功能
接下來就是代碼如何實現標簽的添加,標簽的點擊響應等等,直接看代碼了:
- package com.hust.iprai.wen;
-
- import android.app.AlertDialog;
- import android.app.TabActivity;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.KeyEvent;
- import android.widget.CompoundButton;
- import android.widget.RadioButton;
- import android.widget.TabHost;
- import android.widget.TextView;
-
- public class TiebaActivity extends TabActivity implements
- CompoundButton.OnCheckedChangeListener {
-
- private static final String HOME_TAB = "home_tab";
- private static final String MENTION_TAB = "mention_tab";
- private static final String PERSON_TAB = "person_tab";
- private static final String MORE_TAB = "more_tab";
-
- private Intent mHomeIntent = null;
- private Intent mMentionIntent = null;
- private Intent mPersonIntent = null;
- private Intent mMoreIntent = null;
-
- private TabHost mTabHost = null;
-
- private TextView mMessageTipsMention = null;
- private TextView mMessageTipsPerson = null;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.maintabs_activity);
-
- mTabHost = getTabHost();
- initIntents();
- initTips();
- initRadios();
- setupIntents();
- }
-
- private void initIntents() {
- mHomeIntent = new Intent(this, HomeActivity.class);
- mMentionIntent = new Intent(this, MentionActivity.class);
- mPersonIntent = new Intent(this, PersonInfoActivity.class);
- mMoreIntent = new Intent(this, MoreActivity.class);
- }
-
- private void initTips() {
- mMessageTipsMention = (TextView) findViewById(R.id.message_mention);
- mMessageTipsPerson = (TextView) findViewById(R.id.message_person);
- mMessageTipsMention.setText("2");
- mMessageTipsPerson.setText("4");
- }
-
- private void initRadios() {
- ((RadioButton) findViewById(R.id.radio_home))
- .setOnCheckedChangeListener(this);
- ((RadioButton) findViewById(R.id.radio_mention))
- .setOnCheckedChangeListener(this);
- ((RadioButton) findViewById(R.id.radio_person_info))
- .setOnCheckedChangeListener(this);
- ((RadioButton) findViewById(R.id.radio_more))
- .setOnCheckedChangeListener(this);
- }
-
- private void setupIntents() {
- ((RadioButton) findViewById(R.id.radio_home)).setChecked(true);
- mTabHost.addTab(buildTabSpec(HOME_TAB, mHomeIntent));
- mTabHost.addTab(buildTabSpec(MENTION_TAB, mMentionIntent));
- mTabHost.addTab(buildTabSpec(PERSON_TAB, mPersonIntent));
- mTabHost.addTab(buildTabSpec(MORE_TAB, mMoreIntent));
- mTabHost.setCurrentTabByTag(HOME_TAB);
- }
-
- private TabHost.TabSpec buildTabSpec(String tag, Intent intent) {
- TabHost.TabSpec tabSpec = mTabHost.newTabSpec(tag);
- tabSpec.setContent(intent).setIndicator("",
- getResources().getDrawable(R.drawable.icon));
- return tabSpec;
- }
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- switch (buttonView.getId()) {
- case R.id.radio_home:
- mTabHost.setCurrentTabByTag(HOME_TAB);
- break;
- case R.id.radio_mention:
- mTabHost.setCurrentTabByTag(MENTION_TAB);
- break;
- case R.id.radio_person_info:
- mTabHost.setCurrentTabByTag(PERSON_TAB);
- break;
- case R.id.radio_more:
- mTabHost.setCurrentTabByTag(MORE_TAB);
- break;
- default:
- break;
- }
- }
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if ((event.getAction() == KeyEvent.ACTION_DOWN)
- && (event.getKeyCode() == KeyEvent.KEYCODE_BACK)) {
- quitDialog();
- }
- return super.dispatchKeyEvent(event);
- }
-
- private void quitDialog() {
- new AlertDialog.Builder(this)
- .setTitle(R.string.alerm_title)
- .setIcon(null)
- .setCancelable(false)
- .setMessage(R.string.alert_quit_confirm)
- .setPositiveButton(R.string.alert_yes_button,
- new DialogInterface.OnClickListener() {
-
- public void onClick(DialogInterface dialog,
- int which) {
- TiebaActivity.this.finish();
- }
- })
- .setNegativeButton(R.string.alert_no_button,
- new DialogInterface.OnClickListener() {
-
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- }
- }).create().show();
- }
- }