欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

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

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

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

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

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

/**
?* BaseActivity: 該抽象類定義所有活動(dòng)均擁有的共同屬性。
?* 本 APP 中所有活動(dòng)對(duì)象均繼承此類。
?*/
public abstract class BaseActivity extends Activity {
? ? /**
? ? ?* onCreate(): 重寫(xiě)父類的 onCreate() 方法,向應(yīng)用管理器中添加本活動(dòng)。
? ? ?*/
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? AppManager.get().addActivity(this);
? ? } // onCreate()
?
? ? /**
? ? ?* onDestroy(): 重寫(xiě)父類的 onDestroy() 方法,從應(yīng)用管理器中移除本活動(dòng)。
? ? ?*/
? ? @Override
? ? protected void onDestroy() {
? ? ? ? super.onDestroy();
? ? ? ? AppManager.get().removeActivity(this);
? ? } // onDestroy()
?
? ? /**
? ? ?* jumpTo(): 實(shí)現(xiàn)不傳參的活動(dòng)間跳轉(zhuǎn)。
? ? ?*
? ? ?* @param dst 要跳轉(zhuǎn)到的活動(dòng)的類名(xxxActivity.class)。
? ? ?*/
? ? protected void jumpTo(Class dst) {
? ? ? ? Intent intent = new Intent(this, dst);
? ? ? ? startActivity(intent);
? ? ? ? AppManager.get().finishAllExcept(dst);
? ? } // jumpTo()
?
? ? /**
? ? ?* open(): 將當(dāng)前活動(dòng)壓入堆棧,打開(kāi)一個(gè)新活動(dòng)。
? ? ?*
? ? ?* @param dst 要打開(kāi)的活動(dòng)的類名(xxxActivity.class)。
? ? ?*/
? ? protected void open(Class dst) {
? ? ? ? Intent intent = new Intent(this, dst);
? ? ? ? startActivity(intent);
? ? } // open()
?
? ? /**
? ? ?* showExitDialog(): 顯示退出程序?qū)υ捒?,詢?wèn)用戶是否退出程序。
? ? ?*/
? ? 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()
?
? ? ? ? // 顯示對(duì)話框
? ? ? ? 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)錯(cuò)。這是因?yàn)檫@段代碼中用到了一處尚未定義的字符串資源。我們打開(kāi) 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">主頁(yè)</string>
? ? <string name="btnNavMessage">消息</string>
? ? <string name="btnNavSettings">設(shè)置</string>
</resources>

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

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

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

打開(kā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è)活動(dòng) 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 為主體頁(yè)面布局的容器,你也可以根據(jù)需要換成其他形式的 Layout
? ? ? ? 以上代碼將頁(yè)面布局容器和底部的導(dǎo)航欄進(jìn)行了約束
? ? ? ? 即該容器的底端和導(dǎo)航欄的頂端彼此約束
? ? ? ? 確保該容器的占用空間不會(huì)覆蓋導(dǎo)航欄
? ? ? ? 頁(yè)面的主體布局請(qǐng)?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">
?
? ? ? ? <!-- 【主頁(yè)】活動(dòng)中,【主頁(yè)】按鈕設(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ù)覽頁(yè)面看一下,已經(jīng)有了底部導(dǎo)航欄的雛形:

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

public class HomeActivity extends BaseActivity { // 請(qǐng)注意此處繼承的是 BaseActivity 而不是 Activity
? ? /**
? ? ?* onCreate(): 活動(dòng)創(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)擊的按鈕對(duì)象,用 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ā)。
? ? ?* 彈出對(duì)話框詢問(wèn)是否退出程序。
? ? ?*/
? ? @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è)活動(dòng)直接照葫蘆畫(huà)瓢就 OK 了。不過(guò)這里千萬(wàn)要注意后兩個(gè)活動(dòng)不能只復(fù)制 MainActivity 的布局就完事了,一定要更改導(dǎo)航欄各個(gè)按鈕的樣式!另外就是活動(dòng)中的 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"> <!-- 本活動(dòng)的背景色為 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">
? ? ? ? <!-- 在此定義頁(yè)面主體布局 -->
? ? </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">
?
? ? ? ? <!-- 請(qǐng)務(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()
?
? ? // 請(qǐng)注意本方法內(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"> <!-- 本活動(dòng)的背景色為 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">
? ? ? ? <!-- 在此定義頁(yè)面主體布局 -->
? ? </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">
?
? ? ? ? <!-- 請(qǐng)務(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()
?
? ? // 請(qǐng)注意本方法內(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

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

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

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

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

我們先來(lái)解決第一個(gè)問(wèn)題。這個(gè)問(wèn)題其實(shí)很好辦,在 res/values 目錄下有個(gè) styles.xml 的文件,用于定義活動(dòng)的主題樣式。我們?cè)谄渲性黾訋仔写a用于關(guān)閉活動(dòng)間的跳轉(zhuǎn)動(dòng)畫(huà):

<resources>
?
? ? <!-- Base application theme. -->
? ? <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
? ? ? ? <!-- Customize your theme here. -->
? ? ? ? <!-- 關(guān)閉動(dòng)畫(huà) -->
? ? ? ? <item name="android:windowIsTranslucent">true</item>
? ? ? ? <item name="android:windowAnimationStyle">@style/NoAnimation</item>
? ? </style>
?
? ? <!-- 定義無(wú)動(dòng)畫(huà)樣式 -->
? ? <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>

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

Android 中有一種活動(dòng)啟動(dòng)方式叫 singleInstance,它表示整個(gè) Application 周期里對(duì)應(yīng)的活動(dòng)實(shí)例不能超過(guò)一個(gè),當(dāng)該活動(dòng)已創(chuàng)建但之后又從其他的 intent 跳轉(zhuǎn)而來(lái)時(shí),不新建實(shí)例,而是引用已有的實(shí)例。另外,singleInstance 啟動(dòng)模式的活動(dòng)各自擁有各自的活動(dòng)堆棧,互不影響。我們打開(kāi) 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è)置活動(dòng)的啟動(dòng)方式為 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>

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

效果 PERFECT。

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

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

相關(guān)文章

最新評(píng)論