Common usage skills of Android

1. Skills for obtaining Context globally

In Android, you will find that you need to use Context in many places, such as popping up Toast, starting Activity, sending broadcasts, operating databases, and using notifications.
At this time, if we need to get the Context anywhere in the project, Android provides an Application class. Whenever the application starts, the system will automatically initialize these classes. And we can customize an own Application class to manage some global state information in the program, such as the global Context .
First create a MyApplication

class MyApplication : Application() {
    
    
    companion object{
    
    
        lateinit var context: Context
    }

    override fun onCreate() {
    
    
        super.onCreate()
        context=this.applicationContext
    }
}

As you can see, the code in MyApplication is very simple. Here we define a context variable in the companion object, then override the onCreate() method of the parent class, and assign the return value obtained by calling the getApplicationContext() method to the context variable, so that we can obtain the context as a static variable object.
Next, specify it under the <application> tag in the AndroidManifest.xml file.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.kotlintext">
 <application
        android:name="com.example.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.KotlinText">
        ...
    </application>
</manifest>

In this way, we have implemented a mechanism for global acquisition of Context.
Next, for example, the showToast() method we defined can be written like this

fun String.showToast(duration:Int=Toast.LENGTH_SHORT){
    
    
Toast.makeText(MyApplication.context,this,duration).show()
}

Now you only need to use the following usage to pop up a text prompt:

"This is Toast".showToast()

2. Use Intent to pass objects

There are two ways to use Intent to transfer objects: Serializable and Parcelable.

Serializable method

Serializable means serialization, which means converting an object into a storable or transferable state. Serialized objects can be transmitted over the network or stored locally. As for the serialization method is very simple, just let a class implement the Serializable interface.
For example, there is a Person class that contains two fields, name and age. If you want to serialize it, you can write it like this:

class Person :Serializable{
    
    
    var name=""
    var age=0
}

Here we let the Person class implement the Serializable interface, so that all Person objects are serializable.

        var person=Person()
        person.name="Tom"
        person.age=20
        val intent=Intent(this,SecondActivity::class.java)
        intent.putExtra("person_data",person)
        startActivity(intent)

As you can see, here we create an instance of Person and pass it directly into the putExtra() method of Intent. Because the Person class implements the Serializable interface, it can be written like this.
Next, it is also very simple to obtain this object in SecondActivity, written as follows:

 val person= intent.getSerializableExtra("person_data") as Person

Here, the getSerializableExtra() method of the Intent is called to obtain the serialized object passed through the parameter, and then it is downcast to the Person object, so that we have successfully realized the function of using the Intent to transfer the object.
It should be noted that the working principle of this transfer object is to serialize an object into a storable or transferable state, pass it to another Activity, and then deserialize it into a new object . Although the data stored in these two objects is exactly the same, they are actually different objects.

Parcelable way

In addition to Serializable, Parcelable can also be used to achieve the same effect, but unlike serializing objects, the implementation principle of Parcelable is to decompose a complete object, and each part of the decomposed part is the data supported by Intent Type, so that the function of passing objects can be realized .
Modify the code in Person as follows

class Person() :Parcelable{
    
    
    var name=""
    var age=0

    override fun describeContents(): Int {
    
    
        return 0
    }

    override fun writeToParcel(dest: Parcel?, flags: Int) {
    
    
        dest?.writeString(name)//写出name
        dest?.writeInt(age)//写出age
    }

    companion object CREATOR : Parcelable.Creator<Person> {
    
    
        override fun createFromParcel(parcel: Parcel): Person {
    
    
            val person=Person()
            person.name=parcel.readString() ?:""//读取name
            person.age=parcel.readInt()//读取age
            return person
        }

        override fun newArray(size: Int): Array<Person?> {
    
    
            return arrayOfNulls(size)
        }
    }

}

The implementation of Parcelable is slightly more complicated. As you can see, first we let the Person class implement the Parcelable interface, so we must rewrite the two methods describeContents() and writeToParcel(). Among them, the describeContents() method returns 0, and for the writeToParcel() method, we need to call the writeXxx() method of Parcel to write out the fields in the Person class one by one.
Besides that, we have to provide an anonymous class implementation called CREATOR in the Person class. An implementation of the Parcelable.Creator interface is created here, and the generic type is specified as Person. Then we need to rewrite the two methods createFromParcel() and newArray(). In the createFromParcel() method, we need to create a Person object to return, and read the name and age fields just written. Among them, name and age are read by calling Parcel's readXxx() method (note that the order of reading here must be exactly the same as the order just written) and the implementation in the newArray() method only needs to call arrayOfNulls() method, and use the size passed in the parameter as the array size to create an empty Person array.
Next, we can use the previous code to pass the Person object, but we need to modify it slightly when getting the object:

  val person = intent.getParcelableExtra<Person>("person_data")

But this method is more complicated to implement, so Kotlin provides another more convenient usage, but the premise is that all the data to be passed must be encapsulated in the main constructor of the object.
Modify the code in the Person class as follows

