亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android實(shí)現(xiàn)底部導(dǎo)航欄效果

 更新時(shí)間:2022年01月19日 11:12:36   作者:幸福de小陽  
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)底部導(dǎo)航欄效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

目前網(wǎng)上主流的文章都是用底部的 RadioGroup + 頁面部分的 Fragment 實(shí)現(xiàn)導(dǎo)航欄切換頁面效果的。

然而底部的 RadioGroup 是如此麻煩,每個(gè)按鈕的圖片和文字部分都要做一個(gè) selector 用于表示選中和非選中兩種狀態(tài)時(shí)的樣式。

另外 Fragment 也有很多坑,先不管大家是否已熟練掌握,反正我是看著看著就學(xué)不下去了,所以我另辟蹊徑用 Activity 的方式實(shí)現(xiàn)了偽 Fragment 的效果。

這里我們就來做一個(gè)三個(gè)按鈕的底部導(dǎo)航欄。

因?yàn)槲覀冞@里是用三個(gè) Activity 實(shí)現(xiàn)三個(gè)頁面,而并非一個(gè) Activity 中的三個(gè) Fragment,所以在此之前,我們需要建立一個(gè)管理活動堆棧的類,以便在程序退出時(shí)能直接結(jié)束堆棧中的所有活動,不至于要依次退出三個(gè) Activity。

在 java 代碼目錄里新建一個(gè) base 包,在包內(nèi)新建文件 AppManager.java:

