文章 16
浏览 11957
JetPack

JetPack

Lifecycle

Lifecycle中Activity和Fragment都已经实现了LifecycleOwner接口,在Presenter层中创建传入owner,也可以传入lifecycle

 1class MyPresenter(owner:LifecycleOwnter){
 2    //在初始化的时候创建Obvserver
 3    init{
 4        owner.lifecycle.addObserver(MyViewLifeImpl)
 5    }
 6    //使用一个内部类单独管理生命周期
 7    private val viewLifeImpl by lazy {
 8        ViewLifeImpl()
 9    }
10    inner class MyViewLifeImpl:LifecycleEventObserver{
11        //被动通知View层变化
12        override fun onStateChanged(
13            source: LifecycleOwner, event: Lifecycle.Event) {
14            when(event){
15                Lifecycle.Event.ON_START ->{
16                    println("监听开启")
17                }
18                Lifecycle.Event.ON_PAUSE ->{
19                    println("监听结束")
20                }
21                else ->{
2223                }
24            }
25        }
26    }
27    //或者使用主动获取state
28    owner.lifecycle.currentState
29}
303132class MyActivity(){
33    private val myPresenter by lazy{
34        MyPresenter(this)
35    }
36}
37
 1currentState中方法isAtLeast可以与生命周期比较,比较原理是比较enum顺序
 2也可以使用
 3使用注解来监听
 4 inner class ViewLifeImpl2: LifecycleObserver {
 5        //官方
 6        /**
 7         * 被动通知View层变化
 8         */
 9        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
10        fun removeTT(){
11            println("removeTT")
12        }
13    }

Navigation

简单实用

1<androidx.fragment.app.FragmentContainerView
2        android:name="androidx.navigation.fragment.NavHostFragment"
3        android:id="@+id/fragment_container_view"
4        android:layout_width="match_parent"                             app:defaultNavHost="true"//让nav处理系统返回键
5        app:navGraph ="@navigation/nav_config"
6        android:layout_height="match_parent"
7/>
1app:defaultNavHost="true"//让nav处理系统返回键

设置nav_config

将FragmentContainerView(一个容器)和nav_config链接起来

navigation下

//startDestination默认启动fragment

每一个都对应一个类和需要一个id

 1<navigation xmlns:android="http://schemas.android.com/apk/res/android"
 2    xmlns:app="http://schemas.android.com/apk/res-auto"
 3    android:id="@+id/nav_config"
 4    app:startDestination="@id/login_fragment">
 5    <fragment
 6        android:id="@+id/login_fragment"
 7        android:name="cn.udday.navigationstudy.fragment.LoginFragment">
 8 9    </fragment>
10    <fragment
11        android:id="@+id/register_fragment"
12        android:name="cn.udday.navigationstudy.fragment.RegisterFragment">
1314    </fragment>
1516    <fragment
17        android:id="@+id/forget_fragment"
18        android:name="cn.udday.navigationstudy.fragment.ForgetFragment">
1920    </fragment>
21</navigation>

跳转

在nav_config中设置action就是跳转,id为对外的,destination为对内的跳转目标

1<fragment
2        android:id="@+id/login_fragment"
3        android:name="cn.udday.navigationstudy.fragment.LoginFragment">
4        <action android:id="@+id/to_register_fragment"
5            app:destination="@+id/register_fragment"/>
6        <action android:id="@+id/to_forget_fragment"
7            app:destination="@+id/forget_fragment"/>
8    </fragment>

Activity中,两个按钮指向两个nav_config中的<action 中的id

