目录

  • 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进行网络数据的请求。
  1. 创建数据对象。
/** 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. 创建代理接口。
/** 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. 创建网络连接。
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)}
  1. 进行网络请求。
/*** 网络请求得到数据的具体实现*/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为例子,进行常量变量总结。
  1. 变量(延迟加载)——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) }
  1. 常量——val
  • 就是一个只可读变量,只可以初始化一次。
private val homeFragment : HomeFragment = HomeFragment()
private val homeFragment3 : HomeFragmentinit {homeFragment3 = HomeFragment()}

常量要么在声明时直接初始化,要么在构造函数init中初始化。

9. 你真的理解全局变量吗?

  • C语言提供了全局变量的定义,其又称:外部变量。
  • 顾名思义,定义在函数外的变量,称为外部变量。
  • 它的作用域是整个源程序,所以可以在整个源程序的任意位置访问全局变量。
  • 其不仅仅是在一个类中,叫全局变量;而是在整个源程序中。
  • 全局变量位于整个源程序中,所以它们同生共死,只要程序没有停止,它就不会消失。

10. 闭包概念

  • 闭包是一个能够访问其他函数内部变量的函数。

11. Fragment使用

  • 例子——在Activity中结合Fragment显示界面。
  • 逻辑都存在于Activity中,因为Fragment的跳转等都是在Activity中执行的。
  1. 声明需要显示的Fragment
private var homeFragment : HomeFragment? = nullprivate var recipeFragment : RecipeFragment? = nullprivate var discussionFragment : DiscussionFragment? = nullprivate var myFragment : MyFragment? = null
  1. 创建底部栏监听。
 /*** 设置底部栏监听*/private fun initClick() {ll_home.setOnClickListener(this)ll_recipe.setOnClickListener(this)ll_discussion.setOnClickListener(this)ll_person.setOnClickListener(this)}
  1. 初始化时,底部栏变化,显示第一个Fragment
//创建的时候显示首页ll_home?.isSelected = truell_recipe?.isSelected = falsell_discussion?.isSelected = falsell_person?.isSelected = falseinitHomeFragment()
  1. 显示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()}
  1. 隐藏所有的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. 点击底部栏,显示不同的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实现方剂数据的展示。
  • 效果图如下:
  1. 准备数据类。
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. 准备后端接口。
@GET("recipe")fun listRecipe() : Call<List<Recipe>>
  1. 准备布局和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>
  1. 创建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)}
}
  1. 使用适配器和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_recipeitem子布局的id,对其设置点击事件,内部代码为点击其所执行的具体操作。

14. 数据传递(通过Intent)

  1. 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来连接字符串。
  1. 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需要数据,就在哪个Fragmentarguments中赋值即可)

  • 传递过来的具体数据格式即为第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得到控件。
  • 效果图如下:
  1. 上述序号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")
  • 效果图如下:
  1. Fragment传递到Fragment
  • 位于同一个ActivityFragment的数据,可以通过Activity进行传递;位于不同ActivityFragment,可以通过Fragment本身和Activity作为中间媒介进行传递。
  • 总结来说,这一种传递过程,就是前三种过程的结合实现。

15. 数据回传

  • 数据回传必定是传递回去修改后的数据,若是没有修改,回传就没有意义。

16. 线程同步

  • 线程同步的含义:无论多少个线程执行,确保最后的结果是可确定的,即无论怎么执行,最后的结果是不变的。

17. 数据存储——五大存储

  1. SharedPreferences
  2. 文件存储
  3. SQLite数据库
  4. 四大组件之内容提供器(Content Provider)
  5. 网络存储

18. 四大组件

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

19. 六大布局

  1. 线性布局(LinearLayout)
  2. 表格布局(TableLayout)
  3. 帧布局(FrameLayout)
  4. 相对布局(RelativeLayout)
  5. 网格布局(GridLayout)
  6. 绝对布局(AbsoluteLayout)

20. Glide框架的使用

  1. 导入依赖。
implementation 'com.github.bumptech.glide:glide:4.11.0'  //Glide
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
  1. 加入网络请求权限。
<uses-permission android:name="android.permission.INTERNET" />
  1. 使用其进行图片加载。
//加载控件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)。
  1. 导入依赖。