/**
?* AppManager: 用于對活動進(jìn)行管理。
?* 該模塊僅限 base 包內(nèi)使用。
?* 該模塊為單一實(shí)例,您需要調(diào)用 AppManager.get() 獲取實(shí)例后再調(diào)用方法。
?* <p>
?* 為確保應(yīng)用管理器正常工作,請新建一個(gè)繼承 Activity 的抽象類 BaseActivity,
?* 然后重寫 BaseActivity 類的 onCreate() 和 onDestroy() 方法。
?* 請給 BaseActivity 類的 onCreate() 方法添加如下代碼:
?* AppManager.get().addActivity(this);
?* 請給 BaseActivity 類的 onDestroy() 方法添加如下代碼:
?* AppManager.get().removeActivity(this);
?* 最后,確保本 APP 內(nèi)的所有活動類均繼承于 BaseActivity 類。
?*/
class AppManager {
? ? private static AppManager sManager = new AppManager();
? ? private Stack<BaseActivity> mActivities;
?
? ? private AppManager() {
? ? ? ? // 將作用域關(guān)鍵字設(shè)置為 private 以隱藏該類的構(gòu)造器。
? ? ? ? // 該類的單例由 get() 方法引用。
? ? ? ? // 創(chuàng)建單例的同時(shí)創(chuàng)建活動堆棧。
? ? ? ? mActivities = new Stack<>();
? ? } // AppManager() (Class Constructor)
?
? ? /**
? ? ?* get(): 獲得 AppManager 類的單例。
? ? ?*
? ? ?* @return 該類的單例 sManager。
? ? ?*/
? ? static AppManager get() {
? ? ? ? return sManager;
? ? } // get()
?
? ? /**
? ? ?* addActivity(): 向堆棧中添加一個(gè)活動對象。
? ? ?*
? ? ?* @param activity 要添加的活動對象。
? ? ?*/
? ? void addActivity(BaseActivity activity) {
? ? ? ? mActivities.add(activity);
? ? } // addActivity()
?
? ? /**
? ? ?* removeActivity(): 從堆棧中移除一個(gè)活動對象。
? ? ?*
? ? ?* @param activity 要移除的活動對象。
? ? ?*/
? ? void removeActivity(BaseActivity activity) {
? ? ? ? mActivities.remove(activity);
? ? } // removeActivity()
?
? ? /**
? ? ?* finishAllExcept(): 除一個(gè)特定活動外,結(jié)束堆棧中其余所有活動。
? ? ?* 結(jié)束活動時(shí)會觸發(fā) BaseActivity 類的 onDestroy()方法,
? ? ?* 堆棧中的活動對象會同步移除。
? ? ?*
? ? ?* @param activityClass 要保留的活動的類名(xxxActivity.class)
? ? ?*/
? ? void finishAllExcept(Class activityClass) {
? ? ? ? int i, len;
? ? ? ? BaseActivity[] activities;
?
? ? ? ? // 結(jié)束活動時(shí)會調(diào)用活動的 onDestroy() 方法,堆棧的內(nèi)容會實(shí)時(shí)改變
? ? ? ? // 為避免因此引起的引用錯誤,先將堆棧的內(nèi)容復(fù)制到一個(gè)臨時(shí)數(shù)組里
? ? ? ? activities = mActivities.toArray(new BaseActivity[0]);
? ? ? ? len = activities.length;
? ? ? ? for (i = 0; i < len; ++i) {
? ? ? ? ? ? if (!activities[i].getClass().equals(activityClass)) {
? ? ? ? ? ? ? ? // 從數(shù)組里引用活動對象并結(jié)束,堆棧內(nèi)容的改變不影響數(shù)組
? ? ? ? ? ? ? ? activities[i].finish();
? ? ? ? ? ? } // if (!activities[i].getClass().equals(activityClass))
? ? ? ? } // for (i = 0; i < len; ++i)
? ? } // finishAllExcept()
?
? ? /**
? ? ?* finishAllActivities(): 結(jié)束堆棧中的所有活動。
? ? ?* 結(jié)束活動時(shí)會觸發(fā) BaseActivity 類的 onDestroy()方法,
? ? ?* 堆棧中的活動對象會同步移除。
? ? ?*/
? ? void finishAllActivities() {
? ? ? ? int i, len;
? ? ? ? BaseActivity[] activities;
?
? ? ? ? // 結(jié)束活動時(shí)會調(diào)用活動的 onDestroy() 方法,堆棧的內(nèi)容會實(shí)時(shí)改變
? ? ? ? // 為避免因此引起的引用錯誤,先將堆棧的內(nèi)容復(fù)制到一個(gè)臨時(shí)數(shù)組里
? ? ? ? activities = mActivities.toArray(new BaseActivity[0]);
? ? ? ? len = activities.length;
? ? ? ? for (i = 0; i < len; ++i) {
? ? ? ? ? ? // 從數(shù)組里引用活動對象并結(jié)束,堆棧內(nèi)容的改變不影響數(shù)組
? ? ? ? ? ? activities[i].finish();
? ? ? ? } // for (i = 0; i < len; ++i)
? ? } // finishAllActivities()
} // AppManager Class
?
// E.O.F

上述代碼粘貼完后會報(bào)錯,別著急,那是因?yàn)槲覀冞€沒有建立和管理器相關(guān)聯(lián)的 BaseActivity 類。現(xiàn)在我們在 base 包內(nèi)再新建一個(gè) BaseActivity.java,封裝活動間的跳轉(zhuǎn)方法。其中 jumpTo() 方法表示跳轉(zhuǎn)后活動堆棧中只保留跳轉(zhuǎn)后的那一個(gè)活動,壓在堆棧中的其他活動全部銷毀;而 open() 方法則保留活動堆棧。另外還有一個(gè) showExitDialog() 的方法,用于詢問用戶是否退出程序,當(dāng)用戶選擇“是”時(shí),將堆棧中的所有活動一次性全部銷毀。

