Table of contents
- Android——Kotlin learning
-
- 1. Use the control id to directly use the control
- 2. Inherited class implements interface
- 3. Click event monitoring
- 4. Click event interface implementation
- 5. Page Jump
- 6. Text manipulation
- 7. Network request data
- 8. Variables and constants in Kotlin
- 9. Do you really understand global variables?
- 10. Closure concept
- 11. Fragment use
- 12. RecyclerView use
- 13. RecyclerView click event
- 14. Data transfer (via Intent)
- 15. Data return
- 16. Thread synchronization
- 17. Data Storage——Five Storages
- 18. Four major components
- 19. Six layouts
- 20. Use of Glide framework
- 21. Circular image
- 22. Inner classes
- 23. MediaPlayer + SurfaceView play local video
- 24. MediaPlayer + SurfaceView play network video
- 25. Get the current timestamp
不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!
Android——Kotlin learning
1. Use the control id to directly use the control
- Include the following statement in the code block in
build.gradle
the fileplugins
.
id 'kotlin-android-extensions'
- In the logic file, declare its layout file.
import kotlinx.android.synthetic.main.activity_main.*
- In the follow-up
Fragment
study, I encounteredid
the 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
Kotlin
usingid
the 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 comeid
, how to use it, it must be empty. So inFragment
, if we habituallyonCreateView
use it in the methodid
to operate the control, it will definitely report empty, becauseview
there is none yetreturn
.
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
Java
the 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 usingwhen
.
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
Intent
to jump to the interface.
val intent = Intent(this, HomeActivity::class.java)
//进行页面跳转
startActivity(intent)
6. Text manipulation
get
There is no need for class methods and direct attribute callsget
;set
class 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
Retrofit
make network data requests.
- 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
)
- 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>>
}
- 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)
}
- 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
- As
Fragment
an example, summarize the constant variables.
- 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) }
- 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?
C
The 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 -
Activity
IncorporateFragment
display interface in . - The logic exists
Activity
in , becauseFragment
the jumps and so on are allActivity
executed in .
- 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
- Create a bottom bar listener.
/**
* 设置底部栏监听
*/
private fun initClick() {
ll_home.setOnClickListener(this)
ll_recipe.setOnClickListener(this)
ll_discussion.setOnClickListener(this)
ll_person.setOnClickListener(this)
}
- 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()
- display
Fragment
.
- Notice: Each
Fragment
display 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()
}
- 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!!) }
}
- 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
RecyclerView
to realize the display of prescription data. - The renderings are as follows:
- 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
)
- Prepare the backend interface.
@GET("recipe")
fun listRecipe() : Call<List<Recipe>>
- Prepare layout and
item
layout.
- 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>
- Create
item
an 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)
}
}
- Use adapters and
RecyclerView
controls.
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 onCreateView
in 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
RecyclerView
The click event isitem
the click response.- So
item
just set a click listener for the sub-layout.
layout_recipe.setOnClickListener(View.OnClickListener {
val intent = Intent(context,RecipeActivity::class.java)
context.startActivity(intent)
})
layout_recipe
It isitem
a sub-layoutid
, set a click event on it, and the internal code is the specific operation performed by clicking on it.
14. Data transfer (via Intent)
Activity
delivered toActivity
.
-
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
bundle
data must exist in the object. And since the jump is not performed in the main thread, it is necessary to use@MainActivity
the notificationIntent
object where the starting point of the jump is locatedMainActivity
. 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())
Kotlin
In the language, numbersString
cannot be used+
for concatenation, so it is used hereStringBuilder
to concatenate strings.Note: If you need thread safety, you can useStringBuffer
to connect strings.
Activity
delivered toFragment
.
-
Example - the main page (
HomeActivity.kt
) transfers the login user information to the personal home page (that is, the last oneFragment
, ieMyFragment.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()
}
-
bundle
Activity
It is passed by the user from the login page .Activity
After receiving it on the main page, it is passed directly to the websiteFragment
without any other processing. (PS: WhicheverFragment
needs data, just assign it in whicheverFragment
onearguments
) -
The specific data format passed is
1
the 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
Fragment
is called inid
and needs to beView
returned, soonViewCreated
it is used in the method, becauseonViewCreated
the method isonCreateView
executed after the method. - Another way is to load
Fragment
the layout file first, and thenfindViewById
get the control through it. - The renderings are as follows:
- The transfer of the above serial number
13
isitem
to click to anactivity
interface, so the data isFragment
transferred from toActivity
.
-
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:
Fragment
delivered toFragment
.
Activity
The data located in the sameFragment
place can beActivity
passed through; the data located in differentActivity
placesFragment
can be passed throughFragment
itself andActivity
as 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
- SharedPreferences
- file storage
- SQLite database
- The content provider of the four major components (Content Provider)
- Network Storage
18. Four major components
- Activity
- Service
- Broadcast Receiver
- Content Provider
19. Six layouts
- LinearLayout (LinearLayout)
- Table layout (TableLayout)
- Frame Layout (FrameLayout)
- Relative Layout (RelativeLayout)
- Grid Layout (GridLayout)
- AbsoluteLayout (AbsoluteLayout)
20. Use of Glide framework
- Import dependencies.
implementation 'com.github.bumptech.glide:glide:4.11.0' //Glide
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
- Join the network to request permission.
<uses-permission android:name="android.permission.INTERNET" />
- 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:
21. Circular image
- Use the circular image open source library (
CircleImageView
).
- Import dependencies.
implementation 'de.hdodenhof:circleimageview:3.1.0'
- In the layout file, just
ImageView
change the control tode.hdodenhof.circleimageview.CircleImageView
control.
- The renderings are as follows:
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
inner
specified 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
raw
in 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