【Android】Kotlin学习(一)
目录
- 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: Retrofitprivate 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 : HomeFragmentinit {homeFragment3 = HomeFragment()}
常量要么在声明时直接初始化,要么在构造函数
init
中初始化。
9. 你真的理解全局变量吗?
C
语言提供了全局变量的定义,其又称:外部变量。- 顾名思义,定义在函数外的变量,称为外部变量。
- 它的作用域是整个源程序,所以可以在整个源程序的任意位置访问全局变量。
- 其不仅仅是在一个类中,叫全局变量;而是在整个源程序中。
- 全局变量位于整个源程序中,所以它们同生共死,只要程序没有停止,它就不会消失。
10. 闭包概念
- 闭包是一个能够访问其他函数内部变量的函数。
11. Fragment使用
- 例子——在
Activity
中结合Fragment
显示界面。 - 逻辑都存在于
Activity
中,因为Fragment
的跳转等都是在Activity
中执行的。
- 声明需要显示的
Fragment
。
private var homeFragment : HomeFragment? = nullprivate var recipeFragment : RecipeFragment? = nullprivate var discussionFragment : DiscussionFragment? = nullprivate 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 = truell_recipe?.isSelected = falsell_discussion?.isSelected = falsell_person?.isSelected = falseinitHomeFragment()
- 显示
Fragment
。
- 注意:每一个
Fragment
显示都需要一个新的事务,因为事务提交了,就失效了,需要一个新的。
private fun initHomeFragment() {//每一个Fragment都需要一个单独的事务//因为提交后,事务虽然创建方式一样,但是本质已经不同var fragmentTransaction : FragmentTransaction = supportFragmentManager.beginTransaction()//若是homeFragment为空homeFragment?:let {homeFragment = HomeFragment()fragmentTransaction.add(R.id.fragment, homeFragment!!)}//隐藏事务中所有的FragmenthideAllFragment(fragmentTransaction)//显示需要显示的FragmenthomeFragment?.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 = truell_recipe?.isSelected = falsell_discussion?.isSelected = falsell_person?.isSelected = falseinitHomeFragment()}R.id.ll_recipe -> {ll_home?.isSelected = falsell_recipe?.isSelected = truell_discussion?.isSelected = falsell_person?.isSelected = falseinitRecipeFragment()}R.id.ll_discussion -> {ll_home?.isSelected = falsell_recipe?.isSelected = falsell_discussion?.isSelected = truell_person?.isSelected = falseinitDiscussionFragment()}R.id.ll_person -> {ll_home?.isSelected = falsell_recipe?.isSelected = falsell_discussion?.isSelected = falsell_person?.isSelected = trueinitMyFragment()}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"><TextViewandroid: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" /><TextViewandroid: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" /><TextViewandroid: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.adapterimport 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.nametv_recipe_function.text = item.functiontv_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.fragmentimport 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.GsonConverterFactoryclass 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)//获取控件RecyclerViewrcv_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.VERTICALrcv_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.extrasvar 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.extrasprivate fun initMyFragment() {var fragmentTransaction : FragmentTransaction = supportFragmentManager.beginTransaction()myFragment?:let {myFragment = MyFragment()//将数据传递给fragmentmyFragment?.arguments = bundlefragmentTransaction.add(R.id.fragment,myFragment!!)}//隐藏事务中所有的FragmenthideAllFragment(fragmentTransaction)//显示需要显示的FragmentmyFragment?.let { fragmentTransaction.show(it) }//提交事务,事务一定要提交,不然无效fragmentTransaction.commit()}
bundle
是用户从登录页面Activity
传递过来的,主页面Activity
接收一下,直接就传递给了Fragment
,没有经过其他处理。(PS:哪个Fragment
需要数据,就在哪个Fragment
的arguments
中赋值即可)传递过来的具体数据格式即为第
1
小点中传递过来的数据格式。MyFragment.kt
package com.example.myapplication.fragmentimport 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.StringBuilderclass MyFragment : Fragment() {//声明Bundle存储对象private var bundle : Bundle ?= nullprivate var sb : StringBuilder ?= nulloverride 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.appvideoimport 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? = nullprivate var holder : SurfaceHolder? = nulloverride 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"><SurfaceViewandroid: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.appvideoimport 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? = nullprivate 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?.holderholder?.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"><SurfaceViewandroid:id="@+id/surfaceView"android:layout_width="match_parent"android:layout_height="200dp"android:layout_centerInParent="true"/><ProgressBarandroid: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
【Android】Kotlin学习(一)相关推荐
- Android Kotlin学习笔记(一)—— Kotlin Koans
文章目录 题记 1.资料阅读 2.辅助插件 3.习题演练 3.1 Introduction 1.Hello world 2.Java to Kotlin conversion 3.Named argu ...
- Android Kotlin 学习总结(一) 《KAE 优缺点并且深入字节码分析工作原理》
本章会分为以下内容: 1.Kotlin KAE介绍,使用和原始Android findViewById对比优缺点 2.Kotlin KAE所存在的问题 3.通过字节码分析他的实现原理 阅读本章内容大概 ...
- Android kotlin run函数学习
继续来看一下kotlin中run函数的应用,首先看一下源码: /*** Calls the specified function [block] and returns its result.** F ...
- Android Binder 学习笔记
前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...
- Kotlin学习笔记(1)- 环境配置
系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...
- Android JNI学习(六)——Java与Native实战演习
前言: 前几篇我主要介绍了jni先关的基础知识和常用API,相信看过的童靴对JNI已经有了一定的了解,如果不了解也没关系,下面我给出了链接,可以点进去学习.接下来我将实战一个完整案例,案例很简单,就是 ...
- Android Kotlin之Flow数据流
文章目录 Flow介绍 使用举例 常用操作符 创建操作符 回调操作符 变换操作符 过滤操作符 组合操作符 功能性操作符 末端操作符 冷流 vs 热流 SharedFlow shareIn将普通flow ...
- Kotlin学习笔记12——数据类和密封类
Kotlin学习笔记12--数据类和密封类 前言 数据类 在类体中声明的属性 复制 componentN 解构声明 密封类 尾巴 前言 上一篇,我们学习了Kotlin中的拓展,今天继续来学习Kotli ...
- Kotlin学习安卓篇(一)为什么要学习Kotlin?
一.学前问题 1.Kotlin的身世背景?它是干什么的? 2.Kotlin和Java比较如何?优缺点?相似度?为什么很多大厂都使用它? 3.Android开发是否要掌握Java和Kotlin? 4.对 ...
最新文章
- 建立Windows Embedded Compact 7开发环境
- 那是两个小时我不会回来
- volatile关键字及编译器指令乱序总结
- c语言中格式化字符串系列函数包括,解析C语言中常用的格式化输入、输出函数...
- 地方商城门户网页模板-商城模板
- 中南大学计算机学院夏令营2021,baihhh
- emqx http not found 怎么回事_幽默笑话:行倒是行,但两个大男人这么说话算怎么回事...
- 网页编辑PHP变量,编辑文件中的php代码和变量
- spring boot 使用 websocket tomcat刚启动就关闭到问题
- 如何从技术上“绞杀”盗链者?
- java WebSocket的实现以及Spring WebSocket
- 4_while循环结构和breakcontinue
- pandas获取dataframe的行数,列数,元素个数
- mysql 触发器详情
- cadence allegro - PCB线路敷铜渲染
- CentOS 7 忘记root密码重置密码
- 好数推荐 数据堂平均音色语音库
- iOS开发实战之手机号、座机号正则验证
- java 事物 notsupport_Spring事务传播属性介绍(二).mandatory、not_supported、never、supports...
- HAWKER叉车蓄电池 FLEX LI3 现已通过 UL 2580 认证 霍克叉车电瓶