Jetpack Compose 底部导航栏

注意:您需要有Android Studio Arctic Fox 及更高版本才能在您的项目中使用Jetpack Compose。

添加库

转到您的项目级gradle.build文件,并添加以下扩展名:

buildscript {
    ext {
        compose_version = '1.0.2'
    }

    // ...
}

现在转到应用级gradle.build文件,并添加以下内容:

android {
    // ...

    kotlinOptions {
        jvmTarget = '1.8'
        useIR = true
    }
    buildFeatures {
        // ...
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion compose_version
    }
}

dependencies {
    // ...

    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling:$compose_version"
    implementation "androidx.navigation:navigation-compose:2.4.0-alpha08"
    implementation "androidx.activity:activity-compose:1.3.1"

    // ...
}

创建顶部栏

打开activity(例如MainActivity.kt),并在类之外添加以下可组合函数以创建topbar

@Composable
fun TopBar() {
    TopAppBar(
        title = { Text(text = stringResource(R.string.app_name), fontSize = 18.sp) },
        backgroundColor = colorResource(id = R.color.colorPrimary),
        contentColor = Color.White
    )
}

@Preview(showBackground = true)
@Composable
fun TopBarPreview() {
    TopBar()
}

创建底部导航栏

在创建底部导航栏之前,我们必须准备Item;创建一个Sealed Class并指定NavigationItem作为名称。并使用参数为每个条形项目创建一个模型:

  • route:必须是unique。用来从底部导航栏导航到视图
  • icon : 栏项的图标
  • title : 栏项的名称
sealed class NavigationItem(var route: String, var icon: Int, var title: String) {
    object Home : NavigationItem("home", R.mipmap.ic_home, "首页")
    object Recommend : NavigationItem("recommend", R.mipmap.ic_recommend, "关注")
    object Books : NavigationItem("books", R.mipmap.ic_book, "书城")
    object Profile : NavigationItem("profile", R.mipmap.ic_profile, "我的")
}

同样,在MainActivity.kt 中,添加以下可组合函数:

@Composable
fun BottomNavigationBar() {
    val items = listOf(
        NavigationItem.Home,
        NavigationItem.Recommend,
        NavigationItem.Books,
        NavigationItem.Profile
    )
    BottomNavigation(
        backgroundColor = colorResource(id = R.color.colorPrimary),
        contentColor = Color.White
    ) {
        items.forEach { item ->
            BottomNavigationItem(
                icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
                label = { Text(text = item.title) },
                selectedContentColor = Color.White,
                unselectedContentColor = Color.White.copy(0.4f),
                alwaysShowLabel = true,
                selected = false,
                onClick = {
                    /* Add code later */
                }
            )
        }
    }
}

@Preview(showBackground = true)
@Composable
fun BottomNavigationBarPreview() {
    BottomNavigationBar()
}

创建包含顶部栏和底部导航栏的视图

这里我将使用Scaffold,创建一个名为MainScreen()的新组合函数,里面有一个Scaffold布局,并添加我们之前创建的TopBar()和BottomNavigationBar()

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MainScreen()
        }
    }
}

@Composable
fun MainScreen() {
    Scaffold(
        topBar = { TopBar() },
        bottomBar = { BottomNavigationBar() }
    ) {
        /* Add code later */
    }
}

@Preview(showBackground = true)
@Composable
fun MainScreenPreview() {
    MainScreen()
}

将导航栏与视图连接起来(导航)

连接之前,需要先创建Views,我创建了4个名为*Screen的 Kotlin 文件
HomeScreen.kt

@Composable
fun HomeScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(colorResource(id = R.color.colorPrimaryDark))
            .wrapContentSize(Alignment.Center)
    ) {
        Text(
            text = "Home View",
            fontWeight = FontWeight.Bold,
            color = Color.White,
            modifier = Modifier.align(Alignment.CenterHorizontally),
            textAlign = TextAlign.Center,
            fontSize = 25.sp
        )
    }
}

@Preview(showBackground = true)
@Composable
fun HomeScreenPreview() {
    HomeScreen()
}

RecommendScreen.kt