/**
?* BaseActivity: 該抽象類定義所有活動均擁有的共同屬性。
?* 本 APP 中所有活動對象均繼承此類。
?*/
public abstract class BaseActivity extends Activity {
? ? /**
? ? ?* onCreate(): 重寫父類的 onCreate() 方法,向應(yīng)用管理器中添加本活動。
? ? ?*/
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? AppManager.get().addActivity(this);
? ? } // onCreate()
?
? ? /**
? ? ?* onDestroy(): 重寫父類的 onDestroy() 方法,從應(yīng)用管理器中移除本活動。
? ? ?*/
? ? @Override
? ? protected void onDestroy() {
? ? ? ? super.onDestroy();
? ? ? ? AppManager.get().removeActivity(this);
? ? } // onDestroy()
?
? ? /**
? ? ?* jumpTo(): 實(shí)現(xiàn)不傳參的活動間跳轉(zhuǎn)。
? ? ?*
? ? ?* @param dst 要跳轉(zhuǎn)到的活動的類名(xxxActivity.class)。
? ? ?*/
? ? protected void jumpTo(Class dst) {
? ? ? ? Intent intent = new Intent(this, dst);
? ? ? ? startActivity(intent);
? ? ? ? AppManager.get().finishAllExcept(dst);
? ? } // jumpTo()
?
? ? /**
? ? ?* open(): 將當(dāng)前活動壓入堆棧,打開一個(gè)新活動。
? ? ?*
? ? ?* @param dst 要打開的活動的類名(xxxActivity.class)。
? ? ?*/
? ? protected void open(Class dst) {
? ? ? ? Intent intent = new Intent(this, dst);
? ? ? ? startActivity(intent);
? ? } // open()
?
? ? /**
? ? ?* showExitDialog(): 顯示退出程序?qū)υ捒?,詢問用戶是否退出程序?
? ? ?*/
? ? protected void showExitDialog() {
? ? ? ? AlertDialog dialog;
? ? ? ? DialogInterface.OnClickListener onClick;
?
? ? ? ? onClick = new DialogInterface.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(DialogInterface dialog, int which) {
? ? ? ? ? ? ? ? switch (which) {
? ? ? ? ? ? ? ? ? ? case DialogInterface.BUTTON_POSITIVE:
? ? ? ? ? ? ? ? ? ? ? ? // 確定按鈕
? ? ? ? ? ? ? ? ? ? ? ? dialog.dismiss();
? ? ? ? ? ? ? ? ? ? ? ? AppManager.get().finishAllActivities();
? ? ? ? ? ? ? ? ? ? ? ? break; // case DialogInterface.BUTTON_POSITIVE
?
? ? ? ? ? ? ? ? ? ? case DialogInterface.BUTTON_NEGATIVE:
? ? ? ? ? ? ? ? ? ? ? ? // 取消按鈕
? ? ? ? ? ? ? ? ? ? ? ? dialog.dismiss();
? ? ? ? ? ? ? ? ? ? ? ? break; // case DialogInterface.BUTTON_NEGATIVE
?
? ? ? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? ? ? break; // default
? ? ? ? ? ? ? ? } // switch (which)
? ? ? ? ? ? } // onClick()
? ? ? ? }; // onClick = new DialogInterface.OnClickListener()
?
? ? ? ? // 顯示對話框
? ? ? ? dialog = new AlertDialog.Builder(this)
? ? ? ? ? ? .setMessage(R.string.dlgExitMsg) // "確定要退出嗎?"
? ? ? ? ? ? .setPositiveButton(android.R.string.ok, onClick)
? ? ? ? ? ? .setNegativeButton(android.R.string.cancel, onClick)
? ? ? ? ? ? .create(); // dialog = new AlertDialog.Builder(this)...
? ? ? ? dialog.show();
? ? } // showExitDialog()
} // BaseActivity Abstract Class
?
// E.O.F

上述代碼粘貼完后還是有一處報(bào)錯。這是因?yàn)檫@段代碼中用到了一處尚未定義的字符串資源。我們打開 res/values 目錄下的 strings.xml 文件,添加字符串資源:

<?xml version="1.0" encoding="utf-8"?>
<resources>
? ? <!-- 這個(gè)字符串是你的工程名稱,可以自己取,不一定要是 My Application -->
? ? <string name="app_name">My Application</string>
?
? ? <!-- 這個(gè)是我們新添加的字符串 -->
? ? <string name="dlgExitMsg">確定要退出嗎?</string>
?
? ? <!-- 順便把之后要用到的字符串也一并準(zhǔn)備了 -->
? ? <string name="btnNavHome">主頁</string>
? ? <string name="btnNavMessage">消息</string>
? ? <string name="btnNavSettings">設(shè)置</string>
</resources>