@Parcelize
class Person(var name:String,var age:Int) :Parcelable{
    
    
}

In contrast, the Serializable method is relatively simple, but the entire object is serialized, so the efficiency will be lower than the Parcelable method.

3. Customize your own log tool

When we write a relatively large project, in order to facilitate debugging, a large number of logs are printed in multiple places in the code. When the project is officially launched, it will still be printed as usual, which will reduce the operating efficiency of the program and may also save some data. leak out. The most ideal situation at this time is to be able to freely control the printing of the log. When the program is in the development stage, the log will be printed out, and the log will be blocked when the program is launched.
Create a new LogUtil singleton class

object LogUtil {
    
    
    //const val 可见性为public final static
    private const val VERBOSE=1
    private const val DEBUG=2
    private const val INFO=3
    private const val WARN=4
    private const val ERROR=5
    private var level= VERBOSE
    fun v(tag:String,msg:String){
    
    
        if(level<= VERBOSE){
    
    
            Log.v(tag,msg)
        }
    }
    fun d(tag: String,msg: String){
    
    
        if(level<= DEBUG){
    
    
            Log.d(tag,msg)
        }
    }
    fun i(tag: String,msg: String){
    
    
        if(level<= INFO){
    
    
            Log.i(tag,msg)
        }
    }
    fun w(tag: String,msg: String){
    
    
        if(level<= WARN){
    
    
            Log.w(tag,msg)
        }
    }
    fun e(tag: String,msg: String){
    
    
        if(level<= ERROR){
    
    
            Log.e(tag,msg)
        }
    }

}

In this way, a custom log tool is created. For example, to print a line of DEBUG level logs, you can write like this:

 LogUtil.d("TAG","debug log")

Printing a line of WARN level logs can be written like this:

LogUtil.w("TAG","warn log")

We only need to modify the value of the level variable to freely control the printing behavior of the log. For example, if level is equal to VERBOSE, all logs can be printed out, and if level is equal to ERROR, only the error log of the program can be printed.

4. Dark theme

In addition to making the eyes more comfortable when using it at night, the dark theme can also reduce battery consumption, thereby extending the battery life of the phone. For mobile phones with Android 10.0 and above, the dark theme can be turned on and off in Settings→Display→Dark theme. After turning on the dark theme, the interface style of the system, including some built-in applications, will change to the color of the dark theme.
insert image description here
But when you open the application we wrote ourselves, you will find that the current interface style is still using the light theme mode, which is different from the system theme style, indicating that we need to adapt to this.
The easiest way to adapt is to use Force Dark, which is a way to quickly adapt the application to the dark theme without writing additional code. The working principle of Force Dark is that the system analyzes each layer of View under the light theme application, and automatically converts the colors of these Views to colors that are more suitable for the dark theme before they are drawn to the screen. (Note that only apps with a light theme can use this method. If your app originally uses a dark theme, Force Dark will not work).
Enabling Force Dark requires the help of the android:forceDarkAllowed attribute

<item name="android:forceDarkAllowed">true</item>

Here the android:forceDarkAllowed attribute is set to true, indicating that now we allow the system to use Force Dark to force the application into a dark theme.
In addition, there is another way to implement it. We know that the built-in themes of the AppCompat library are mainly divided into two types: light theme and dark theme. For example, Theme.AppCompat.Light.NoActionBar is a light theme, and Theme. AppCompat.NoActionBar is the dark theme. Choosing different themes will have completely different effects in terms of the default color of the controls.
And now we have an extra DayNight theme. After using this theme, when the user turns on the dark theme in the system settings, the application will automatically use the dark theme, otherwise it will use the light theme.
Create a new values-night

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_200</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/black</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_200</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color . -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

As you can see, here we specify the parent theme of AppTheme as Theme.AppCompat.DayNight.NoActionBar, which is a DayNight theme. Therefore, under normal circumstances, the application will still use the light theme, but once the user turns on the dark theme in the system settings, the application will automatically use the dark theme.
We should use more theme properties that automatically switch colors based on the current theme. For example, black text should usually be set off against a white background, whereas white text should usually be set off against a black background. Then at this time we can use the theme attribute to specify the color of the background and text, written as follows:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?android:attr/colorBackground"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World"
        android:layout_gravity="center"
        android:textSize="40sp"
        android:textColor="?android:attr/textColorPrimary"
        />
</FrameLayout>

insert image description here
insert image description here
In addition, when you need to execute different code logics under light theme and dark theme, you can use the following code to judge whether the current system is dark theme at any time:

    fun isDarkTheme(context: Context):Boolean{
    
    
        val flag=context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
        return flag==Configuration.UI_MODE_NIGHT_YES
    }

Call the isDarkTheme() method to determine whether the current system has a light theme or a dark theme, and then execute different code logics according to the return value.

5. Conversion between Java and Kotlin code

insert image description here
insert image description here
insert image description here
Click the Decompile button in the upper left corner of the window to compile the previous Kotlin code into Java code.

Guess you like

Origin blog.csdn.net/ChenYiRan123456/article/details/128794489