@Composable
fun RecommendScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(colorResource(id = R.color.colorPrimaryDark))
            .wrapContentSize(Alignment.Center)
    ) {
        Text(
            text = "Recommend View",
            fontWeight = FontWeight.Bold,
            color = Color.White,
            modifier = Modifier.align(Alignment.CenterHorizontally),
            textAlign = TextAlign.Center,
            fontSize = 25.sp
        )
    }
}

@Preview(showBackground = true)
@Composable
fun RecommendScreenPreview() {
    RecommendScreen()
}

BooksScreen.kt

@Composable
fun BooksScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(colorResource(id = R.color.colorPrimaryDark))
            .wrapContentSize(Alignment.Center)
    ) {
        Text(
            text = "Books View",
            fontWeight = FontWeight.Bold,
            color = Color.White,
            modifier = Modifier.align(Alignment.CenterHorizontally),
            textAlign = TextAlign.Center,
            fontSize = 25.sp
        )
    }
}

@Preview(showBackground = true)
@Composable
fun BooksScreenPreview() {
    BooksScreen()
}

ProfileScreen.kt

@Composable
fun ProfileScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(colorResource(id = R.color.colorPrimaryDark))
            .wrapContentSize(Alignment.Center)
    ) {
        Text(
            text = "Profile View",
            fontWeight = FontWeight.Bold,
            color = Color.White,
            modifier = Modifier.align(Alignment.CenterHorizontally),
            textAlign = TextAlign.Center,
            fontSize = 25.sp
        )
    }
}

@Preview(showBackground = true)
@Composable
fun ProfileScreenPreview() {
    ProfileScreen()
}

接下来在BottomNavigationBar中,添加参数navController,并创建navBackStackEntry和currentRoute。使用currentRoute,我们检查是否必须突出显示栏项目,然后使用navigate()方法导航到视图

@Composable
fun BottomNavigationBar(navController: NavController) {
    val items = listOf(
        NavigationItem.Home,
        NavigationItem.Recommend,
        NavigationItem.Books,
        NavigationItem.Profile
    )
    BottomNavigation(
        backgroundColor = colorResource(id = R.color.colorPrimary),
        contentColor = Color.White
    ) {
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination?.route
        items.forEach { item ->
            BottomNavigationItem(
                icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
                label = { Text(text = item.title) },
                selectedContentColor = Color.White,
                unselectedContentColor = Color.White.copy(0.4f),
                alwaysShowLabel = true,
                selected = currentRoute == item.route,
                onClick = {
                    navController.navigate(item.route) {                        
                        navController.graph.startDestinationRoute?.let { route ->
                            popUpTo(route) {
                                saveState = true
                            }
                        }
                       
                        launchSingleTop = true
                        // 重新选择同一项目时避免同一目的地的多个副本
                        restoreState = true
                    }
                }
            )
        }
    }
}

@Preview(showBackground = true)
@Composable
fun BottomNavigationBarPreview() {
    // BottomNavigationBar()
}

创建一个新的可组合函数并将其命名为Navigation(),参数为NavHostController类型的参数navController。在这里,我们创建了一个NavHost,我们将Home视图设置为startDestination,我们将每个View的路由设置为可组合的。

@Composable
fun Navigation(navController: NavHostController) {
    NavHost(navController, startDestination = NavigationItem.Home.route) {
        composable(NavigationItem.Home.route) {
            HomeScreen()
        }
        composable(NavigationItem.Recommend.route) {
            RecommendScreen()
        }
        composable(NavigationItem.Books.route) {
            BooksScreen()
        }
        composable(NavigationItem.Profile.route) {
            ProfileScreen()
        }
    }
}

在MainScreen()中创建NavigationController,并将其传递给BottomNavigationBar和Navigation

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    Scaffold(
        topBar = { TopBar() },
        bottomBar = { BottomNavigationBar(navController) }
    ) {
        Navigation(navController)
    }
}

adb shell input keyevent

通过adb shell input keyevent,event_code发送到设备

usage: input [text|keyevent]
  input text <string>
  input keyevent <event_code>

event_code:

