【Android】Kotlin学习(一)


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

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: Retrofit
    private lateinit var api: ApiService


/**
     * 建立网络请求链接
     */
    fun initSocket() {
    
    
        // builder模式构建Retrofit对象
        retrofit = retrofit2.Retrofit.Builder()
            .baseUrl("https://www.fastmock.site/mock/d6931ad23f0e1bdb2061d1c5363c45cb/KotlinLearning/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        // 创建接口层的代理对象,内部通过动态代理创建了ApiService的代理对象
        api = retrofit.create(ApiService::class.java)
    }
  1. 进行网络请求。
/**
     * 网络请求得到数据的具体实现
     */
    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 : HomeFragment

init {
    
    
        homeFragment3 = HomeFragment()
    }

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

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

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

10. 闭包概念

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

11. Fragment使用

  • 例子——在Activity中结合Fragment显示界面。
  • 逻辑都存在于Activity中,因为Fragment的跳转等都是在Activity中执行的。
  1. 声明需要显示的Fragment
private var homeFragment : HomeFragment? = null
    private var recipeFragment : RecipeFragment? = null
    private var discussionFragment : DiscussionFragment? = null
    private 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 = true
        ll_recipe?.isSelected = false
        ll_discussion?.isSelected = false
        ll_person?.isSelected = false
        initHomeFragment()
  1. 显示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()
    }
  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 = 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实现方剂数据的展示。
  • 效果图如下:
    在这里插入图片描述
  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">

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

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

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

</androidx.constraintlayout.widget.ConstraintLayout>
  1. 创建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)
    }
}
  1. 使用适配器和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_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.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来连接字符串。
  1. 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需要数据,就在哪个Fragmentarguments中赋值即可)

  • 传递过来的具体数据格式即为第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得到控件。
  • 效果图如下:
    在这里插入图片描述
  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.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

猜你喜欢

转载自blog.csdn.net/qq_46546793/article/details/126231929