从此
📄文章 #️⃣专题 🌐酷站 👨‍💻技术 📺 📱

Android Studio 最精简和最佳实践用项目模板

综合/最新

主题
  兼容 - Theme.AppCompat.*;当代 - Theme.Material3.*(已取代 Theme.MaterialComponents.* 即 MDC 早期版本 Material 1、Material 2 / 库名未变);叠加 - ThemeOverlay.* 将部分属性应用于当前主题。
  新兴 - Android 12+ 壁纸取色 - Application onCreate() DynamicColors.applyToActivitiesIfAvailable(this); 并 android:background="?attr/colorPrimary"。

  说明 - Toolbar 已取代 ActionBar,可以放在 Activity 任意位置,应使用 NoActionBar 主题来隐藏默认带的 ActionBar,并 setSupportActionBar(myToolbar);。
    显示返回按钮:
        var ab = getSupportActionBar();
        if (ab != null) {
            ab.setDisplayHomeAsUpEnabled(true);
        }

    约定样式:至少定义下主色 colorPrimary,会应用于 Toolbar 背景色,位置 res/values/themes.xml。
      说明 - colorPrimary(主色)、colorOnPrimary(主色表面之上);colorSecondary(辅助色)、colorOnSecondary(辅助色表面之上);android:statusBarColor(系统状态条);android:textColorPrimary(Setting项名字)
      <resources><style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
        <item name="colorPrimary">#4188F4</item>
        <item name="android:textAllCaps">false</item>
      </style></resources>

  疑惑 - 亲测 Android 14+ 模拟器用了 Theme.Material3.DayNight 主题后,弹不出授权框,还未找到原因。

Android SDK 缺失类补全:
  android{
    // ... other ...
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
        isCoreLibraryDesugaringEnabled = true
    }
    //  Android 15+ 不再支持32位的armeabi-v7a和x86;故首选 arm64-v8a,模拟器、Google Play Games for PC或ChromeOS(x86占三分之一)系统则可选 x86_64"。
    ndk.abiFilters += listOf("arm64-v8a", "x86_64")
  }
  dependencies {
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
  }

最精简项目模板

Android Studio 会自动下载安装版本较低的 Gradle,故不需要 gradlew。

app/src/main/java/com/example/myapplication/MainActivity.java
package com.example.myapplication; import android.os.Bundle; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); // [可选] Android 15+ 启用全面屏,导航条会自动取反色,使其看着明显些。 setContentView(R.layout.activity_main); // [可选] 启用全面屏后,系统框可能会跟操作按钮重叠,故规避下: ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { var systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); } }
app/src/main/res/layout/activity_main.xml 说明 - 若想使用 android:onClick="method" 则应指定所属上下文 xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity" 约束属性用 - xmlns:app="http://schemas.android.com/apk/res-auto" app:layout_constraintTop_toTopOf="parent"
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </androidx.constraintlayout.widget.ConstraintLayout>
app\build.gradle.kts
plugins { alias(libs.plugins.android.application) } android { namespace = "com.example.myapplication" compileSdk = 34 defaultConfig { applicationId = "com.example.myapplication" minSdk = libs.versions.minSdk.get().toInt() // minSdk = 28 targetSdk = 34 // library 项目似乎将该属性迁移到了 lint{ targetSdk = 34 } versionCode = 1 versionName = "1.0" } buildTypes { release { isMinifyEnabled = false proguardFiles( // 注意 - isMinifyEnabled = true 方进行混淆,光指定规则文件没用。 getDefaultProguardFile("proguard-android-optimize.txt"), //"proguard-rules.pro" ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } } dependencies { implementation(libs.appcompat) // 不想用老旧的 appcompat 可从 extends AppCompatActivity 换为继承 android.app.Activity + material,同时 android:theme 也成可选了。 implementation(libs.material) implementation(libs.activity) implementation(libs.constraintlayout) }
app/src/main/AndroidManifest.xml 说明 - AppCompatActivity 依赖了 android:theme="@style/Theme.Material3.DayNight.NoActionBar" 否则报:You need to use a Theme.AppCompat theme (or descendant) with this activity.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:theme="@style/Theme.Material3.DayNight.NoActionBar"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
settings.gradle.kts
pluginManagement { repositories { google { content { includeGroupByRegex("com\\.android.*") includeGroupByRegex("com\\.google.*") includeGroupByRegex("androidx.*") } } mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() } versionCatalogs { // 提示不支持但实际可用:Android Studio does not support Version Catalog entries defined in Gradle Settings DSL create("libs") { version("minSdk","28") library("appcompat", "androidx.appcompat", "appcompat").version("1.7.0") library("material", "com.google.android.material", "material").version("1.12.0") library("activity", "androidx.activity", "activity").version("1.9.3") library("constraintlayout", "androidx.constraintlayout", "constraintlayout").version("2.2.0") plugin("android-application", "com.android.application").version("8.7.3") // 若为 android library 项目,则必须移除 defaultConfig 的 applicationId、versionCode、versionName; // 同时将 com.android.application 换为: //plugin("android-application", "com.android.library").version("8.7.3") } } } rootProject.name = "My Application" include(":app")
gradle.properties 说明 - 项目根右键进入 Open Module Settings 会自动生成 local.properties
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true android.nonTransitiveRClass=true

以下均为可选文件: build.gradle.kts
plugins { alias(libs.plugins.android.application) apply false }
.gitignore
*.iml .gradle /local.properties /.idea/caches /.idea/libraries /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store /build /captures .externalNativeBuild .cxx local.properties
app\.gitignore - 根目录 .gitignore 的 /build 管不了子项目。
/build
[未定义 versionCatalogs 则取该配置文件/可通过 generateCatalogAsToml 任务生成] gradle\libs.versions.toml
[versions] minSdk = "28" agp = "8.7.3" appcompat = "1.7.0" material = "1.12.0" activity = "1.9.3" constraintlayout = "2.2.0" [libraries] appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }

偏好设置

  简单用AlertDialog,自定义则用PreferenceFragmentCompat

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        var uText3 = getResources().getString(R.string.right_menu_setting);
        menu.add(uText3).setShowAsActionFlags(MenuItem.
                SHOW_AS_ACTION_IF_ROOM).setOnMenuItemClickListener(item -> {

            var ad = new AlertDialog.Builder(MainActivity.this).create();
            ad.setTitle(uText3 + " - " + com.openle.our.aos.AppCommon.appName(this) + " v" + AppCommon.versionName(this));

            ad.setButton(Dialog.BUTTON_POSITIVE, "[" + getResources().getString(R.string.right_menu_feedback) + "]", (dialog, which) -> {
                startActivity(new Intent(this, FeedbackActivity.class));
            });

            ad.setButton(Dialog.BUTTON_NEUTRAL, "[" + getResources().getString(R.string.right_menu_upgrade) + "]", (dialog, which) -> {
                AppUpgradeCommon.upgradeCheck(this);
            });

            var resValuePp = getResources().getIdentifier("right_menu_privacy", "string", getPackageName());
            ad.setButton(Dialog.BUTTON_NEGATIVE, "[" + getResources().getString(resValuePp) + "]", (dialog, which) -> {
                AndroidCommon.visitUri(MainActivity.this, "https://tsc.openle.com/.well-known/general/privacy/google-play/necessary-data.html");
            });
            ad.show();  // 未测ad.findViewById(resId)

            return true;
        });
        return super.onCreateOptionsMenu(menu);
    }

其他