目录
- Android——Kotlin学习
- 1. 使用控件id直接使用控件
- 2. 继承类实现接口
- 3. 点击事件监听
- 4. 点击事件接口实现
- 5. 页面跳转
- 6. 文本操作
- 7. 网络请求数据
- 8. Kotlin的变量、常量
- 9. 你真的理解全局变量吗?
- 10. 闭包概念
- 11. Fragment使用
- 12. RecyclerView使用
- 13. RecyclerView点击事件
- 14. 数据传递(通过Intent)
- 15. 数据回传
- 16. 线程同步
- 17. 数据存储——五大存储
- 18. 四大组件
- 19. 六大布局
- 20. Glide框架的使用
- 21. 圆形图片
- 22. 内部类
- 23. MediaPlayer + SurfaceView播放本地视频
- 24. MediaPlayer + SurfaceView播放网络视频
- 25. 获取当前时间戳
不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!
Android——Kotlin学习
1. 使用控件id直接使用控件
- 在
build.gradle
文件中的plugins
代码块中加入以下语句。
id 'kotlin-android-extensions'
- 在逻辑文件中,声明其布局文件。
import kotlinx.android.synthetic.main.activity_main.*
- 在后续使用
Fragment
进行学习时,遇到了直接使用控件id
来使用控件的问题。其总是报空指针,加入?.
后,虽然不会出现崩溃的情况,但是却也不能显示出控件,证明其确实未加载。 - 百思不得其解,通过学习后发现:在
Kotlin
中直接使用id
操作控件的情况,必须在布局文件加载完毕后使用才可以,因为只有加载完毕了,其才存在;布局文件都没加载,如何来的id
,怎么去使用,肯定是报空的。所以在Fragment
中,我们习惯性在onCreateView
方法中使用id
来操作控件肯定是会报空的,因为view
都还没有return
。
2. 继承类实现接口
- 冒号后继承和实现,带有
()
的属于类,是继承;没有()
的属于接口,是实现。
class MainActivity : AppCompatActivity(), View.OnClickListener{...}
3. 点击事件监听
- 和
Java
语法一致。
fun initClick() {
//跳转页面功能
btn_register.setOnClickListener(this)
//网络请求功能
btn_login.setOnClickListener(this)
//文本控件操作功能
btn_reset.setOnClickListener(this)
}
4. 点击事件接口实现
- 不再是使用
switch
了,而是使用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. 页面跳转
- 依然是用
Intent
进行界面跳转。
val intent = Intent(this, HomeActivity::class.java)
//进行页面跳转
startActivity(intent)
6. 文本操作
get
类方法,直接属性调用,都不需要get
了;set
类方法可有两种操作。
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. 网络请求数据
- 使用
Retrofit
进行网络数据的请求。
- 创建数据对象。
/** 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
)
- 创建代理接口。
/** 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>>
}
- 创建网络连接。
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)
}
- 进行网络请求。
/**
* 网络请求得到数据的具体实现
*/
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. Kotlin的变量、常量
- 以
Fragment
为例子,进行常量变量总结。
- 变量(延迟加载)——
lateinit var
- 变量可以读写,可以修改赋值。
private lateinit var homeFragment: HomeFragment
变量延迟加载,是声明变量时不加载,使用其时才加载,即懒汉式加载。无论其有无声明,在没有创建前使用它,会报错。
if(homeFragment == null){
homeFragment = HomeFragment()
}
上述代码中,未创建
homeFragment
,就使用其来判断是否为空,故会报错。
- 正确变量写法。
private var myFragment : MyFragment? = null
?
代表该对象可为空。
- 为空时执行。
myFragment?:let {
myFragment = MyFragment()
fragmentTransaction.add(R.id.fragment,myFragment!!)
}
- 不为空时执行。
myFragment?.let { fragmentTransaction.show(it) }
- 常量——
val
- 就是一个只可读变量,只可以初始化一次。
private val homeFragment : HomeFragment = HomeFragment()
private val homeFragment3 : HomeFragment
init {
homeFragment3 = HomeFragment()
}
常量要么在声明时直接初始化,要么在构造函数
init
中初始化。
9. 你真的理解全局变量吗?
C
语言提供了全局变量的定义,其又称:外部变量。- 顾名思义,定义在函数外的变量,称为外部变量。
- 它的作用域是整个源程序,所以可以在整个源程序的任意位置访问全局变量。
- 其不仅仅是在一个类中,叫全局变量;而是在整个源程序中。
- 全局变量位于整个源程序中,所以它们同生共死,只要程序没有停止,它就不会消失。
10. 闭包概念
- 闭包是一个能够访问其他函数内部变量的函数。
11. Fragment使用
- 例子——在
Activity
中结合Fragment
显示界面。 - 逻辑都存在于
Activity
中,因为Fragment
的跳转等都是在Activity
中执行的。
- 声明需要显示的
Fragment
。
private var homeFragment : HomeFragment? = null
private var recipeFragment : RecipeFragment? = null
private var discussionFragment : DiscussionFragment? = null
private var myFragment : MyFragment? = null
- 创建底部栏监听。
/**
* 设置底部栏监听
*/
private fun initClick() {
ll_home.setOnClickListener(this)
ll_recipe.setOnClickListener(this)
ll_discussion.setOnClickListener(this)
ll_person.setOnClickListener(this)
}
- 初始化时,底部栏变化,显示第一个
Fragment
。
//创建的时候显示首页
ll_home?.isSelected = true
ll_recipe?.isSelected = false
ll_discussion?.isSelected = false
ll_person?.isSelected = false
initHomeFragment()
- 显示
Fragment
。
- 注意:每一个
Fragment
显示都需要一个新的事务,因为事务提交了,就失效了,需要一个新的。
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()
}
- 隐藏所有的
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!!) }
}
- 点击底部栏,显示不同的
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使用
- 例子——利用
RecyclerView
实现方剂数据的展示。 - 效果图如下:
- 准备数据类。
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
)
- 准备后端接口。
@GET("recipe")
fun listRecipe() : Call<List<Recipe>>
- 准备布局和
item
布局。
- 布局只有一个
RecyclerView
,子布局如下:
<?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>
- 创建
item
适配器。
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)
}
}
- 使用适配器和
RecyclerView
控件。
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())
}
})
}
}
注意:适配器加载不放在onCreateView
中,是因为异步请求,可能网络请求的线程还没有执行完毕,就进行了适配器加载,导致出现没有数据的情况,需要进行线程同步。但是放在网络请求线程中,可以保证其在同一个线程,必然在数据请求完毕后,再进行适配器的加载,必然有数据。若是就不要放在同一个线程中,那么就进行线程的同步,这个在下文序号16进行阐述。
13. RecyclerView点击事件
RecyclerView
的点击事件就是item
的点击响应。- 所以将
item
的子布局设置一个点击监听即可。
layout_recipe.setOnClickListener(View.OnClickListener {
val intent = Intent(context,RecipeActivity::class.java)
context.startActivity(intent)
})
layout_recipe
是item
子布局的id
,对其设置点击事件,内部代码为点击其所执行的具体操作。
14. 数据传递(通过Intent)
Activity
传递到Activity
。
-
例子——登录界面(
MainActivity.kt
)跳转到主页面(HomeActivity.kt
),传递过去登录用户信息并打印在日志栏中。 -
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)
- 此处在网络请求数据线程中进行跳转,原因还是因为线程同步的问题。要是不在此处跳转,无法确保
bundle
对象中一定存在数据。而且由于不在主线程进行跳转,所以要使用@MainActivity
告知Intent
对象跳转的起点位于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())
Kotlin
语言中,String
无法使用+
号进行连接,因此此处使用StringBuilder
来连接字符串。注:要是需要线程安全的,可以使用StringBuffer
来连接字符串。
Activity
传递到Fragment
。
-
例子——主页面(
HomeActivity.kt
)传递登录用户信息到个人主页(即最后一个Fragment
,即MyFragment.kt
),然后在个人主页面进行用户信息显示。 -
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
传递过来的,主页面Activity
接收一下,直接就传递给了Fragment
,没有经过其他处理。(PS:哪个Fragment
需要数据,就在哪个Fragment
的arguments
中赋值即可) -
传递过来的具体数据格式即为第
1
小点中传递过来的数据格式。 -
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()
}
}
- 此处在
Fragment
中调用id
直接操作控件,需要在View
返回之后,所以在onViewCreated
方法中使用,因为onViewCreated
方法在onCreateView
方法之后执行。 - 还有一种方式,就是先加载
Fragment
的布局文件,然后通过findViewById
得到控件。 - 效果图如下:
- 上述序号
13
的传递是item
点击到一个activity
界面,所以数据是从Fragment
传递到Activity
。
-
以上例点击事件为例子,点击
item
,跳转到另一个界面,并传递过去方剂数据。 -
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")
- 效果图如下:
Fragment
传递到Fragment
。
- 位于同一个
Activity
的Fragment
的数据,可以通过Activity
进行传递;位于不同Activity
的Fragment
,可以通过Fragment
本身和Activity
作为中间媒介进行传递。 - 总结来说,这一种传递过程,就是前三种过程的结合实现。
15. 数据回传
- 数据回传必定是传递回去修改后的数据,若是没有修改,回传就没有意义。
16. 线程同步
- 线程同步的含义:无论多少个线程执行,确保最后的结果是可确定的,即无论怎么执行,最后的结果是不变的。
17. 数据存储——五大存储
- SharedPreferences
- 文件存储
- SQLite数据库
- 四大组件之内容提供器(Content Provider)
- 网络存储
18. 四大组件
- Activity
- Service
- Broadcast Receiver
- Content Provider
19. 六大布局
- 线性布局(LinearLayout)
- 表格布局(TableLayout)
- 帧布局(FrameLayout)
- 相对布局(RelativeLayout)
- 网格布局(GridLayout)
- 绝对布局(AbsoluteLayout)
20. Glide框架的使用
- 导入依赖。
implementation 'com.github.bumptech.glide:glide:4.11.0' //Glide
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
- 加入网络请求权限。
<uses-permission android:name="android.permission.INTERNET" />
- 使用其进行图片加载。
//加载控件
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)
}
- 效果图如下:
21. 圆形图片
- 利用圆形图片开源库(
CircleImageView
)。
- 导入依赖。
implementation 'de.hdodenhof:circleimageview:3.1.0'
- 在布局文件中,将
ImageView
控件改为de.hdodenhof.circleimageview.CircleImageView
控件即可。
- 效果图如下:
22. 内部类
- 非写在类的内部就是内部类。
- 写在类内部的类,无法调用大类的成员变量,所以其不算内部类。
- 内部类需要加
inner
关键词指定。
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播放本地视频
- 将视频资源放在
raw
文件夹中。 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播放网络视频
参考博客:实现网络视频播放
- 将视频资源放在网络中。
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. 获取当前时间戳
//系统
val currentTimeMillis = System.currentTimeMillis()
//Date类
val timeGetTime = Date().time
//Calendar类
val timeMillis = Calendar.getInstance().timeInMillis