implementation 'de.hdodenhof:circleimageview:3.1.0'
  1. 在布局文件中,将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学习(一)相关推荐

  1. Android Kotlin学习笔记(一)—— Kotlin Koans

    文章目录 题记 1.资料阅读 2.辅助插件 3.习题演练 3.1 Introduction 1.Hello world 2.Java to Kotlin conversion 3.Named argu ...

  2. Android Kotlin 学习总结(一) 《KAE 优缺点并且深入字节码分析工作原理》

    本章会分为以下内容: 1.Kotlin KAE介绍,使用和原始Android findViewById对比优缺点 2.Kotlin KAE所存在的问题 3.通过字节码分析他的实现原理 阅读本章内容大概 ...

  3. Android kotlin run函数学习

    继续来看一下kotlin中run函数的应用,首先看一下源码: /*** Calls the specified function [block] and returns its result.** F ...

  4. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  5. Kotlin学习笔记(1)- 环境配置

    系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...

  6. Android JNI学习(六)——Java与Native实战演习

    前言: 前几篇我主要介绍了jni先关的基础知识和常用API,相信看过的童靴对JNI已经有了一定的了解,如果不了解也没关系,下面我给出了链接,可以点进去学习.接下来我将实战一个完整案例,案例很简单,就是 ...

  7. Android Kotlin之Flow数据流

    文章目录 Flow介绍 使用举例 常用操作符 创建操作符 回调操作符 变换操作符 过滤操作符 组合操作符 功能性操作符 末端操作符 冷流 vs 热流 SharedFlow shareIn将普通flow ...

  8. Kotlin学习笔记12——数据类和密封类

    Kotlin学习笔记12--数据类和密封类 前言 数据类 在类体中声明的属性 复制 componentN 解构声明 密封类 尾巴 前言 上一篇,我们学习了Kotlin中的拓展,今天继续来学习Kotli ...

  9. Kotlin学习安卓篇(一)为什么要学习Kotlin?

    一.学前问题 1.Kotlin的身世背景?它是干什么的? 2.Kotlin和Java比较如何?优缺点?相似度?为什么很多大厂都使用它? 3.Android开发是否要掌握Java和Kotlin? 4.对 ...

最新文章

  1. 建立Windows Embedded Compact 7开发环境
  2. 那是两个小时我不会回来
  3. volatile关键字及编译器指令乱序总结
  4. c语言中格式化字符串系列函数包括,解析C语言中常用的格式化输入、输出函数...
  5. 地方商城门户网页模板-商城模板
  6. 中南大学计算机学院夏令营2021,baihhh
  7. emqx http not found 怎么回事_幽默笑话:行倒是行,但两个大男人这么说话算怎么回事...
  8. 网页编辑PHP变量,编辑文件中的php代码和变量
  9. spring boot 使用 websocket tomcat刚启动就关闭到问题
  10. 如何从技术上“绞杀”盗链者?
  11. java WebSocket的实现以及Spring WebSocket
  12. 4_while循环结构和breakcontinue
  13. pandas获取dataframe的行数,列数,元素个数
  14. mysql 触发器详情
  15. cadence allegro - PCB线路敷铜渲染
  16. CentOS 7 忘记root密码重置密码
  17. 好数推荐 数据堂平均音色语音库
  18. iOS开发实战之手机号、座机号正则验证
  19. java 事物 notsupport_Spring事务传播属性介绍(二).mandatory、not_supported、never、supports...
  20. HAWKER叉车蓄电池 FLEX LI3 现已通过 UL 2580 认证 霍克叉车电瓶

热门文章

  1. 决赛来了,你,准备好了吗?
  2. 操作系统-----进程的同步
  3. 错误651服务器无响应,Win7宽带连接错误651的原因和解决方法
  4. pgsql修改表名和修改字段的操作
  5. 2020年11月国产数据库排行:GaussDB砥砺前行成第八,EsgynDB 4.2亿战绩晋级20强!...
  6. 信息技术必修丨网络域名
  7. 微信开发者工具中,新建git本地分支并上传到git服务器中
  8. 推荐:懂程序、不会美术怎么办?
  9. ROS联合webots实战案例(五)导航功能包入门2
  10. Python Manim 绘制动态函数切线