[Android] Kotlin learning (1)


不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!

Android——Kotlin learning

1. Use the control id to directly use the control

  • Include the following statement in the code block in build.gradlethe file plugins.
id 'kotlin-android-extensions'
  • In the logic file, declare its layout file.
import kotlinx.android.synthetic.main.activity_main.*
  • In the follow-up Fragmentstudy, I encountered idthe problem of directly using the control to use the control. It always reports a null pointer. ?.After joining, although there will be no crash, the control cannot be displayed, which proves that it is indeed not loaded.
  • It is puzzling, and after learning, I found that when Kotlinusing idthe operation control directly in , it must be used after the layout file is loaded, because it exists only after the loading is complete; the layout file is not loaded, how come id, how to use it, it must be empty. So in Fragment, if we habitually onCreateViewuse it in the method idto operate the control, it will definitely report empty, because viewthere is none yet return.

2. Inherited class implements interface

  • Inheritance and implementation after the colon, with ()a class, is inheritance; without ()an interface, is implementation.
class MainActivity : AppCompatActivity(), View.OnClickListener{
    
    ...}

3. Click event monitoring

  • consistent with Javathe grammar.
fun initClick() {
    
    
        //跳转页面功能
        btn_register.setOnClickListener(this)
        //网络请求功能
        btn_login.setOnClickListener(this)
        //文本控件操作功能
        btn_reset.setOnClickListener(this)
    }

4. Click event interface implementation

  • It is no longer using switch, but using when.
override fun onClick(v: View?) {
    
    
        when (v?.id) {
    
    
            //点击注册,跳转进入注册页面
            R.id.btn_register -> {
    
    
                //创建跳转对象
                val intent = Intent(this, MainActivity2::class.java)
                //进行页面跳转
                startActivity(intent)
            }
            //点击监听,得到网络请求数据
            R.id.btn_login -> {
    
    
                val intent = Intent(this, HomeActivity::class.java)
                //进行页面跳转
                startActivity(intent)
                getDataByInternet()
            }
            R.id.btn_reset -> {
    
    
                edit_username.setText("")
                edit_password.setText("")
            }
        }
    }

5. Page Jump

  • It is still used Intentto jump to the interface.
val intent = Intent(this, HomeActivity::class.java)
                //进行页面跳转
                startActivity(intent)

6. Text manipulation

  • getThere is no need for class methods and direct attribute calls get; setclass methods can have two operations.
Log.i("testData", edit_username.text.toString())
                Log.i("testData", edit_password.text.toString())
                //setText  Java版本
                edit_username.setText("")
                edit_password.setText("")
                // = 版本
                edit_username.text = ""
                edit_password.text = ""

7. Network request data

  • Use to Retrofitmake network data requests.
  1. Create data objects.
/** FileName: User
 * Author: lvjunkai
 * Date: 2022/8/2 14:17
 * Description: 用户类,和后端接口的数据格式一致
 */
class User(
    val id : Int,
    val username : String,
    val password : String,
    val nickname : String,
    val avatar : String,
    val sex : String,
    val age : Int,
    val phone : String,
    val email : String,
    val islogin : String
)
  1. Create a proxy interface.
/** FileName: ApiService
 * Author: lvjunkai
 * Date: 2022/8/2 15:31
 * Description: 网络请求接口
 */

interface ApiService {
    
    

    @GET("user")
    fun listUser(): Call<List<User>>

    @GET("recipe")
    fun listRecipe() : Call<List<Recipe>>
}
  1. Create a network connection.
private lateinit var retrofit: Retrofit
    private lateinit var api: ApiService