0 -->  "KEYCODE_UNKNOWN" 
1 -->  "KEYCODE_MENU" 
2 -->  "KEYCODE_SOFT_RIGHT" 
3 -->  "KEYCODE_HOME" 
4 -->  "KEYCODE_BACK" 
5 -->  "KEYCODE_CALL" 
6 -->  "KEYCODE_ENDCALL" 
7 -->  "KEYCODE_0" 
8 -->  "KEYCODE_1" 
9 -->  "KEYCODE_2" 
10 -->  "KEYCODE_3" 
11 -->  "KEYCODE_4" 
12 -->  "KEYCODE_5" 
13 -->  "KEYCODE_6" 
14 -->  "KEYCODE_7" 
15 -->  "KEYCODE_8" 
16 -->  "KEYCODE_9" 
17 -->  "KEYCODE_STAR" 
18 -->  "KEYCODE_POUND" 
19 -->  "KEYCODE_DPAD_UP" 
20 -->  "KEYCODE_DPAD_DOWN" 
21 -->  "KEYCODE_DPAD_LEFT" 
22 -->  "KEYCODE_DPAD_RIGHT" 
23 -->  "KEYCODE_DPAD_CENTER" 
24 -->  "KEYCODE_VOLUME_UP" 
25 -->  "KEYCODE_VOLUME_DOWN" 
26 -->  "KEYCODE_POWER" 
27 -->  "KEYCODE_CAMERA" 
28 -->  "KEYCODE_CLEAR" 
29 -->  "KEYCODE_A" 
30 -->  "KEYCODE_B" 
31 -->  "KEYCODE_C" 
32 -->  "KEYCODE_D" 
33 -->  "KEYCODE_E" 
34 -->  "KEYCODE_F" 
35 -->  "KEYCODE_G" 
36 -->  "KEYCODE_H" 
37 -->  "KEYCODE_I" 
38 -->  "KEYCODE_J" 
39 -->  "KEYCODE_K" 
40 -->  "KEYCODE_L" 
41 -->  "KEYCODE_M" 
42 -->  "KEYCODE_N" 
43 -->  "KEYCODE_O" 
44 -->  "KEYCODE_P" 
45 -->  "KEYCODE_Q" 
46 -->  "KEYCODE_R" 
47 -->  "KEYCODE_S" 
48 -->  "KEYCODE_T" 
49 -->  "KEYCODE_U" 
50 -->  "KEYCODE_V" 
51 -->  "KEYCODE_W" 
52 -->  "KEYCODE_X" 
53 -->  "KEYCODE_Y" 
54 -->  "KEYCODE_Z" 
55 -->  "KEYCODE_COMMA" 
56 -->  "KEYCODE_PERIOD" 
57 -->  "KEYCODE_ALT_LEFT" 
58 -->  "KEYCODE_ALT_RIGHT" 
59 -->  "KEYCODE_SHIFT_LEFT" 
60 -->  "KEYCODE_SHIFT_RIGHT" 
61 -->  "KEYCODE_TAB" 
62 -->  "KEYCODE_SPACE" 
63 -->  "KEYCODE_SYM" 
64 -->  "KEYCODE_EXPLORER" 
65 -->  "KEYCODE_ENVELOPE" 
66 -->  "KEYCODE_ENTER" 
67 -->  "KEYCODE_DEL" 
68 -->  "KEYCODE_GRAVE" 
69 -->  "KEYCODE_MINUS" 
70 -->  "KEYCODE_EQUALS" 
71 -->  "KEYCODE_LEFT_BRACKET" 
72 -->  "KEYCODE_RIGHT_BRACKET" 
73 -->  "KEYCODE_BACKSLASH" 
74 -->  "KEYCODE_SEMICOLON" 
75 -->  "KEYCODE_APOSTROPHE" 
76 -->  "KEYCODE_SLASH" 
77 -->  "KEYCODE_AT" 
78 -->  "KEYCODE_NUM" 
79 -->  "KEYCODE_HEADSETHOOK" 
80 -->  "KEYCODE_FOCUS" 
81 -->  "KEYCODE_PLUS" 
82 -->  "KEYCODE_MENU" 
83 -->  "KEYCODE_NOTIFICATION" 
84 -->  "KEYCODE_SEARCH" 
85 -->  "TAG_LAST_KEYCODE"

kotlin-android-extensions 已被弃用,如何使用 @Parcelize?

第 1 步。更新到最新的 kotlin 版本 -1.4.20并替换

apply plugin: 'kotlin-android-extensions'

to

apply plugin: 'kotlin-parcelize'

或者

plugins {
    ..
    id 'kotlin-parcelize'
}

第 2 步。从 android {} 中删除以下代码

androidExtensions {
    experimental = true
}