至此,報(bào)錯全部消除。

現(xiàn)在我們建立底部導(dǎo)航欄的三個(gè)按鈕對應(yīng)的三個(gè) Activity 頁面。

準(zhǔn)備導(dǎo)航欄的圖標(biāo)資源,放入 res/drawable 文件夾內(nèi):

打開 res/values/colors.xml 文件,定義導(dǎo)航欄相關(guān)顏色(背景、選中顏色、非選中顏色)

<?xml version="1.0" encoding="utf-8"?>
<resources>
? ? <!-- 定義導(dǎo)航欄的相關(guān)顏色 -->
? ? <color name="navBack">#e0e0e0</color> <!-- 導(dǎo)航欄背景色 Grey 300-->
? ? <color name="navNormal">#000000</color> <!-- 未激活項(xiàng)目的文字顏色 Black -->
? ? <color name="navActivated">#039be5</color> <!-- 已激活項(xiàng)目的文字顏色 Light Blue 600 -->
</resources>

新建三個(gè)活動 HomeActivity、MessageActivity 和 SettingsActivity。

MainActivity 的布局文件 activity_main.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? android:background="@android:color/holo_blue_dark"
? ? tools:context=".HomeActivity">
?
? ? <LinearLayout
? ? ? ? android:id="@+id/llHomePage"
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="0dp"
? ? ? ? android:layout_marginStart="8dp"
? ? ? ? android:layout_marginLeft="8dp"
? ? ? ? android:layout_marginTop="8dp"
? ? ? ? android:layout_marginEnd="8dp"
? ? ? ? android:layout_marginRight="8dp"
? ? ? ? android:layout_marginBottom="8dp"
? ? ? ? android:orientation="vertical"
? ? ? ? app:layout_constraintBottom_toTopOf="@+id/llHomeNav"
? ? ? ? app:layout_constraintEnd_toEndOf="parent"
? ? ? ? app:layout_constraintStart_toStartOf="parent"
? ? ? ? app:layout_constraintTop_toTopOf="parent">
? ? ? ? <!--
? ? ? ? 該 LinearLayout 為主體頁面布局的容器,你也可以根據(jù)需要換成其他形式的 Layout
? ? ? ? 以上代碼將頁面布局容器和底部的導(dǎo)航欄進(jìn)行了約束
? ? ? ? 即該容器的底端和導(dǎo)航欄的頂端彼此約束
? ? ? ? 確保該容器的占用空間不會覆蓋導(dǎo)航欄
? ? ? ? 頁面的主體布局請?jiān)谠撊萜鲀?nèi)部(即此處)創(chuàng)建
? ? ? ? -->
? ? </LinearLayout>
?
? ? <!-- 以下為導(dǎo)航欄的布局 -->
? ? <LinearLayout
? ? ? ? android:id="@+id/llHomeNav"
? ? ? ? style="?android:attr/buttonBarStyle"
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:background="@color/navBack"
? ? ? ? android:orientation="horizontal"
? ? ? ? app:layout_constraintBottom_toBottomOf="parent"
? ? ? ? app:layout_constraintEnd_toEndOf="parent"
? ? ? ? app:layout_constraintStart_toStartOf="parent"
? ? ? ? app:layout_constraintTop_toBottomOf="@+id/llHomePage">
?
? ? ? ? <!-- 【主頁】活動中,【主頁】按鈕設(shè)為已激活樣式,注意 drawableTop 和 textColor 屬性的值 -->
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavHome"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/home1"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavHome"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navActivated" />
?
? ? ? ? <!-- 其他按鈕設(shè)為未激活樣式,注意 drawableTop 和 textColor 屬性的值 -->
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavMessage"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/message0"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavMessage"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navNormal" />
?
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavSettings"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/settings0"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavSettings"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navNormal" />
? ? </LinearLayout>
</android.support.constraint.ConstraintLayout>