/**
     * 建立网络请求链接
     */
    fun initSocket() {
    
    
        // builder模式构建Retrofit对象
        retrofit = retrofit2.Retrofit.Builder()
            .baseUrl("https://www.fastmock.site/mock/d6931ad23f0e1bdb2061d1c5363c45cb/KotlinLearning/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        // 创建接口层的代理对象,内部通过动态代理创建了ApiService的代理对象
        api = retrofit.create(ApiService::class.java)
    }
  1. Make a network request.
/**
     * 网络请求得到数据的具体实现
     */
    fun getDataByInternet() {
    
    
        // 执行异步请求
        api.listUser().enqueue(object : Callback<List<User>> {
    
    
            override fun onResponse(call: Call<List<User>>, response: Response<List<User>>) {
    
    
                Log.e("data", response.body().toString())
                response.body()?.let {
    
     it1 -> Log.e("data", it1.size.toString()) }
                for (i in 0 until (response.body()?.size!!)) {
    
    
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.id.toString()) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.username) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.password) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.nickname) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.avatar) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.sex) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.age.toString()) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.phone) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.email) }
                    response.body()?.get(i)?.let {
    
     it1 -> Log.e("onResponse", it1.islogin) }
                }
            }

            override fun onFailure(call: Call<List<User>>, t: Throwable) {
    
    
                Log.e("onFailure", t.toString())
            }
        })
    }

8. Variables and constants in Kotlin

  • AsFragment an example, summarize the constant variables.
  1. Variables (lazy loading) -lateinit var
  • Variables can be read and written, and assigned values ​​can be modified.
private lateinit var homeFragment: HomeFragment

Variable lazy loading means that the variable is not loaded when it is declared, and it is loaded when it is used, that is, lazy loading. Regardless of whether it is declared or not, if it is used before it is created, an error will be reported.

if(homeFragment == null){
    
    
            homeFragment = HomeFragment()
        }

In the above code, if it is not created homeFragment, it is used to judge whether it is empty, so an error will be reported.

  • Correct variable writing.
private var myFragment : MyFragment? = null

?Indicates that the object is nullable.

  • Executed when empty.
myFragment?:let {
            myFragment = MyFragment()
            fragmentTransaction.add(R.id.fragment,myFragment!!)
        }
  • Executed when not empty.
myFragment?.let {
    
     fragmentTransaction.show(it) }
  1. constant--val
  • It is a read-only variable that can only be initialized once.
private val homeFragment : HomeFragment = HomeFragment()
private val homeFragment3 : HomeFragment

init {
    
    
        homeFragment3 = HomeFragment()
    }

Constants are either initialized directly at declaration time, or in the constructor init.

9. Do you really understand global variables?

  • CThe language provides the definition of global variables, also known as: external variables.
  • As the name implies, variables defined outside the function are called external variables.
  • Its scope is the entire source program, so global variables can be accessed anywhere in the entire source program.
  • It is not only in a class, called global variables; but in the entire source program.
  • Global variables are located throughout the source program, so they live and die together, and as long as the program is not stopped, it will not disappear.

10. Closure concept

  • A closure is a function that can access variables inside other functions.

11. Fragment use

  • Example - ActivityIncorporate Fragmentdisplay interface in .
  • The logic exists Activityin , because Fragmentthe jumps and so on are all Activityexecuted in .
  1. The statement needs to be displayed Fragment.
private var homeFragment : HomeFragment? = null
    private var recipeFragment : RecipeFragment? = null
    private var discussionFragment : DiscussionFragment? = null
    private var myFragment : MyFragment? = null
  1. Create a bottom bar listener.
 /**
     * 设置底部栏监听
     */
    private fun initClick() {
    
    
        ll_home.setOnClickListener(this)
        ll_recipe.setOnClickListener(this)
        ll_discussion.setOnClickListener(this)
        ll_person.setOnClickListener(this)
    }
  1. On initialization, the bottom bar changes, showing the first one Fragment.
//创建的时候显示首页
        ll_home?.isSelected = true
        ll_recipe?.isSelected = false
        ll_discussion?.isSelected = false
        ll_person?.isSelected = false
        initHomeFragment()
  1. display Fragment.
  • Notice: Each Fragmentdisplay requires a new transaction, because the transaction is committed, it becomes invalid, and a new one is needed.
private fun initHomeFragment() {
    
    
        //每一个Fragment都需要一个单独的事务
        //因为提交后,事务虽然创建方式一样,但是本质已经不同
        var fragmentTransaction : FragmentTransaction = supportFragmentManager.beginTransaction()
        //若是homeFragment为空
        homeFragment?:let {
    
    
            homeFragment = HomeFragment()
            fragmentTransaction.add(R.id.fragment, homeFragment!!)
        }
        //隐藏事务中所有的Fragment
        hideAllFragment(fragmentTransaction)
        //显示需要显示的Fragment
        homeFragment?.let {
    
     fragmentTransaction.show(it) }
        //提交事务,事务一定要提交,不然无效
        fragmentTransaction.commit()
    }
  1. hide all Fragment.
