2026-04-06

【Kotlin】自定義 app 結束對話框

本筆記搭配使用 ViewBinding, AdMob, Edge-to-Edge


app  層級 build.gradle
 
android {
    
    compileSdk {
        version = release(36)
    }
...
...

    buildFeatures {
        viewBinding = true
    }
    
    defaultConfig {
        ...
        ...
        minSdk = 26
        targetSdk = 36
    }
}

...
...

dependencies {

    ...
    ...
    
    // adMob
    implementation("com.google.android.gms:play-services-ads:25.1.0")
}
 


strings.xml
 
<resources>
    
    <string name="dialog_title">確定要離開?</string>
    <string name="dialog_message">您確定要關閉應用程式嗎?</string>
    <string name="btn_cancel">取消</string>
    <string name="btn_exit">離開</string>
    
    ...
    ...
 
    <!-- adMob app id 測試 -->
    <string name="admob_app_id">ca-app-pub-3940256099942544~3347511713</string>
    <!-- 橫幅 測試 -->
    <string name="banner_ad_unit_id">ca-app-pub-3940256099942544/6300978111</string>
 
</resources>
 


AndroidManifest.xml
 
<manifest
 
    <!-- 存取網路連線 -->
    <uses-permission android:name="android.permission.INTERNET" />
 
    ...
    ...
 
    <application  ... >
 
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.gms.ads.APPLICATION_ID"
            android:value="@string/admob_app_id" />
 
        ...
        ...
 
        <!-- AdMob 的 AdActivity -->
        <activity
            android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
            android:theme="@android:style/Theme.Translucent" />
 
        ...
        ...
 
    </application>
 
</manifest>
 


activity_main.xml
 
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
 


自定義對話框 dialog_exit.xml,因為版面很單純,所以採用 LinearLayout
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="24dp">

    <TextView
        android:id="@+id/tv_dialog_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/dialog_title"
        android:textSize="20sp"
        android:textStyle="bold"
        android:textColor="?attr/colorOnSurface"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:gravity="end"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_cancel"
            style="?attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/btn_cancel" />

        <Button
            android:id="@+id/btn_exit"
            style="?attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:text="@string/btn_exit"
            android:textColor="?attr/colorError"/>

    </LinearLayout>

    <com.google.android.gms.ads.AdView
        android:id="@+id/adView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ads:adSize="MEDIUM_RECTANGLE"
        ads:adUnitId="@string/banner_ad_unit_id" />
</LinearLayout>
 


MainActivity.kt
 
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        // 設定返回鍵攔截(支援新舊所有 Android 版本)
        setupBackPressedListener()
        
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
    }
    
    
    private fun setupBackPressedListener() {
        val callback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // 當使用者按下返回鍵(或手勢返回)時,顯示對話框
                showExitCustomDialog()
            }
        }
        // 將 callback 加到 Dispatcher 中,它會自動處理生命週期
        onBackPressedDispatcher.addCallback(this, callback)
    }


    private fun showExitCustomDialog() {
        // 初始化 Dialog 的 View Binding
        val dialogBinding = DialogExitBinding.inflate(layoutInflater)

        val dialog = MaterialAlertDialogBuilder(this)
            .setView(dialogBinding.root)
            .setCancelable(true)
            .create()

        // AdMob 初始化
        MobileAds.initialize(this)
        dialogBinding.adView.loadAd(AdRequest.Builder().build())

        dialogBinding.btnCancel.setOnClickListener {
            dialog.dismiss()
        }

        dialogBinding.btnExit.setOnClickListener {
            dialog.dismiss()
            finish()
        }

        dialog.show()
    }

}
 

實際的對話框效果


沒有留言:

張貼留言