切換到預(yù)覽頁面看一下,已經(jīng)有了底部導(dǎo)航欄的雛形:

現(xiàn)在打開代碼文件 HomeActivity.java 編寫點(diǎn)擊導(dǎo)航欄按鈕時(shí)的活動跳轉(zhuǎn)代碼:

public class HomeActivity extends BaseActivity { // 請注意此處繼承的是 BaseActivity 而不是 Activity
? ? /**
? ? ?* onCreate(): 活動創(chuàng)建時(shí)觸發(fā)。
? ? ?*/
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_home);
? ? } // onCreate()
?
? ? /**
? ? ?* onNavButtonsTapped(): 點(diǎn)擊導(dǎo)航欄上的標(biāo)簽時(shí)觸發(fā)。
? ? ?*
? ? ?* @param v 點(diǎn)擊的按鈕對象,用 v.getId() 獲取其資源 ID。
? ? ?*/
? ? public void onNavButtonsTapped(View v) {
? ? ? ? switch (v.getId()) {
? ? ? ? ? ? case R.id.btnNavMessage:
? ? ? ? ? ? ? ? open(MessageActivity.class);
? ? ? ? ? ? ? ? break; // case R.id.btnNavMessage
?
? ? ? ? ? ? case R.id.btnNavSettings:
? ? ? ? ? ? ? ? open(SettingsActivity.class);
? ? ? ? ? ? ? ? break; // case R.id.btnNavSettings
? ? ? ? } // switch (v.getId())
? ? } // onNavButtonsTapped()
?
? ? /**
? ? ?* onKeyDown(): 按下回退鍵時(shí)觸發(fā)。
? ? ?* 彈出對話框詢問是否退出程序。
? ? ?*/
? ? @Override
? ? public boolean onKeyDown(int keyCode, KeyEvent event) {
? ? ? ? if (keyCode == KeyEvent.KEYCODE_BACK) {
? ? ? ? ? ? showExitDialog();
? ? ? ? ? ? return true;
? ? ? ? } // if (keyCode == KeyEvent.KEYCODE_BACK)
? ? ? ? else {
? ? ? ? ? ? return super.onKeyDown(keyCode, event);
? ? ? ? } // else
? ? } // onKeyDown()
} // HomeActivity Class
?
// E.O.F

另外兩個(gè)活動直接照葫蘆畫瓢就 OK 了。不過這里千萬要注意后兩個(gè)活動不能只復(fù)制 MainActivity 的布局就完事了,一定要更改導(dǎo)航欄各個(gè)按鈕的樣式!另外就是活動中的 onNavButtonsTapped() 方法的內(nèi)容也不一樣!

activity_message.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? android:background="@android:color/holo_green_dark"
? ? tools:context=".MessageActivity"> <!-- 本活動的背景色為 holo green dark -->
? ? <!-- 注意上方的 Context -->
?
? ? <LinearLayout
? ? ? ? android:id="@+id/llMessagePage"
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="0dp"
? ? ? ? android:layout_marginStart="8dp"
? ? ? ? android:layout_marginLeft="8dp"
? ? ? ? android:layout_marginTop="8dp"
? ? ? ? android:layout_marginEnd="8dp"
? ? ? ? android:layout_marginRight="8dp"
? ? ? ? android:layout_marginBottom="8dp"
? ? ? ? android:orientation="vertical"
? ? ? ? app:layout_constraintBottom_toTopOf="@+id/llMessageNav"
? ? ? ? app:layout_constraintEnd_toEndOf="parent"
? ? ? ? app:layout_constraintStart_toStartOf="parent"
? ? ? ? app:layout_constraintTop_toTopOf="parent">
? ? ? ? <!-- 在此定義頁面主體布局 -->
? ? </LinearLayout>
?
? ? <LinearLayout
? ? ? ? android:id="@+id/llMessageNav"
? ? ? ? style="?android:attr/buttonBarStyle"
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:background="@color/navBack"
? ? ? ? android:orientation="horizontal"
? ? ? ? app:layout_constraintBottom_toBottomOf="parent"
? ? ? ? app:layout_constraintEnd_toEndOf="parent"
? ? ? ? app:layout_constraintStart_toStartOf="parent"
? ? ? ? app:layout_constraintTop_toBottomOf="@+id/llMessagePage">
?
? ? ? ? <!-- 請務(wù)必注意以下各按鈕的 drawableTop 和 textColor 屬性 -->
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavHome"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/home0"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavHome"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navNormal" />
?
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavMessage"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/message1"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavMessage"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navActivated" />
?
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavSettings"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/settings0"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavSettings"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navNormal" />
? ? </LinearLayout>
</android.support.constraint.ConstraintLayout>