/**
     * 隐藏所有Fragment
     */
    private fun hideAllFragment(fragmentTransaction: FragmentTransaction) {
    
    
        homeFragment?.let {
    
     fragmentTransaction.hide(homeFragment!!) }
        recipeFragment?.let {
    
     fragmentTransaction.hide(recipeFragment!!) }
        discussionFragment?.let {
    
     fragmentTransaction.hide(discussionFragment!!) }
        myFragment?.let {
    
     fragmentTransaction.hide(myFragment!!) }
    }
  1. Click on the bottom bar to display the different Fragment.
override fun onClick(v: View?) {
    
    
        when (v?.id) {
    
    
            R.id.ll_home -> {
    
    
                ll_home?.isSelected = true
                ll_recipe?.isSelected = false
                ll_discussion?.isSelected = false
                ll_person?.isSelected = false
                initHomeFragment()
            }
            R.id.ll_recipe -> {
    
    
                ll_home?.isSelected = false
                ll_recipe?.isSelected = true
                ll_discussion?.isSelected = false
                ll_person?.isSelected = false
                initRecipeFragment()
            }
            R.id.ll_discussion -> {
    
    
                ll_home?.isSelected = false
                ll_recipe?.isSelected = false
                ll_discussion?.isSelected = true
                ll_person?.isSelected = false
                initDiscussionFragment()
            }
            R.id.ll_person -> {
    
    
                ll_home?.isSelected = false
                ll_recipe?.isSelected = false
                ll_discussion?.isSelected = false
                ll_person?.isSelected = true
                initMyFragment()
            }
            else -> {
    
     }
        }
    }

12. RecyclerView use

  • Example - use RecyclerViewto realize the display of prescription data.
  • The renderings are as follows:
    insert image description here
  1. Prepare data classes.
package com.example.myapplication.model

/** FileName: Recipe
 * Author: lvjunkai
 * Date: 2022/8/3 11:36
 * Description: 方剂数据类 网络请求数据
 */
class Recipe(
    val id: Int,
    val name: String,
    val song: String,
    val medicines: String,
    val function: String,
    val cure: String,
    val type: String,
    val childtype: String,
    val book: String
)
  1. Prepare the backend interface.
@GET("recipe")
    fun listRecipe() : Call<List<Recipe>>
  1. Prepare layout and itemlayout.
  • There is only one layout RecyclerView, and the sub-layouts are as follows:
<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_recipe_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:text="大承气汤"
        android:textColor="@color/black"
        android:textSize="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_recipe_function"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="峻下热结"
        app:layout_constraintStart_toStartOf="@+id/tv_recipe_name"
        app:layout_constraintTop_toBottomOf="@+id/tv_recipe_name" />

    <TextView
        android:id="@+id/tv_recipe_book"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:text="《伤寒论》"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. Create iteman adapter.
package com.example.myapplication.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.R
import com.example.myapplication.model.Recipe

/** FileName: RecipeAdapter
 * Author: lvjunkai
 * Date: 2022/8/4 9:52
 * Description: 方剂数据适配器
 */
class RecipeAdapter(private val recipeList : List<Recipe>) : RecyclerView.Adapter<RecipeAdapter.MyViewHolder>(){
    
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeAdapter.MyViewHolder {
    
    
        //加载子布局
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recipe,parent,false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: RecipeAdapter.MyViewHolder, position: Int) {
    
    
        //操作控件
        val item = recipeList[position]
        with(holder){
    
    
            tv_recipe_name.text = item.name
            tv_recipe_function.text = item.function
            tv_recipe_book.text = item.book
        }
    }

    override fun getItemCount(): Int {
    
    
        //返回数据数目
        return recipeList.size
    }

    inner class MyViewHolder(view : View) : RecyclerView.ViewHolder(view){
    
    
        //获取控件
        val tv_recipe_name : TextView = view.findViewById(R.id.tv_recipe_name)
        val tv_recipe_function : TextView = view.findViewById(R.id.tv_recipe_function)
        val tv_recipe_book : TextView = view.findViewById(R.id.tv_recipe_book)
    }
}
  1. Use adapters and RecyclerViewcontrols.
