Android開發——初步學習Activity:什么是Activity
? 在 Android 中,Activity 是一個用于展示用戶界面的組件。每個 Activity 通常對應應用中的一個屏幕,例如主界面、設置界面或詳情頁。Activity 負責處理用戶的輸入事件,更新 UI,并與其他組件(如服務、廣播接收器)進行交互。如果接觸過Qt開發的同志,一點不會難以想到Activity就有點像咱們的QMainWindow一樣,作為UI交互大的容器。
🛠?創建一個Activity
? 咱們先不deep dive到Activity的基本原理,我們就先創建一個玩玩。一般而言,咱們作為快速開發者,不會自己鹿代碼和在XML中自己手動改,容易出錯。
基本步驟(一次性的)
- 打開項目:啟動 Android Studio,打開您的項目。
- 右鍵點擊包名:在項目視圖中,導航到
app > src > main > java > com.example.yourapp
,右鍵點擊您的包名(例如com.example.myapp
)。 - 選擇創建 Activity:選擇
New > Activity
,然后選擇您想要的 Activity 類型,例如Empty Activity
。 - 配置 Activity:
- Activity Name:輸入 Activity 的名稱(如
SecondActivity
)。 - Layout Name:輸入布局文件的名稱(默認為
activity_second
)。 - Title:設置該 Activity 的標題。
- Launcher Activity:如果您希望此 Activity 為啟動 Activity,請勾選此選項。
- Source Language:在今天,你只能使用Kotlin了,Java是不被支持的。
- Activity Name:輸入 Activity 的名稱(如
- 完成創建:點擊
Finish
,Android Studio 會自動生成相應的 Activity 類、布局文件,并在AndroidManifest.xml
中注冊該 Activity。
手動創建 Activity(分步進行)
如果您希望更靈活地控制創建過程,可以手動創建 Activity。
-
創建 Activity 類:
- 在項目視圖中,導航到
app > src > main > java > com.example.yourapp
。 - 右鍵點擊您的包名,選擇
New > Java Class
或New > Kotlin Class/File
。 - 輸入類名(如
SecondActivity
),并使其繼承AppCompatActivity
。 - 實現
onCreate()
方法,并通過setContentView()
設置布局文件。
public class SecondActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);} }
- 在項目視圖中,導航到
-
創建布局文件:
- 在
res > layout
目錄下,右鍵點擊,選擇New > Layout Resource File
。 - 輸入文件名(如
activity_second.xml
),并設計您的布局。
- 在
-
注冊 Activity:
- 打開
AndroidManifest.xml
文件。 - 在
<application>
標簽內,添加以下代碼以注冊新創建的 Activity:
<activity android:name=".SecondActivity" />
- 打開
在 AndroidManifest.xml 中注冊 Activity(一般你不用做這個,Android Studio幫你做了)
? AndroidManifest.xml
是咱們的安卓APP的一個總導覽,我們的Activities必須在這里被表達,所以,無論是使用向導還是手動創建 Activity,都需要在 AndroidManifest.xml
中注冊該 Activity。通常,Android Studio 會自動為您完成此操作,但如果沒有,您可以手動添加:
<activity android:name=".SecondActivity" />
如果您希望此 Activity 為啟動 Activity(即應用啟動時首先顯示的界面),需要在 <activity>
標簽中添加 intent-filter
:
<activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
</activity>
準備完成我們的業務——Layout文件
? Activities的默認代碼中,就幫助我們生成好了OnCreate方法,一般而言,我們的默認代碼包括但不限于:回調父類的onCreate創建方法,下一步,我們往往是要加載我們的ContentView,讓我們的APP的布局文件生效。
? 在Qt中,咱們的Layout是Ui_xxx.ui搞定的,它本身是一個XML文件,聲明式的說明我們的布局如何。安卓自身也是這樣做的。我們會在Layout文件夾下創建layout XML文件,在代碼中采用資源加載的方式聯合起來搞定我們的布局加載動作。
什么是 Android 布局文件?
Android 布局文件是以 XML 格式編寫的文件,位于項目的 res/layout/
目錄下。它們描述了應用界面的結構和外觀,包括視圖組件(如按鈕、文本框、圖片等)及其排列方式。通過布局文件,開發者可以在不編寫大量代碼的情況下,快速構建出界面。
🧩 布局文件的基本結構
一個典型的布局文件通常包含以下幾個部分:
-
XML 聲明和命名空間
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent">
xmlns:android
:聲明 XML 命名空間,Android 系統通過它識別屬性。android:layout_width
和android:layout_height
:定義布局的寬度和高度,常用值有match_parent
、wrap_content
和具體的 dp 值。
-
視圖組件(Views)
在布局文件中,視圖組件是構建 UI 的基本單元,如:
<TextView android:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello, World!" />
android:id
:為視圖指定唯一標識符,供代碼中引用。android:text
:設置文本內容。
-
布局容器(ViewGroup)
布局容器是用于組織和排列子視圖的容器類,如:
LinearLayout
:線性布局,按水平或垂直方向排列子視圖。RelativeLayout
:相對布局,子視圖相對于父視圖或其他兄弟視圖定位。ConstraintLayout
:約束布局,使用約束關系定位子視圖,推薦用于復雜布局。FrameLayout
:幀布局,通常用于顯示單一視圖或堆疊多個視圖。
示例:
<LinearLayout android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><!-- 子視圖 --> </LinearLayout>
🛠? 如何在 Android Studio 中創建布局文件
在 Android Studio 中創建布局文件的步驟如下:
- 使用向導創建布局文件
- 在項目視圖中,右鍵點擊
res/layout
目錄,選擇New > Layout Resource File
。 - 輸入文件名(如
activity_main.xml
),選擇根布局類型(如LinearLayout
)。 - 點擊
OK
,Android Studio 會自動生成布局文件。
- 在項目視圖中,右鍵點擊
- 手動編寫布局文件
- 在
res/layout
目錄下創建新的 XML 文件。 - 使用上述結構和組件,手動編寫布局內容。
- 在
- 使用設計視圖
- 打開布局文件,切換到
Design
視圖。 - 使用拖拽方式添加和配置視圖組件,Android Studio 會自動生成相應的 XML 代碼。
- 打開布局文件,切換到
💡 常用布局類型及其適用場景
布局類型 | 描述 | 適用場景 |
---|---|---|
LinearLayout | 子視圖按線性方向排列(水平或垂直) | 簡單的列表、表單布局 |
RelativeLayout | 子視圖相對于父視圖或其他兄弟視圖定位 | 復雜的布局,子視圖之間有相對關系時 |
ConstraintLayout | 使用約束關系定位子視圖,性能優于嵌套布局 | 推薦用于復雜布局,減少嵌套層級,提高性能 |
FrameLayout | 子視圖堆疊在一起,通常用于顯示單一視圖或覆蓋層 | 顯示單一內容、圖片查看器、視頻播放器等 |
TableLayout | 子視圖按行和列排列,適用于表格布局 | 表格數據展示、日歷布局 |
GridLayout | 子視圖按網格排列,支持跨行跨列 | 圖像畫廊、日程安排等 |
📐 布局屬性詳解
android:layout_width
和android:layout_height
match_parent
:視圖的大小填充父容器。wrap_content
:視圖的大小適應其內容。- 固定值(如
200dp
):指定具體的尺寸。
android:orientation
horizontal
:子視圖水平排列。vertical
:子視圖垂直排列。
android:padding
和android:margin
padding
:視圖內容與邊界之間的內邊距。margin
:視圖與其他視圖之間的外邊距。
android:gravity
和android:layout_gravity
gravity
:控制視圖內部內容的對齊方式。layout_gravity
:控制視圖在父容器中的對齊方式。
android:layout_weight
- 在
LinearLayout
中,指定視圖占用剩余空間的比例。
- 在
📱 響應式布局與多屏適配
為了適應不同設備和屏幕尺寸,Android 提供了多種方式進行布局適配:
-
布局資源目錄分組
res/layout/
:默認布局。res/layout-large/
:大屏設備布局。res/layout-land/
:橫屏布局。res/layout-sw600dp/
:屏幕寬度大于等于 600dp 的設備布局。
Android Studio 支持根據設備配置自動選擇合適的布局資源。
-
布局變體(Layout Variants)
- 在 Android Studio 中,右鍵點擊布局文件,選擇
Create Layout Variant
。 - 為特定設備配置創建布局變體,如平板、折疊屏等。
- 在 Android Studio 中,右鍵點擊布局文件,選擇
-
使用
ConstraintLayout
- 通過約束關系定位視圖,減少嵌套層級,提高布局性能。
- 支持鏈式布局、偏移量、比例等復雜布局需求。
在布局文件中編寫自己的控件排布
? 咱們說了,布局使用XML編寫,我們其中的控件亦是如此。
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompatxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.appcompat.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="?attr/colorPrimary"/><Buttonandroid:id="@+id/button1"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/Button1Text"tools:ignore="MissingConstraints" /><Buttonandroid:id="@+id/button2"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/Button2Text"tools:ignore="MissingConstraints" /><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/button_depatch_activity"android:text="@string/button_start_activity_text"/>
</androidx.appcompat.widget.LinearLayoutCompat>
1. android:id
該屬性為視圖組件指定一個唯一的標識符。在 Java 或 Kotlin 代碼中,可以通過該 ID 引用該按鈕,以便設置點擊事件監聽器或修改其屬性。
例如:
val button = findViewById<Button>(R.id.button_depatch_activity)
button.setOnClickListener {// 處理按鈕點擊事件
}
2. android:layout_width
和 android:layout_height
這兩個屬性定義了視圖組件的寬度和高度。
match_parent
:視圖將擴展以填充其父容器的剩余空間。wrap_content
:視圖將根據其內容的大小來調整自身的尺寸。
在上述示例中,layout_width
設置為 match_parent
,表示按鈕的寬度將與其父容器的寬度相同;layout_height
設置為 wrap_content
,表示按鈕的高度將根據其內容的高度來調整。
3. android:text
該屬性設置按鈕上顯示的文本內容。在上述示例中,@string/button_start_activity_text
是一個引用字符串資源的方式,表示按鈕上顯示的文本內容來自于 res/values/strings.xml
文件中的 button_start_activity_text
字符串資源。
使用字符串資源的好處是可以方便地進行國際化和本地化,使應用支持多種語言。
4. 其他常用的按鈕屬性
android:background
:設置按鈕的背景,可以是顏色、圖片或 drawable 資源。android:textColor
:設置按鈕文本的顏色。android:textSize
:設置按鈕文本的大小。android:padding
:設置按鈕內容與邊界之間的內邊距。android:layout_margin
:設置按鈕與其他視圖之間的外邊距。
例如:
<Buttonandroid:id="@+id/button_example"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Click Me"android:background="@drawable/button_background"android:textColor="@color/white"android:textSize="16sp"android:padding="10dp"android:layout_margin="20dp" />
在代碼中使用我們的布局文件下的控件
? 在上面我們看到了layout下的所有控件都有自己的ID。這是我們訪問控件的一個關鍵。筆者學習了兩種辦法搞定拿到控件這個工作
使用 findViewById
方法
findViewById
是 Android 中傳統的控件查找方式。它通過控件的 ID 在視圖層次結構中查找對應的控件。例如:
val textView: TextView = findViewById(R.id.textView)
優點:
- 簡單直接:對于小型項目或簡單的布局,
findViewById
足夠使用。
缺點:
- 類型轉換問題:需要手動進行類型轉換,容易出錯。
- 空指針異常:如果控件未在布局中定義,可能導致空指針異常。
- 性能問題:每次調用都會遍歷視圖層次結構,可能影響性能。
使用 ViewBinding 方法
ViewBinding 是 Jetpack 提供的一種類型安全、空安全的控件訪問方式。它在編譯時生成綁定類,直接引用控件,避免了類型轉換和空指針異常的問題。例如:
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val textView: TextView = binding.textView
優點:
- 類型安全:生成的綁定類直接引用控件,避免了類型轉換問題。
- 空安全:如果控件未在布局中定義,對應的綁定屬性為
null
,避免了空指針異常。 - 性能優化:綁定類在編譯時生成,運行時無需再次查找控件,提升性能。
缺點:
- 需要啟用 ViewBinding:需要在
build.gradle
文件中啟用 ViewBinding。
特性 | findViewById | ViewBinding |
---|---|---|
類型安全 | ? 手動類型轉換 | ? 自動類型匹配 |
空安全 | ? 可能導致空指針異常 | ? 自動處理空值 |
性能 | ? 每次查找控件 | ? 編譯時生成綁定類 |
使用復雜度 | ? 簡單 | ? 需要啟用 ViewBinding |
適用場景 | 小型項目、簡單布局 | 中大型項目、復雜布局 |
啟用 ViewBinding(需要修改gradle文件)
在項目中啟用 ViewBinding 的步驟如下:
-
在
build.gradle
文件中啟用 ViewBinding:android {...viewBinding {enabled = true} }
-
在 Activity 或 Fragment 中使用 ViewBinding:
val binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root)
這樣,您就可以通過
binding.textView
直接訪問控件,而無需使用findViewById
。
Activity使用最簡單的Options Menu的辦法
Options Menu 的典型流程:
- 你在
res/menu/*.xml
定義菜單資源(<menu>
+<item>
)。 - 系統(或 AppCompat 的框架)在需要時創建一個
Menu
對象并調用 Activity 的onCreateOptionsMenu(menu)
—— 在這里你用menuInflater.inflate()
把 XML 轉成Menu
項。 - 菜單顯示前會調用
onPrepareOptionsMenu(menu)
,你可以在這里做按狀態修改(可見性、enable 等)。 - 用戶點擊菜單項時,系統調用
onOptionsItemSelected(item)
,你在這里處理點擊邏輯。 - 當你希望運行時刷新菜單,調用
invalidateOptionsMenu()
(Activity 方法),系統會觸發onPrepareOptionsMenu()
(必要時會重建)。
基本菜單資源(XML)
res/menu/menu_main.xml
(示例)
<menu xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><itemandroid:id="@+id/action_search"android:title="@string/search"android:icon="@drawable/ic_search"app:showAsAction="ifRoom|collapseActionView"app:actionViewClass="androidx.appcompat.widget.SearchView" /><itemandroid:id="@+id/action_settings"android:title="@string/settings"app:showAsAction="never" />
</menu>
app:showAsAction
:控制是否直接顯示在 Toolbar(action)或放入 overflow。務必用app
命名空間(res-auto
)。- 可用屬性:
android:id
、android:title
、android:icon
、子<menu>
、<group>
、android:checkableBehavior
等。
在 Activity 中 inflate 菜單
MenuInflater.inflate(int menuRes, Menu menu)
—— 將 XML 轉為 Menu 項。通常在:
override fun onCreateOptionsMenu(menu: Menu): Boolean {menuInflater.inflate(R.menu.menu_main, menu)// 可在這里初始化 actionView(SearchView 等)return true // 返回 true 表示要顯示菜單
}
注意:返回 true
很關鍵——否則不會顯示菜單。
動態更新菜單
onPrepareOptionsMenu(menu: Menu): Boolean
:每次菜單顯示之前被調用(可以修改menu.findItem(id)
的狀態)。invalidateOptionsMenu()
(Activity 方法):通知系統菜單需要刷新(將觸發onPrepareOptionsMenu()
)。
示例:
override fun onPrepareOptionsMenu(menu: Menu): Boolean {menu.findItem(R.id.action_settings)?.isVisible = isLoggedInreturn super.onPrepareOptionsMenu(menu)
}
處理菜單點擊
override fun onOptionsItemSelected(item: MenuItem): Boolean {return when (item.itemId) {R.id.action_search -> { /* 處理 */ true }R.id.action_settings -> { startActivity(...) ; true }else -> super.onOptionsItemSelected(item)}
}
返回 true
表示你處理了事件;否則交給父類或系統。
訪問/操作 Menu 與 MenuItem(常用 API)
menu.findItem(R.id.some)
→MenuItem
menu.add(int groupId, int itemId, int order, CharSequence title)
→ 動態添加項menu.removeItem(itemId)
→ 刪除項menu.setGroupVisible(groupId, boolean)
→ 整組顯示/隱藏menu.setGroupCheckable(groupId, true, true)
→ 設置單選/多選行為
MenuItem
常用方法:
item.setVisible(boolean)
、item.setEnabled(boolean)
、item.setChecked(boolean)
item.getActionView()
/item.setActionView(view or layoutRes)
—— 獲取或設置當作 action view 的自定義視圖(例如 SearchView)item.expandActionView()
/item.collapseActionView()
—— 展開/收起 action view(如果設置了collapseActionView
)
與 Toolbar/ActionBar 的關系
- 如果你使用
Toolbar
并想讓 it 成為 ActionBar:
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
之后菜單回調 (onCreateOptionsMenu
/ onOptionsItemSelected
) 不變。顯示與布局由 Toolbar
管理(它會給 Menu
渲染視圖)。
SearchView / actionView 的常見用法
在 onCreateOptionsMenu()
初始化:
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as? SearchView
searchView?.setOnQueryTextListener(object: SearchView.OnQueryTextListener {override fun onQueryTextSubmit(query: String): Boolean { doSearch(query); return true }override fun onQueryTextChange(newText: String): Boolean = false
})