MessageActivity.java

public class MessageActivity extends BaseActivity {
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_message);
? ? } // onCreate()
?
? ? // 請注意本方法內(nèi)容的變化
? ? public void onNavButtonsTapped(View v) {
? ? ? ? switch (v.getId()) {
? ? ? ? ? ? case R.id.btnNavHome:
? ? ? ? ? ? ? ? open(HomeActivity.class);
? ? ? ? ? ? ? ? break; // case R.id.btnNavHome
?
? ? ? ? ? ? case R.id.btnNavSettings:
? ? ? ? ? ? ? ? open(SettingsActivity.class);
? ? ? ? ? ? ? ? break; // case R.id.btnNavSettings
? ? ? ? } // switch (v.getId())
? ? } // onNavButtonsTapped()
?
? ? @Override
? ? public boolean onKeyDown(int keyCode, KeyEvent event) {
? ? ? ? if (keyCode == KeyEvent.KEYCODE_BACK) {
? ? ? ? ? ? showExitDialog();
? ? ? ? ? ? return true;
? ? ? ? } // if (keyCode == KeyEvent.KEYCODE_BACK)
? ? ? ? else {
? ? ? ? ? ? return super.onKeyDown(keyCode, event);
? ? ? ? } // else
? ? } // onKeyDown()
} // MessageActivity Class
?
// E.O.F

最后是 SettingsActivity。

activity_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? xmlns:tools="http://schemas.android.com/tools"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent"
? ? android:background="@android:color/holo_orange_dark"
? ? tools:context=".SettingsActivity"> <!-- 本活動的背景色為 holo orange dark -->
? ? <!-- 注意上方的 Context -->
?
? ? <LinearLayout
? ? ? ? android:id="@+id/llHomePage"
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="0dp"
? ? ? ? android:layout_marginStart="8dp"
? ? ? ? android:layout_marginLeft="8dp"
? ? ? ? android:layout_marginTop="8dp"
? ? ? ? android:layout_marginEnd="8dp"
? ? ? ? android:layout_marginRight="8dp"
? ? ? ? android:layout_marginBottom="8dp"
? ? ? ? android:orientation="vertical"
? ? ? ? app:layout_constraintBottom_toTopOf="@+id/llHomeNav"
? ? ? ? app:layout_constraintEnd_toEndOf="parent"
? ? ? ? app:layout_constraintStart_toStartOf="parent"
? ? ? ? app:layout_constraintTop_toTopOf="parent">
? ? ? ? <!-- 在此定義頁面主體布局 -->
? ? </LinearLayout>
?
? ? <!-- 以下為導(dǎo)航欄的布局 -->
? ? <LinearLayout
? ? ? ? android:id="@+id/llHomeNav"
? ? ? ? style="?android:attr/buttonBarStyle"
? ? ? ? android:layout_width="0dp"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:background="@color/navBack"
? ? ? ? android:orientation="horizontal"
? ? ? ? app:layout_constraintBottom_toBottomOf="parent"
? ? ? ? app:layout_constraintEnd_toEndOf="parent"
? ? ? ? app:layout_constraintStart_toStartOf="parent"
? ? ? ? app:layout_constraintTop_toBottomOf="@+id/llHomePage">
?
? ? ? ? <!-- 請務(wù)必注意以下各按鈕的 drawableTop 和 textColor 屬性 -->
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavHome"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/home0"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavHome"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navNormal" />
?
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavMessage"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/message0"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavMessage"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navNormal" />
?
? ? ? ? <Button
? ? ? ? ? ? android:id="@+id/btnNavSettings"
? ? ? ? ? ? style="?android:attr/buttonBarButtonStyle"
? ? ? ? ? ? android:layout_width="0dp"
? ? ? ? ? ? android:layout_height="wrap_content"
? ? ? ? ? ? android:layout_weight="1"
? ? ? ? ? ? android:drawableTop="@drawable/settings1"
? ? ? ? ? ? android:onClick="onNavButtonsTapped"
? ? ? ? ? ? android:text="@string/btnNavSettings"
? ? ? ? ? ? android:textAppearance="?android:attr/textAppearanceSmall"
? ? ? ? ? ? android:textColor="@color/navActivated" />
? ? </LinearLayout>
</android.support.constraint.ConstraintLayout>