package com.example.myapplication.fragment

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.R
import com.example.myapplication.adapter.RecipeAdapter
import com.example.myapplication.model.Recipe
import com.example.myapplication.tools.ApiService
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory


class RecipeFragment : Fragment() {
    
    
    //控件
    private lateinit var rcv_recipe: RecyclerView
    //网络连接
    private lateinit var retrofit: Retrofit
    //网络接口
    private lateinit var api: ApiService
    //适配器
    private var recipeAdapter: RecipeAdapter? = null
    //数据列表
    private var recipeList = ArrayList<Recipe>()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    
    
        //加载布局
        val view = LayoutInflater.from(container?.context)
            .inflate(R.layout.fragment_recipe, container, false)
        //获取控件RecyclerView
        rcv_recipe = view.findViewById(R.id.rcv_recipe)
        
        initSocket()
        initData()

        return view
    }

    /**
     * 初始化网络连接
     */
    private fun initSocket() {
    
    
        // builder模式构建Retrofit对象
        retrofit = retrofit2.Retrofit.Builder()
            .baseUrl("https://www.fastmock.site/mock/d6931ad23f0e1bdb2061d1c5363c45cb/KotlinLearning/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        // 创建接口层的代理对象,内部通过动态代理创建了ApiService的代理对象
        api = retrofit.create(ApiService::class.java)
    }

    /**
     * 加载网络数据并显示
     */
    private fun initData() {
    
    
        //异步加载网络数据
        api.listRecipe().enqueue(object : Callback<List<Recipe>> {
    
    
            override fun onResponse(call: Call<List<Recipe>>, response: Response<List<Recipe>>) {
    
    
                //打印数据数量
                response.body()?.let {
    
     it1 -> Log.e("size", it1.size.toString()) }
                for (i in 0 until (response.body()?.size!!)) {
    
    
                    //循环加入数据列表
                    response.body()?.get(i)?.let {
    
     it1 ->
                        recipeList.add(it1)
                    }
                }
                //设置垂直布局
                val linearLayoutManager = LinearLayoutManager(activity)
                linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
                rcv_recipe.layoutManager = linearLayoutManager
                //添加下划线
                rcv_recipe.addItemDecoration(DividerItemDecoration(activity, LinearLayoutManager.VERTICAL))
                //加载适配器
                recipeAdapter = RecipeAdapter(recipeList)
                rcv_recipe.adapter = recipeAdapter
            }

            override fun onFailure(call: Call<List<Recipe>>, t: Throwable) {
    
    
                Log.e("onFailure", t.toString())
            }
        })
    }
}

Notice: The adapter loading is not placed onCreateViewin the middle, because of asynchronous requests, the thread of the network request may not be executed yet, and the adapter loading is carried out, resulting in the situation that there is no data, and thread synchronization is required. However, if it is placed in the network request thread, it can be guaranteed that it is in the same thread, and the adapter must be loaded after the data request is completed, and there must be data. If you don't want to put it in the same thread, then perform thread synchronization, which will be explained in number 16 below.

13. RecyclerView click event

  • RecyclerViewThe click event is itemthe click response.
  • So itemjust set a click listener for the sub-layout.
layout_recipe.setOnClickListener(View.OnClickListener {
    
    
                val intent = Intent(context,RecipeActivity::class.java)
                context.startActivity(intent)
            })
  • layout_recipeIt is itema sub-layout id, set a click event on it, and the internal code is the specific operation performed by clicking on it.

14. Data transfer (via Intent)

  1. Activitydelivered to Activity.
  • Example - the login interface ( MainActivity.kt) jumps to the main page ( HomeActivity.kt), passes the login user information and prints it in the log column.

  • MainActivity.kt

//声明Bundle传递对象
private var bundle : Bundle ?= null