1override fun onClick(v: View?) {
2        when(v!!.id){
3            R.id.toRegisterPage -> findNavController().navigate(R.id.to_register_fragment)
4            R.id.toForgetPage -> findNavController().navigate(R.id.to_forget_fragment)
5        }

跳转Activity同理(Fragment不能用系统返回键返回)

跳转动画

新建anim包

新建slipe_from_right_to_left_in

slipe_from_right_to_left_out

 1<set xmlns:android="http://schemas.android.com/apk/res/android">
 2    <translate
 3        android:duration = "400"//400毫秒
 4        android:fromXDelta="100%p"
 5        android:toXDelta="0"/>
 6 7</set>
 8<set xmlns:android="http://schemas.android.com/apk/res/android">
 9    <translate
10        android:duration = "400"
11        android:fromXDelta="0"
12        android:toXDelta="-100%p"/>
13</set>

在nav-config中

1<action android:id="@+id/to_register_fragment"
2            app:destination="@+id/register_fragment"
3            app:exitAnim="@anim/slipe_from_right_to_left_out"
4            app:enterAnim="@anim/slide_from_right_to_left_in"/>
5            //一个进
6            exitAnim和enterAnim是跳转去
7            popEnterAnim和popExitAnims是跳转回来

全局添加Activity切换动画

主题配置下

 1<resources xmlns:tools="http://schemas.android.com/tools">
 2    <!-- Base application theme. -->
 3    <style name="Theme.NavigationStudy" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
 4        <!-- Primary brand color. -->
 5        <item name="colorPrimary">@color/purple_500</item>
 6        <item name="colorPrimaryVariant">@color/purple_700</item>
 7        <item name="colorOnPrimary">@color/white</item>
 8        <!-- Secondary brand color. -->
 9        <item name="colorSecondary">@color/teal_200</item>
10        <item name="colorSecondaryVariant">@color/teal_700</item>
11        <item name="colorOnSecondary">@color/black</item>
12        <!-- Status bar color. -->
13        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
14        <!-- Customize your theme here. -->
15        <item name="android:windowAnimationStyle">@style/activityAnimation</item>
16        <!-- 这句话将下面的引用至主题 -->
17    </style>
18    <style name="activityAnimation" parent="@android:style/Animation">
19        <item name="android:activityOpenEnterAnimation">@anim/slide_from_right_to_left_in</item>
20        <item name="android:activityOpenExitAnimation">@anim/slide_from_right_to_left_out</item>
21        <item name="android:activityCloseEnterAnimation">@anim/slide_from_left_to_right_in</item>
22        <item name="android:activityCloseExitAnimation">@anim/slide_from_left_to_right_out</item>
23    </style>
24</resources>

跳转与回退

跳转

1Fragment.findNavController()
2View.findNavController()
3Activity.findNavController(viewId:Int)
1NavHostFragment.findNavController(Fragment)
2Navigation.findNavController(Activity,@IdRes int viewId)
3Navigation.findNavController(View)

回退

1NavController.navigateUp()/NavController.popBackStack()

navigateUP()适用于左上角那种返回键

popBackStack()适用系统返回键

元素共享

使用动画启动 Activity:https://developer.android.google.cn/training/transitions/start-activity

fragment动画:https://developer.android.google.cn/guide/fragments/animate?hl=zh_cn

Fragment元素共享

 1//两个元素的transitionName一致
 2android:transitionName=""
 3 4//在发出的Fragment中点击事件
 5            R.id.toRegisterPage ->{
 6                val extras:FragmentNavigator.Extras =
 7                    FragmentNavigatorExtras(userAvatarIv to "userAvatarTn")
 8                findNavController().navigate(R.id.to_register_fragment,null,null,extras)
 9            } 
10//在接受的Fragment中
11    override fun onCreate(savedInstanceState: Bundle?) {
12        super.onCreate(savedInstanceState)
13        sharedElementEnterTransition = TransitionInflater.from(requireContext()).inflateTransition(R.transition.shared_image)
14    }
15//新建transition,加上shared_image
1//shared_image
2<?xml version="1.0" encoding="utf-8"?>
3<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
4    <changeImageTransform />//替换成
5    <autoTransition 
6    android:duration="400"/>
7</transitionSet>
 1//添加多个元素共享的办法
 2override fun onClick(v: View?) {
 3    when(v!!.id){
 4        R.id.toRegisterPage ->{
 5            val imagePair :Pair<View,String> = Pair<View,String>(userAvatarIv,"userAvatarTn")
 6            val imagePair2 :Pair<View,String> = Pair<View,String>(userAvatarIv,"userAvatarTn")
 7            val extras:FragmentNavigator.Extras =
 8                FragmentNavigatorExtras(imagePair,imagePair2)
 9            findNavController().navigate(R.id.to_register_fragment,null,null,extras)
10        }

Activity元素共享

同样的需要一个android:transitionName=""

 1//发起方
 2R.id.toAgreementAPage ->{
 3    val imagePair = androidx.core.util.Pair<View,String>(userAvatarIv,"userAvatarTn")//特别注意,这里的Pair是Androidx中的
 4    val options:ActivityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(
 5        requireActivity(),
 6        imagePair
 7    )
 8    val extras = ActivityNavigator.Extras.Builder()
 9        .setActivityOptions(options)
10        .build();
11    findNavController().navigate(R.id.to_agreement_activity,null,null,extras)
12}

数据共享

 1R.id.toRegisterPage ->{
 2    //两个共享元素
 3    //头像
 4    val imagePair = Pair<View,String>(userAvatarIv,"userAvatarTn")
 5    //用户名
 6    val userNamePair = Pair<View,String>(userNameInp,"userNameTn")
 7    val extras:FragmentNavigator.Extras =
 8        FragmentNavigatorExtras(imagePair,userNamePair)
 9    //数据
10    val bundle = Bundle()
11    bundle.putString("userName",userNameInp.text.toString())
12    findNavController().navigate(R.id.to_register_fragment,bundle,null,extras)
13}

使用bundle将数据传入navigate的args中

接受方

1val userName:String?= arguments?.getString("userName")
2rootView.RegUserNameInp.setText(userName)

Acitivty

1val userName: String? = intent.getStringExtra("userName")
2agreeUserNameInp.setText(userName)

启动DataBinding

1android {
2    ...
3    buildFeatures {
4        dataBinding true
5    }
6}
1plugins {
2    id  'kotlin-kapt'
3}

在xml文件中对最外层布局按alt+回车可以生成DataBinding

新建一个data class

右键名称,Copy Reference,复制全路径名称

1<data>
2    <variable
3        name="user"
4        type="com.example.databindingstudy.domain.User" />
5</data>
1android:text="@{user.name}"

在Activity中

把下划线改成驼峰命名法

activity_main

ActivityMainBinding

1override fun onCreate(savedInstanceState: Bundle?) {
2    super.onCreate(savedInstanceState)
34    val binding:ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
5    binding.user = User("张三",14,Gender.Man)
6    setContentView(binding.root)
7}

或者用

1val contentView:ActivityMainBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

Fragment中使用

 1abstract class BaseViewFragment<T:ViewDataBinding> :Fragment() {
 2    protected var binding:T? = null
 3    override fun onCreateView(
 4        inflater: LayoutInflater,
 5        container: ViewGroup?,
 6        savedInstanceState: Bundle?
 7    ): View? {
 8        val rootView = inflater.inflate(getSubLayoutId(), container, false)
 9        binding = DataBindingUtil.bind<T>(rootView)
10        //binding = DataBindingUtil.inflate<T>(inflater, getSubLayoutId(), container, false)第二种方法
1112        return rootView
13        //return binding!!.root
14    }
15    abstract fun getSubLayoutId():Int
16}

带ViewModel

 1abstract class BaseVmFragment<T:ViewDataBinding,VM:ViewModel>:BaseViewFragment<T>() {
 2    protected lateinit var viewModel: ViewModel
 3 4    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 5        super.onViewCreated(view, savedInstanceState)
 6        //创建ViewModel
 7        initViewModel()
 8        //观察数据变化 -->更新UI
 9        observerData()
10        //设置相关事件
11        initEvent()
12        //开始去加载数据
13        startLoadData()
14    }
15    open fun startLoadData() {
16        
17    }
18    open fun initEvent() {
1920    }
2122    open fun observerData() {
2324    }
2526    private fun initViewModel() {
27        viewModel = ViewModelProvider(this,ViewModelProvider.NewInstanceFactory()).get(getSubVMClass())
28    }
293031    abstract fun getSubVMClass(): Class<VM>
32}

搭好MVVM

1class OnSellViewModel:ViewModel() {
2}
1class OnSellRepository {
2}
1class OnSellFragment:BaseVmFragment<FragmentOnSellBinding,OnSellViewModel>() {
2    override fun getSubVMClass(): Class<OnSellViewModel> {
3        return OnSellViewModel::class.java
4    }
56    override fun getSubLayoutId(): Int {
7        return R.layout.fragment_on_sell
8    }
9}

在ViewModel里面建立关心的数据

 1class OnSellViewModel:ViewModel() {
 2    //所关心的数据
 3 4    //加载状态:Loading,Success,Empty,Error,None
 5    val loadState by lazy {
 6        MutableLiveData<LoadState>()
 7    }
 8 9    //数据列表 contentList
10    val contentList by lazy {
11        MutableLiveData<MutableList<OnSellItem>>()
12    }
13}

在Ui即Fragment中创建观察

 1override fun observerData() {
 2        //观察ViewModel里的数据变化
 3        viewModel.loadState.observe(this, Observer { newState ->
 4            println("newState --> $newState")
 5            //更新UI
 6            //TODO
 7        })
 8 9        viewModel.contentList.observe(this, Observer { contentList ->
10            "contentList.size --> ${contentList.size}"
11            //当内容列表发生变化的时候,就会通知到这里了
12            //TODO
13        })
14    }

新建RetrofitClient

 1object RetrofitClient {
 2    private const val BASE_URL = "https://v2.alapi.cn/api/eventHistory/"
 3    private val okHttpClient = OkHttpClient.Builder()
 4        .callTimeout(30,TimeUnit.SECONDS)
 5        .build()
 6    //创建retrofit
 7    private val retrofit = Retrofit.Builder()
 8        .baseUrl(BASE_URL)
 9        .addConverterFactory(GsonConverterFactory.create())
10        .client(okHttpClient)
11        .build()
12    val apiService = retrofit.create(ApiService::class.java)
13}

在Repository中新建加载的方法

1class OnSellRepository {
2    suspend fun getOnSellDataS(year:Int):OnSellItem = RetrofitClient.apiService.getOnSellList(year)
3}

在ViewModel中持有Repository的实例,同时在loadData中加载数据

 1class OnSellViewModel:ViewModel() {
 2    private val repository by lazy {
 3        OnSellRepository()
 4    }
 5    private var year = 2021;
 6    fun loadData() {
 7        //这个加载需要UI层转圈圈(Loading)
 8        //更新状态
 9        loadState.value = LoadState.LOADING
10        //加载数据
11        loadDataS(year)
12    }
1314    private fun loadDataS(year:Int) {
15        //根据年份加载数据
16        viewModelScope.launch {
17            val onSellDataS = repository.getOnSellDataS(year)
18            println("result code --> ${onSellDataS.code}")
19            println("result msg--> ${onSellDataS.msg}")
20            println("result data--> ${onSellDataS.data.get(0).date}")
2122        }
23    }
24    //所关心的数据
2526    //加载状态:Loading,Success,Empty,Error,None
27    val loadState by lazy {
28        MutableLiveData<LoadState>()
29    }
3031    //数据列表 contentList
32    val contentList by lazy {
33        MutableLiveData<MutableList<OnSellItem>>()
34    }
35}

与RecycleView配合

 1private fun loadDataS(year:Int) {
 2        //根据年份加载数据
 3        viewModelScope.launch {
 4//            val onSellDataS = repository.getOnSellDataSearch(year)
 5            val onSellDataS = repository.getOnSellDataS()
 6            println(onSellDataS.msg)
 7            println(onSellDataS.code)
 8            //对数据进行判断,数据有空能为空,有可能网络请求出错
 9             if (onSellDataS.code == 200){
10                 contentList.value = onSellDataS.data
11                 //在处理之后返回Success
12                 loadState.value = LoadState.SUCCESS
13             }else{
14                 loadState.value = LoadState.EMPTY
15                 Log.e(TAG,"${onSellDataS.code} : ${onSellDataS.msg}")
16             }
17            //处理一下数据
18        }
19    }

新建Adapter

 1class OnSellListAdapter: RecyclerView.Adapter<OnSellListAdapter.InnerHolder>() {
 2 3    private val mMutableList by lazy {
 4        mutableListOf<Data>()
 5    }
 6    class InnerHolder(itemView: View,val binding: ItemOnSellBinding):RecyclerView.ViewHolder(itemView) {
 7 8    }
 910    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InnerHolder {
11        //这里的不可以设置成员变量,这里是频繁创建的
12        //DataBinding日常操作
13        val itemBinding = DataBindingUtil.inflate<ItemOnSellBinding>(
14            LayoutInflater.from(parent.context),
15            R.layout.item_on_sell, parent, false
16        )
17        return InnerHolder(itemBinding.root,itemBinding)
18    }
1920    override fun getItemCount(): Int {
21        return mMutableList.size;
22    }
2324    override fun onBindViewHolder(holder: InnerHolder, position: Int) {
2526        //在这里设置数据
27        //拿到对应位置的数据
28        val itemData:Data = mMutableList[position]
29        //需要binding设置数据
30        holder.binding.itemOnSellData = itemData
31        /*
32        在xml中设置了data,然后获取OnSellData,然后绑定domain,然后设置相应的控件,在获取到数据之后与数据进行绑定
33         */
34    }
3536    fun setData(contentList: MutableList<Data>) {
37        //清空
38        mMutableList.clear()
39        //添加为成员变量
40        mMutableList.addAll(contentList)
41        //全部更新
42        notifyDataSetChanged()
43        //如果是添加到头部或者尾部,可以局部更新
44    }
4546}

更改BaseFragmentView中的信息

 1abstract class BaseViewFragment<T:ViewDataBinding> :Fragment() {
 2 3    protected lateinit var binding:T
 4    //做成成员变量,直接获取
 5    protected lateinit var rootView:View
 6    override fun onCreateView(
 7        inflater: LayoutInflater,
 8        container: ViewGroup?,
 9        savedInstanceState: Bundle?
10    ): View? {
11        binding = DataBindingUtil.inflate<T>(inflater, getSubLayoutId(), container, false)
12        rootView = binding.root
13        return rootView
14    }
15    abstract fun getSubLayoutId():Int
16}
 1override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 2        super.onViewCreated(view, savedInstanceState)
 3        //创建ViewModel
 4        initViewModel()
 5        //观察数据变化 -->更新UI
 6        observerData()
 7        //初始化相关的控件
 8        initView()
 9        //设置相关事件
10        initEvent()
11        //开始去加载数据
12        startLoadData()
13    }

设置RecycleView

 1private val mAdapter by lazy {
 2    OnSellListAdapter()
 3}
 4override fun initView() {
 5    rootView.onSellListView.run {
 6        //布局管理器
 7        layoutManager = LinearLayoutManager(context)
 8        //适配器
 9        adapter = mAdapter
10        //
11    }
12}

在observerData中设置

1viewModel.contentList.observe(this, Observer { contentList ->
2    "contentList.size --> ${contentList.size}"
3    //当内容列表发生变化的时候,就会通知到这里了
4    //更新列表
5    mAdapter.setData(contentList)
6})

设置更新列表

1fun setData(contentList: MutableList<Data>) {
2    //清空
3    mMutableList.clear()
4    //添加为成员变量
5    mMutableList.addAll(contentList)
6    //全部更新
7    notifyDataSetChanged()
8    //如果是添加到头部或者尾部,可以局部更新
9}

控件样例

1<TextView
2    android:id="@+id/ItemOnSellTitle"
3    android:gravity="center"
4    android:textColor="@color/black"
5    android:text="@{itemOnSellData.title}"
6    android:layout_width="match_parent"
7    android:layout_height="wrap_content" />

标题:JetPack
作者:kuohai
地址:https://udday.cn/articles/2021/04/23/1619142409580.html

充实的一天

取消