SettingsActivity.java:

package com.example.myapplication;
?
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
?
import com.example.myapplication.base.BaseActivity;
?
public class SettingsActivity extends BaseActivity {
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_settings);
? ? } // onCreate()
?
? ? // 請注意本方法內(nèi)容的變化
? ? public void onNavButtonsTapped(View v) {
? ? ? ? switch (v.getId()) {
? ? ? ? ? ? case R.id.btnNavHome:
? ? ? ? ? ? ? ? open(HomeActivity.class);
? ? ? ? ? ? ? ? break; // case R.id.btnNavHome
?
? ? ? ? ? ? case R.id.btnNavMessage:
? ? ? ? ? ? ? ? open(MessageActivity.class);
? ? ? ? ? ? ? ? break; // case R.id.btnNavMessage
? ? ? ? } // switch (v.getId())
? ? } // onNavButtonsTapped()
?
? ? @Override
? ? public boolean onKeyDown(int keyCode, KeyEvent event) {
? ? ? ? if (keyCode == KeyEvent.KEYCODE_BACK) {
? ? ? ? ? ? showExitDialog();
? ? ? ? ? ? return true;
? ? ? ? } // if (keyCode == KeyEvent.KEYCODE_BACK)
? ? ? ? else {
? ? ? ? ? ? return super.onKeyDown(keyCode, event);
? ? ? ? } // else
? ? } // onKeyDown()
} // SettingsActivity Class
?
// E.O.F

做到這一步,先中場休息,打開模擬器調(diào)試一下。

我們之后還有兩個(gè)問題需要解決:

①活動間的跳轉(zhuǎn)是有動畫的,而我們并不需要這畫蛇添足的動畫;

②這一點(diǎn)也是更重要的。每次一點(diǎn)擊導(dǎo)航欄上的按鈕,就會打開一個(gè)新活動。當(dāng)我們從主頁活動跳至設(shè)置活動,然后再由設(shè)置活動跳回主頁活動時(shí),系統(tǒng)的堆棧里其實(shí)是有兩個(gè)主頁活動的實(shí)例的,如果反復(fù)跳轉(zhuǎn),系統(tǒng)也會一直繼續(xù)創(chuàng)建活動實(shí)例,原先的活動實(shí)例名存實(shí)亡,造成實(shí)質(zhì)上的內(nèi)存泄漏,直到最后內(nèi)存不夠用而崩潰。我們需要的是這樣的效果:跳回曾經(jīng)已經(jīng)創(chuàng)建過的活動時(shí),不要新建實(shí)例,而是直接重新引用原先活動的實(shí)例。這樣,不論在導(dǎo)航欄上跳轉(zhuǎn)多少次,內(nèi)存中最多只會有 3 個(gè)活動,永遠(yuǎn)不會有內(nèi)存泄漏的問題。