//创建传递对象
                bundle = Bundle()
                //加入第一个用户数据
                response.body()?.get(0)?.id?.let {
    
     bundle?.putInt("id", it) }
                response.body()?.get(0)?.username?.let {
    
     bundle?.putString("username", it) }
                response.body()?.get(0)?.password?.let {
    
     bundle?.putString("password", it) }
                response.body()?.get(0)?.nickname?.let {
    
     bundle?.putString("nickname", it) }
                response.body()?.get(0)?.avatar?.let {
    
     bundle?.putString("avatar", it) }
                response.body()?.get(0)?.sex?.let {
    
     bundle?.putString("sex", it) }
                response.body()?.get(0)?.age?.let {
    
     bundle?.putInt("age", it) }
                response.body()?.get(0)?.phone?.let {
    
     bundle?.putString("phone", it) }
                response.body()?.get(0)?.email?.let {
    
     bundle?.putString("email", it) }
                response.body()?.get(0)?.islogin?.let {
    
     bundle?.putString("islogin", it) }
                //创建跳转对象
                intent = Intent(this@MainActivity, HomeActivity::class.java)
                //传递对象不为空,跳转对象不为空,就存入
                bundle?.let {
    
     intent?.putExtras(it) }
                //进行页面跳转
                startActivity(intent)
  • Here, the jump is made in the network request data thread, because of the problem of thread synchronization. If you don't jump here, you can't ensure that bundledata must exist in the object. And since the jump is not performed in the main thread, it is necessary to use @MainActivitythe notification Intentobject where the starting point of the jump is located MainActivity.
  • HomeActivity.kt
//获取传递过来的数据存储对象
        //自动判断类型,无需手动书写
        var bundle = this.intent.extras
        var sb = StringBuilder()
        sb.append(bundle?.getInt("id"))
        sb.append(bundle?.getString("username"))
        sb.append(bundle?.getString("password"))
        sb.append(bundle?.getString("nickname"))
        sb.append(bundle?.getString("avatar"))
        sb.append(bundle?.getString("sex"))
        sb.append(bundle?.getInt("age"))
        sb.append(bundle?.getString("phone"))
        sb.append(bundle?.getString("email"))
        sb.append(bundle?.getString("islogin"))
        Log.e("bundle",sb.toString())
  • KotlinIn the language, numbers Stringcannot be used +for concatenation, so it is used here StringBuilderto concatenate strings.Note: If you need thread safety, you can use StringBufferto connect strings.
  1. Activitydelivered to Fragment.
  • Example - the main page ( HomeActivity.kt) transfers the login user information to the personal home page (that is, the last one Fragment, ie MyFragment.kt), and then displays the user information on the personal main page.

  • MainActivity.kt

//声明Bundle传递对象
private var bundle : Bundle ?= null

//获取传递过来的数据存储对象
        //自动判断类型,无需手动书写
        bundle = this.intent.extras
        
private fun initMyFragment() {
    
    
        var fragmentTransaction : FragmentTransaction = supportFragmentManager.beginTransaction()
        myFragment?:let {
    
    
            myFragment = MyFragment()
            //将数据传递给fragment
            myFragment?.arguments = bundle
            fragmentTransaction.add(R.id.fragment,myFragment!!)
        }
        //隐藏事务中所有的Fragment
        hideAllFragment(fragmentTransaction)
        //显示需要显示的Fragment
        myFragment?.let {
    
     fragmentTransaction.show(it) }
        //提交事务,事务一定要提交,不然无效
        fragmentTransaction.commit()
    }
  • bundleActivityIt is passed by the user from the login page . ActivityAfter receiving it on the main page, it is passed directly to the website Fragmentwithout any other processing. (PS: Whichever Fragmentneeds data, just assign it in whichever Fragmentone arguments)

  • The specific data format passed is 1the data format passed in the first point.

  • MyFragment.kt

package com.example.myapplication.fragment

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.myapplication.R
import kotlinx.android.synthetic.main.fragment_my.*
import java.lang.StringBuilder

class MyFragment : Fragment() {
    
    
    //声明Bundle存储对象
    private var bundle : Bundle ?= null
    private var sb : StringBuilder ?= null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    
    