第 3 步。最后,替换旧的 import ->

import kotlinx.android.parcel.Parcelize

to

import kotlinx.parcelize.Parcelize

新插件:https : //plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.parcelize

迁移已弃用的 Kotlin Android Extension 使用ViewBinding

最近出现在了一个这样的警告:

The 'kotlin-android-extensions' Gradle plugin is deprecated. Please use this migration guide (https://goo.gle/kotlin-android-extensions-deprecation) to start working with View Binding (https://developer.android.com/topic/libraries/view-binding) and the 'kotlin-parcelize' plugin.

kotlinx.android.synthetic 不再是推荐的做法,删除支持显式findViewById,用ViewBinding替代

ViewBinding

与 Kotlin Extensions相比,它增加了视图查找和类型安全的编译时检查。但视图绑定后,可让您更轻松地编写与视图交互的代码。启用视图绑定后,它会为该模块中存在的每个 XML 布局文件生成一个绑定类。

如何启用视图ViewBinding?

在build.gradle中添加:

android {
..
    buildFeatures {
       viewBinding true
    }
}

如何使用ViewBinding?

  • 如果为模块启用了视图绑定,则会为模块包含的每个 XML 布局文件生成一个绑定类。
  • 每个绑定类都包含对根视图和所有具有 ID 的视图的引用。
  • 绑定类的名称是通过将 XML 文件的名称转换为 Pascal 大小写并Binding在末尾添加单词来生成的。

在Activity中使用ViewBinding

private lateinit var binding: ActivityMainBinding

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

使用binding对象访问View

binding.name.text = "this is ViewBinding"

在Fragment中使用ViewBinding

在Fragment中使用ViewBinding需要注意,因为ViewBinding不能很好地与Fragment一起使用,如果不在OnDestroy清除,则它不会从内存中清除,导致内存泄漏。

private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = FragmentMainBinding.inflate(inflater, container, false)
    return binding.root
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

像在activity中一样使用对象访问视图

binding.name.text = "this is ViewBinding"

备注

ViewBinding将为模块中的每个XML布局生成一个绑定对象,例如: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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ViewBinding将生成 ActivityMainBinding.java

public final class ActivityMainBinding implements ViewBinding {
  @NonNull
  private final ConstraintLayout rootView;

  @NonNull
  public final TextView textView;
...
}

ViewBinding将为每个具有指定id. 在ActivityMainBinding.java中,ViewBinding生成一个公共inflate方法。
它调用bind将绑定属性的位置,并进行一些错误检查。

Room数据库Schema export Error

在使用Android Room数据库的时候报错了:

Schema export directory is not provided to the annotation processor so we cannot export the schema. You can either provide `room.schemaLocation` annotation processor argument OR set exportSchema to false.

如果您不需要检查架构并且想要摆脱警告,只需将exportSchema = false添加到RoomDatabase中,如下所示:

@Database(entities = { YourEntity.class }, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
   //...
}

另一种方法是在应用模块的build.gradle文件中,将此文件添加到defaultConfig部分(在android部分下面)。这会将架构写到项目文件夹的schemas子文件夹中。

android {

    // ... (compileSdkVersion, buildToolsVersion, etc)

    defaultConfig {

     // ... (applicationId, miSdkVersion, etc)

        kapt {
            arguments {
                arg("room.schemaLocation","$projectDir/schemas".toString())
            }
        }
    }

    buildTypes {
        // ... (buildTypes, compileOptions, etc)
    }
}

//...

以上是Kotlin,如果是Java:

android {

    // ... (compileSdkVersion, buildToolsVersion, etc)

    defaultConfig {

        // ... (applicationId, miSdkVersion, etc)

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation":"$projectDir/schemas".toString()]
            }
        }
    }

    // ... (buildTypes, compileOptions, etc)

}

当执行项目后,在Android Studio 的Project视图下,查看项目,会发现Module生成了一个schemas的文件夹,打开.json文件看起来像这样:

{
 "formatVersion": 1,
 "database": {
   "version": 1,
   "identityHash":"6240057b6178b803a0bf9915edf969e3",
   "entities": [
      {
       "tableName":"sms_table",
       "createSql":"CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `message` TEXT, `date` INTEGER, `client_id` INTEGER)",
       "fields": [
          {
           "fieldPath":"id",
           "columnName":"id",
           "affinity":"INTEGER"
          },
          {
           "fieldPath":"message",
           "columnName":"message",
           "affinity":"TEXT"
          },
          {
           "fieldPath":"date",
           "columnName":"date",
           "affinity":"INTEGER"
          },
          {
           "fieldPath":"clientId",
           "columnName":"client_id",
           "affinity":"INTEGER"
          }
        ],
       "primaryKey": {
         "columnNames": [
           "id"
          ],
         "autoGenerate": true
        },
       "indices": [],
       "foreignKeys": []
      }
    ],
   "setupQueries": [
     "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
     "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, "6240057b6178b803a0bf9915edf969e3")"
    ]
  }
}

改变状态栏文本颜色(WHITE/BLACK)

API> = 23

从API v23及更高版本开始,可以将以下内容添加到AppTheme styles.xml中:

  • 解决方案1
// View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR: 将状态栏的文字设置为黑色
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
  • 解决方案1
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
<item name="android:windowLightStatusBar">true</item>

当android:windowLightStatusBar设置为true,状态栏文字颜色将能够可以看出,当状态栏的颜色是白色的,而当android:windowLightStatusBar设置为false,状态栏文字颜色将被设计在状态栏的颜色是可以看到暗。

API <23

在Android中无法更改状态栏的颜色。可以在应用程序中设置状态栏的背景色

val window: Window = activity.getWindow()
// clear FLAG_TRANSLUCENT_STATUS flag:
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
// add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
// finally change the color
window.setStatusBarColor(ContextCompat.getColor(activity,R.color.my_statusbar_color))

https://developer.android.google.cn/reference/android/view/Window.html#setStatusBarColor(int)

onActivityResult过时了,registerForActivityResult使用方法

最常用的写法

registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) { result ->
            val data = result.data
            val resultCode= result.resultCode
            val extra = data?.getStringExtra("extra")
        }.launch(Intent(this, MainActivity::class.java))

除了基本用法,ActivityResultContracts还提供了一些ActivityResultContract的实现类,以便进行其他的操作:

  • RequestMultiplePermissions : 多个权限请求
  • RequestPermission : 单个权限请求
  • TakePicturePreview : 拍照预览
  • TakePicture : 拍照
  • TakeVideo : 摄像
  • PickContact : 选择联系人
  • GetContent : 获取各种文件的Uri
  • GetMultipleContents : 获取多个各种文件的Uri
  • OpenDocument : 打开文件
  • OpenMultipleDocuments : 打开多个文件
  • OpenDocumentTree : 打开文件夹
  • CreateDocument : 创建文件

打开相机拍照:

registerForActivityResult(ActivityResultContracts.TakePicturePreview()){
           // 返回bitmap
        }.launch(null)

获取单个/多个权限请求:


// 单个权限获取
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
            if (it) {//同意权限
            } else {//拒绝权限
            }
        }.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
// 多个权限获取
var permissions: Array<String> = arrayOf(
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.CAMERA
        )
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
            if (it[Manifest.permission.WRITE_EXTERNAL_STORAGE]!!) {// 同意
            } else {// 拒绝
            }
            if (it[Manifest.permission.CAMERA]!!) {// 同意
            } else {// 拒绝
            }
        }.launch(permissions)

查询联系人信息:

registerForActivityResult(ActivityResultContracts.PickContact()){
            if(it != null){
                val cursor = contentResolver.query(it, null, null, null, null)
                cursor?.run {
                    if(cursor.moveToFirst()){
                        val name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                    }
                }
            }
        }.launch(null)

选取文件:

registerForActivityResult(ActivityResultContracts.OpenDocument()){
            // 获取的文件uri
        }.launch(arrayOf("image/*","text/plain"))

Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

android 8.0 (API=26) 出现的问题

解决方法:

  1. 去掉android:screenOrientation;
  2. android:windowIsTranslucent 改为false; 如果需要透明的设置加上android:windowDisablePreview =true;
    在项目res目录下创建values-v26/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme.Start" parent="AppTheme">
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
      
        <!-- 适配android手机系统8.0(api26),Only fullscreen opaque activities can request orientation -->
        <!--用背景图消除启动白屏-->
        <item name="android:windowBackground">@mipmap/background</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowDisablePreview">true</item>
    </style>
</resources>