我們先來解決第一個(gè)問題。這個(gè)問題其實(shí)很好辦,在 res/values 目錄下有個(gè) styles.xml 的文件,用于定義活動的主題樣式。我們在其中增加幾行代碼用于關(guān)閉活動間的跳轉(zhuǎn)動畫:

<resources>
?
? ? <!-- Base application theme. -->
? ? <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
? ? ? ? <!-- Customize your theme here. -->
? ? ? ? <!-- 關(guān)閉動畫 -->
? ? ? ? <item name="android:windowIsTranslucent">true</item>
? ? ? ? <item name="android:windowAnimationStyle">@style/NoAnimation</item>
? ? </style>
?
? ? <!-- 定義無動畫樣式 -->
? ? <style name="NoAnimation">
? ? ? ? <item name="android:activityCloseEnterAnimation">@null</item>
? ? ? ? <item name="android:activityCloseExitAnimation">@null</item>
? ? ? ? <item name="android:activityOpenEnterAnimation">@null</item>
? ? ? ? <item name="android:activityOpenExitAnimation">@null</item>
? ? ? ? <item name="android:taskCloseEnterAnimation">@null</item>
? ? ? ? <item name="android:taskCloseExitAnimation">@null</item>
? ? ? ? <item name="android:taskOpenEnterAnimation">@null</item>
? ? ? ? <item name="android:taskOpenExitAnimation">@null</item>
? ? ? ? <item name="android:taskToBackEnterAnimation">@null</item>
? ? ? ? <item name="android:taskToBackExitAnimation">@null</item>
? ? ? ? <item name="android:taskToFrontEnterAnimation">@null</item>
? ? ? ? <item name="android:taskToFrontExitAnimation">@null</item>
? ? </style>
</resources>

接下來是第二個(gè)問題,保證每個(gè)活動只有唯一的實(shí)例,避免跳轉(zhuǎn)過程中活動實(shí)例反復(fù)創(chuàng)建造成的內(nèi)存泄漏。

Android 中有一種活動啟動方式叫 singleInstance,它表示整個(gè) Application 周期里對應(yīng)的活動實(shí)例不能超過一個(gè),當(dāng)該活動已創(chuàng)建但之后又從其他的 intent 跳轉(zhuǎn)而來時(shí),不新建實(shí)例,而是引用已有的實(shí)例。另外,singleInstance 啟動模式的活動各自擁有各自的活動堆棧,互不影響。我們打開 AndroidManifest,添加如下代碼:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
? ? package="com.example.myapplication">
?
? ? <application
? ? ? ? android:allowBackup="true"
? ? ? ? android:icon="@mipmap/ic_launcher"
? ? ? ? android:label="@string/app_name"
? ? ? ? android:roundIcon="@mipmap/ic_launcher_round"
? ? ? ? android:supportsRtl="true"
? ? ? ? android:theme="@style/AppTheme">
? ? ? ? <activity
? ? ? ? ? ? android:name=".HomeActivity"
? ? ? ? ? ? android:launchMode="singleInstance"> <!-- 設(shè)置活動的啟動方式為 singleInstance -->
? ? ? ? ? ? <intent-filter>
? ? ? ? ? ? ? ? <action android:name="android.intent.action.MAIN" />
?
? ? ? ? ? ? ? ? <category android:name="android.intent.category.LAUNCHER" />
? ? ? ? ? ? </intent-filter>
? ? ? ? </activity>
? ? ? ? <activity
? ? ? ? ? ? android:name=".MessageActivity"
? ? ? ? ? ? android:launchMode="singleInstance" /> <!-- 如法炮制 -->
? ? ? ? <activity
? ? ? ? ? ? android:name=".SettingsActivity"
? ? ? ? ? ? android:launchMode="singleInstance" /> <!-- 如法炮制 -->
? ? </application>
?
</manifest>

再次打開模擬器進(jìn)行調(diào)試:

效果 PERFECT。

至此,我們就實(shí)現(xiàn)了無 Fragment 的底部導(dǎo)航欄。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論