        //获取数据存储对象
        bundle = this.arguments
        //连接字符串
        sb = StringBuilder()
        sb?.append(bundle?.getInt("id"))
        sb?.append(",")
        sb?.append(bundle?.getString("username"))
        sb?.append(",")
        sb?.append(bundle?.getString("password"))
        sb?.append(",")
        sb?.append(bundle?.getString("nickname"))
        sb?.append(",")
        sb?.append(bundle?.getString("avatar"))
        sb?.append(",")
        sb?.append(bundle?.getString("sex"))
        sb?.append(",")
        sb?.append(bundle?.getInt("age"))
        sb?.append(",")
        sb?.append(bundle?.getString("phone"))
        sb?.append(",")
        sb?.append(bundle?.getString("email"))
        sb?.append(",")
        sb?.append(bundle?.getString("islogin"))

        return inflater.inflate(R.layout.fragment_my, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
    
        super.onViewCreated(view, savedInstanceState)
        //传递过来的数据显示在界面上
        tv_test.text = sb.toString()
    }
}
  • Here, the direct operation control Fragmentis called in idand needs to be Viewreturned, so onViewCreatedit is used in the method, because onViewCreatedthe method is onCreateViewexecuted after the method.
  • Another way is to load Fragmentthe layout file first, and then findViewByIdget the control through it.
  • The renderings are as follows:
    insert image description here
  1. The transfer of the above serial number 13is itemto click to an activityinterface, so the data is Fragmenttransferred from to Activity.
  • Take the click event above as an example, click item, jump to another interface, and pass the prescription data.

  • RecipeAdapter.kt

//页面跳转逻辑
                var intent = Intent(context,RecipeActivity::class.java)
                //数据存储对象
                var bundle = Bundle()
                //将数据保存进去
                bundle.putInt("id",item.id)
                bundle.putString("name",item.name)
                bundle.putString("song",item.song)
                bundle.putString("medicines",item.medicines)
                bundle.putString("function",item.function)
                bundle.putString("cure",item.cure)
                bundle.putString("type",item.type)
                bundle.putString("childtype",item.childtype)
                bundle.putString("book",item.book)
                //将数据对象保存进跳转对象
                intent.putExtras(bundle)
                //实现跳转传递
                context.startActivity(intent)
  • RecipeActivity.kt
//获取传递过来的数据对象
        var bundle = this.intent.extras
        //将数据放入文本控件显示
        tv_id.text =  bundle?.getInt("id").toString()
        tv_name.text = bundle?.getString("name")
        tv_song.text = bundle?.getString("song")
        tv_medicines.text = bundle?.getString("medicines")
        tv_function.text = bundle?.getString("function")
        tv_cure.text = bundle?.getString("cure")
        tv_type.text = bundle?.getString("type")
        tv_childtype.text = bundle?.getString("childtype")
        tv_book.text = bundle?.getString("book")
  • The renderings are as follows:
    insert image description here
  1. Fragmentdelivered to Fragment.
  • ActivityThe data located in the same Fragmentplace can be Activitypassed through; the data located in different Activityplaces Fragmentcan be passed through Fragmentitself and Activityas an intermediary.
  • In summary, this transfer process is the combination of the first three processes.

15. Data return

  • The data return must pass back the modified data. If there is no modification, the return is meaningless.

16. Thread synchronization

  • The meaning of thread synchronization: no matter how many threads execute, ensure that the final result is determinable, that is, no matter how it is executed, the final result remains unchanged.

17. Data Storage——Five Storages

  1. SharedPreferences
  2. file storage
  3. SQLite database
  4. The content provider of the four major components (Content Provider)
  5. Network Storage

18. Four major components

  1. Activity
  2. Service
  3. Broadcast Receiver
  4. Content Provider

19. Six layouts

  1. LinearLayout (LinearLayout)
  2. Table layout (TableLayout)
  3. Frame Layout (FrameLayout)
  4. Relative Layout (RelativeLayout)
  5. Grid Layout (GridLayout)
  6. AbsoluteLayout (AbsoluteLayout)

20. Use of Glide framework

  1. Import dependencies.
implementation 'com.github.bumptech.glide:glide:4.11.0'  //Glide
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
  1. Join the network to request permission.
<uses-permission android:name="android.permission.INTERNET" />
  1. Use it for image loading.
//加载控件
        Glide.with(img_avatar)
            //形成bitmap类型
            .asBitmap()
            //加载图片网址
            .load(bundle?.getString("avatar"))
            //预加载
            .placeholder(R.mipmap.person_selected)
            //加载错误显示图片
            .error(R.drawable.btn_background)
            //放入控件
            .into(img_avatar)
activity?.let {
    
    
            Glide.with(it)
                //形成bitmap类型
                .asBitmap()
                //加载图片网址
                .load(bundle?.getString("avatar"))
                //预加载
                .placeholder(R.mipmap.person_selected)
                //加载错误显示图片
                .error(R.drawable.btn_background)
                //放入控件
                .into(img_avatar)
        }
  • The renderings are as follows:insert image description here

21. Circular image

  • Use the circular image open source library ( CircleImageView).
  1. Import dependencies.
implementation 'de.hdodenhof:circleimageview:3.1.0'
  1. In the layout file, just ImageViewchange the control to de.hdodenhof.circleimageview.CircleImageViewcontrol.
  • The renderings are as follows:
    insert image description here

22. Inner classes

  • If it is not written inside the class, it is an inner class.
  • A class written inside a class cannot call member variables of a large class, so it is not an inner class.
  • Inner classes need to be innerspecified with keywords.
private inner class MyCallBack : SurfaceHolder.Callback {
    
    
        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
    
    

        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {
    
    

        }

        override fun surfaceCreated(holder: SurfaceHolder) {
    
    
            mediaPlayer?.setDisplay(holder)
        }
    }

23. MediaPlayer + SurfaceView play local video

  • Put video assets rawin folders.
  • MainActivity.java
package com.example.appvideo

import android.media.MediaPlayer
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.SurfaceHolder
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    
    
    private var mediaPlayer : MediaPlayer? = null
    private var holder : SurfaceHolder? = null

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initMediaPlayer()
    }

    private fun initMediaPlayer() {
    
    
        //本地视频
        //创建MediaPlayer对象
        mediaPlayer = MediaPlayer.create(this,R.raw.video)
        //获取SurfaceHolder对象
        holder = surfaceView?.holder
        //实现接口回调
        holder?.addCallback(MyCallBack())
        //开始播放
        mediaPlayer?.start()
        //开启循环播放
        mediaPlayer?.isLooping = true
        //隐藏加载按钮
        progressBar?.visibility = View.INVISIBLE
    }

    private inner class MyCallBack : SurfaceHolder.Callback {
    
    
        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
    
    

        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {
    
    

        }

        override fun surfaceCreated(holder: SurfaceHolder) {
    
    
            mediaPlayer?.setDisplay(holder)
        }
    }
}
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        />
    
</RelativeLayout>

24. MediaPlayer + SurfaceView play network video

Reference Blog: Implementing Network Video Playback

  • Put video assets on the web.
  • MainActivity.java
package com.example.appvideo

import android.media.MediaPlayer
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.SurfaceHolder
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    
    
    private var mediaPlayer : MediaPlayer? = null
    private var holder : SurfaceHolder? = null
    //网络地址
    private val uri : String = "https://media.w3.org/2010/05/sintel/trailer.mp4"

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //初始化播放器
        initMediaPlayer()
    }

    private fun initMediaPlayer() {
    
    
        //网络视频
        mediaPlayer = MediaPlayer()
        mediaPlayer?.setDataSource(this, Uri.parse(uri))
        holder = surfaceView?.holder
        holder?.addCallback(MyCallBack())
        //异步加载
        mediaPlayer?.prepareAsync()
        mediaPlayer?.setOnPreparedListener {
    
    
            //加载完毕,加载按钮隐藏
            progressBar?.visibility = View.INVISIBLE
            //开始播放
            mediaPlayer?.start()
            //循环播放
            mediaPlayer?.isLooping = true
        }
    }

    private inner class MyCallBack : SurfaceHolder.Callback {
    
    
        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
    
    

        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {
    
    

        }

        override fun surfaceCreated(holder: SurfaceHolder) {
    
    
            mediaPlayer?.setDisplay(holder)
        }
    }
}
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        />

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        />

</RelativeLayout>

25. Get the current timestamp

//系统
val currentTimeMillis = System.currentTimeMillis()
//Date类
val timeGetTime = Date().time
//Calendar类
val timeMillis = Calendar.getInstance().timeInMillis

Guess you like

Origin blog.csdn.net/qq_46546793/article/details/126231929