Android在kts中使用navigation及Args

Android在kts中使用navigation及Args

前言:

​ 之前在项目中使用过navigation,但都是以Groory的方式,最近一年多使用kts后忍不住把项目都改成kts的方式,不过其中也遇到不少坑,今天就讲解一下如何在kts中使用navigation和安全地传递参数Args。

1.项目依赖导入:

在libs.versions.toml文件下添加以下依赖:

navigationFragmentKtx = "2.6.0"
navigationUiKtx = "2.6.0"

navigation-fragment = {group = "androidx.navigation",name = "navigation-fragment-ktx",version.ref = "navigationFragmentKtx"}
navigation-ui = {group = "androidx.navigation",name = "navigation-ui-ktx",version.ref = "navigationUiKtx"}

navigation-safe-args = { id = "androidx.navigation.safeargs.kotlin", version = "2.8.0" }

2.app目录的build.gradle配置:

kotlin 复制代码
plugins {
    alias(libs.plugins.androidApplication)
    alias(libs.plugins.jetbrainsKotlinAndroid)
    alias(libs.plugins.navigation.safe.args)
}

    implementation(libs.navigation.fragment)
    implementation(libs.navigation.ui)

3.项目的build.gradle配置:

kotlin 复制代码
plugins {
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.jetbrainsKotlinAndroid) apply false
    alias(libs.plugins.navigation.safe.args) apply false
}

4.在布局添加导航组件:

在res目录添加navigation------nav_graph文件

kotlin 复制代码
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:label="fragment_main"
        android:name="com.cloud.flowbusdemo.fragment.MainFragment"
        tools:layout="@layout/fragment_main">

        <action
            android:id="@+id/action_mainFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"/>
        <action
            android:id="@+id/action_mainFragment_to_mineFragment"
            app:destination="@id/mineFragment"
            app:enterAnim="@anim/slide_in_left"
            app:exitAnim="@anim/slide_in_right"
            app:popEnterAnim="@anim/slide_out_left"
            app:popExitAnim="@anim/slide_out_right" />
        <argument
            android:name="name"
            app:argType="string"
            android:defaultValue="xiaozhang"/>
        <argument
            android:name="age"
            app:argType="integer"
            android:defaultValue="1"/>
    </fragment>

    <fragment
        android:id="@+id/secondFragment"
        android:label="fragment_second"
        android:name="com.cloud.flowbusdemo.fragment.SecondFragment"
        tools:layout="@layout/fragment_second"/>

    <fragment
        android:id="@+id/mineFragment"
        android:name="com.cloud.flowbusdemo.fragment.MineFragment"
        android:label="fragment_mine"
        tools:layout="@layout/fragment_mine" />
</navigation>

5.Fragment_main布局:

fragment_mine.xml

kotlin 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvTitle"
        android:layout_width="0dp"
        android:layout_height="40dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textSize="18sp"
        android:textColor="@color/white"
        android:gravity="center"
        android:text="MainFragment"
        android:layout_margin="20dp"
        android:background="@color/design_default_color_primary"
        tools:ignore="MissingConstraints" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/btnToSecondFragment"
        android:layout_width="0dp"
        android:layout_height="40dp"
        app:layout_constraintTop_toBottomOf="@id/tvTitle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:textAllCaps="false"
        android:textColor="@color/white"
        android:gravity="center"
        android:layout_margin="20dp"
        android:background="@color/design_default_color_primary"
        android:text="打开SecondFragment"/>

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/btnToMineFragment"
        android:layout_width="0dp"
        android:layout_height="40dp"
        app:layout_constraintTop_toBottomOf="@+id/btnToSecondFragment"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:textAllCaps="false"
        android:layout_marginTop="10dp"
        android:textColor="@color/white"
        android:gravity="center"
        android:layout_margin="20dp"
        android:background="@color/design_default_color_primary"
        android:text="打开MineFragment"/>
</androidx.constraintlayout.widget.ConstraintLayout>

6.Fragment_mine布局:

kotlin 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:textSize="20sp"
        tools:text="姓名"
        android:gravity="center"
        android:background="@color/design_default_color_primary"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:textColor="@color/white"
        android:layout_marginTop="20dp"/>

    <TextView
        android:id="@+id/tvAge"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:textSize="18sp"
        tools:text="年龄"
        android:gravity="center"
        android:background="@color/design_default_color_primary"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvTitle"
        android:textColor="@color/white"
        android:layout_marginTop="20dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

7.Fragment_second布局:

kotlin 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:textSize="20sp"
        tools:text="姓名"
        android:gravity="center"
        android:background="@color/design_default_color_primary"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:textColor="@color/white"
        android:layout_marginTop="20dp"/>

    <TextView
        android:id="@+id/tvAge"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:textSize="18sp"
        tools:text="年龄"
        android:gravity="center"
        android:background="@color/design_default_color_primary"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvTitle"
        android:textColor="@color/white"
        android:layout_marginTop="20dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

8.activity_main主界面:

kotlin 复制代码
<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_wallpaper"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="2dp"
        android:paddingEnd="2dp"
        android:visibility="gone" />

    <ProgressBar
        android:id="@+id/pb_loading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/btn_get_wallpaper"
        android:layout_width="0dp"
        android:layout_height="40dp"
        android:text="获取壁纸"
        android:textColor="@color/white"
        android:gravity="center"
        android:background="@color/design_default_color_primary"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_margin="20dp"/>
    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_get_wallpaper"
        app:navGraph="@navigation/nav_graph"
        android:layout_marginTop="20dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

9.MainActivity代码:

kotlin 复制代码
package com.cloud.flowbusdemo

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import com.blankj.utilcode.util.LogUtils
import com.cloud.flowbusdemo.databinding.ActivityMainBinding
import com.cloud.flowbusdemo.flow.FlowBus
import com.cloud.flowbusdemo.http.HttpUtils
import com.cloud.flowbusdemo.intent.MainIntent
import com.cloud.flowbusdemo.model.MessageEvent
import com.cloud.flowbusdemo.service.FlowBusTestService
import com.cloud.flowbusdemo.ui.adapter.WallpaperAdapter
import com.cloud.flowbusdemo.ui.viewmodel.MainViewModel
import com.cloud.flowbusdemo.ui.viewmodel.ViewModelFactory
import com.cloud.flowbusdemo.uistate.MainUIState
import com.cloud.flowbusdemo.utils.CToast
import com.cloud.flowbusdemo.utils.GenericToast
import com.cloud.flowbusdemo.utils.SingleToast
import kotlinx.coroutines.launch


class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private lateinit var mainViewModel: MainViewModel

    private var wallPaperAdapter = WallpaperAdapter(arrayListOf())
    private val TAG = "flowBusDemo"

    private var mCToast: CToast? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        mainViewModel = ViewModelProvider(
            this,
            ViewModelFactory(HttpUtils.apiService)
        )[MainViewModel::class.java]
        initView()
        observeViewModel()
        initService()
    }

    private fun initService() {
        val intent = Intent(this@MainActivity, FlowBusTestService::class.java)
        intent.putExtra("sockUrl","")
        startService(intent)
    }

    /**
     * ViewModel
     */
    @SuppressLint("NotifyDataSetChanged")
    private fun observeViewModel() {
        lifecycleScope.launch {
            mainViewModel.state.collect {
                when (it) {
                    is MainUIState.Idle -> {

                    }

                    is MainUIState.Loading -> {
                        binding.btnGetWallpaper.visibility = View.GONE
                        binding.pbLoading.visibility = View.VISIBLE
                    }

                    is MainUIState.Success -> {     //数据返回
                        binding.btnGetWallpaper.visibility = View.GONE
                        binding.pbLoading.visibility = View.GONE

                        binding.rvWallpaper.visibility = View.VISIBLE
                        it.wallpaper.let { paper ->
                            wallPaperAdapter.addData(paper.res.vertical)
                        }
                        wallPaperAdapter.notifyDataSetChanged()
                    }

                    is MainUIState.Error -> {
                        binding.pbLoading.visibility = View.GONE
                        binding.btnGetWallpaper.visibility = View.VISIBLE
                        Log.d("TAG", "observeViewModel: $it.error")
                        Toast.makeText(this@MainActivity, it.error, Toast.LENGTH_LONG).show()
                    }
                }
            }
        }
    }

    /**
     * 初始化
     */
    private fun initView() {
        binding.rvWallpaper.apply {
            layoutManager = GridLayoutManager(this@MainActivity, 2)
            adapter = wallPaperAdapter
        }
        binding.btnGetWallpaper.setOnClickListener {
           lifecycleScope.launch {
                mainViewModel.mainIntentChannel.send(MainIntent.GetWallpaper)
            }
            val intent = Intent(this@MainActivity,TestActivity::class.java)
            startActivity(intent)


            val timeToast =
                SingleToast.makeText(this@MainActivity, "显示时间自定的Toast", 10.0)
            timeToast.show()
        }
        FlowBus.with<MessageEvent>("test").register(this@MainActivity) {
            LogUtils.d(TAG,it.toString())
            if(it.message == "stop"){
                LogUtils.d(TAG,"===接收到的消息为==="+it.message)
            }
        }
        FlowBus.with<MessageEvent>("mineFragment").register(this@MainActivity) {
            LogUtils.d(TAG,it.toString())
            if(it.message == "onMine"){
                LogUtils.d(TAG,"===接收到的消息为1111==="+it.message)
            }
        }
    }
}

10.MainFragment代码:

package com.cloud.flowbusdemo.fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import com.cloud.flowbusdemo.R
import com.cloud.flowbusdemo.databinding.FragmentMainBinding

private const val ARG_PARAM_NAME = "name"
private const val ARG_PARAM_AGE = "age"

/**
 * @auth: njb
 * @date: 2024/9/17 18:46
 * @desc: 描述
 */
class MainFragment : Fragment() {
    private lateinit var binding: FragmentMainBinding
    private var name: String? = null
    private var age: Int? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            name = it.getString(ARG_PARAM_NAME)
            age = it.getInt(ARG_PARAM_AGE)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentMainBinding.inflate(layoutInflater)
        initView()
        return binding.root
    }

    private fun initView() {
        binding.btnToSecondFragment.setOnClickListener(View.OnClickListener { v ->
            /*      val bundle = Bundle()
                  bundle.putString("name", "Michael")
                  bundle.putInt("age", 30)*/
            val args: Bundle = Bundle().apply {
                this.putString(ARG_PARAM_NAME, "哈哈")
                this.putInt(ARG_PARAM_AGE, 25)
            }
            Navigation.findNavController(v)
                .navigate(R.id.action_mainFragment_to_secondFragment, args)
        })
        binding.btnToMineFragment.setOnClickListener{v ->
            val args: Bundle = Bundle().apply {
                this.putString(ARG_PARAM_NAME, "Tom")
                this.putInt(ARG_PARAM_AGE, 18)
            }
            val navController = Navigation.findNavController(v)
            //navController.navigate(R.id.action_mainFragment_to_mineFragment, args)
            val bundle: Bundle = MainFragmentArgs("haha",20).toBundle()
             Navigation.findNavController(v).navigate(R.id.action_mainFragment_to_mineFragment,bundle)
        }
    }


}

11.MineFragment代码:

kotlin 复制代码
package com.cloud.flowbusdemo.fragment

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import com.cloud.flowbusdemo.databinding.FragmentMineBinding
import com.cloud.flowbusdemo.flow.FlowBus
import com.cloud.flowbusdemo.model.MessageEvent
import kotlinx.coroutines.launch

private const val ARG_PARAM_NAME = "name"
private const val ARG_PARAM_AGE = "age"
/**
 * @auth: njb
 * @date: 2024/9/17 19:43
 * @desc: 描述
 */
class MineFragment :Fragment(){
    private lateinit var binding: FragmentMineBinding
    private val TAG = "MineFragment"
    private var name: String? = null
    private var age: Int? = 0
    private val args:MainFragmentArgs by  navArgs()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        args.let {
            name = args.name
            age = args.age
        }

        Log.i(TAG, "传递过来的参数为 name = $name , age = $age")
        Log.d(TAG, "姓名:" + name + "年龄:" + age)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentMineBinding.inflate(layoutInflater)
        initView()
        return binding.root
    }


    private fun initView() {
        val messageEvent = MessageEvent()
        messageEvent.message = "onMine"
        messageEvent.state = false
        binding.let {
            it.tvTitle.text = name
            it.tvAge.text  = age.toString()
            it.tvTitle.setOnClickListener {
                lifecycleScope.launch {
                    FlowBus.with<MessageEvent>("mineFragment").post(this, messageEvent)
                }
            }
        }
    }
}

12.SecondFragment代码:

kotlin 复制代码
package com.cloud.flowbusdemo.fragment

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.cloud.flowbusdemo.constants.Constants
import com.cloud.flowbusdemo.databinding.FragmentMineBinding
import com.cloud.flowbusdemo.databinding.FragmentSecondBinding
import com.cloud.flowbusdemo.flow.FlowBus
import com.cloud.flowbusdemo.model.MessageEvent
import kotlinx.coroutines.launch

/**
 * @auth: njb
 * @date: 2024/9/17 18:48
 * @desc: 描述
 */
class SecondFragment : Fragment(){
    private val TAG = "SecondFragment"
    private var name: String? = null
    private var age: Int? = null
    private lateinit var binding: FragmentSecondBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentSecondBinding.inflate(layoutInflater)
        initView()
        return binding.root
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            name = it.getString(Constants.ARG_PARAM_NAME)
            age = it.getInt(Constants.ARG_PARAM_AGE)
        }
        Log.i(TAG, "MainFragment 传递到 SecondFragment 的参数为 name = $name , age = $age")
        Log.d(TAG, "姓名:" + name + "年龄:" + age)
    }

    private fun initView() {
        binding.let {
            it.tvTitle.text = name
            it.tvAge.text  = age.toString()
        }
    }
}

13.传递参数的方式:

13.1使用bundle:

kotlin 复制代码
binding.btnToSecondFragment.setOnClickListener(View.OnClickListener { v ->
          val bundle = Bundle()
          bundle.putString("name", "Michael")
          bundle.putInt("age", 30)
    Navigation.findNavController(v)
        .navigate(R.id.action_mainFragment_to_secondFragment, bundle)
})

13.2使用Safs安全方式传递:

kotlin 复制代码
binding.btnToMineFragment.setOnClickListener{v ->
    val bundle: Bundle = MainFragmentArgs("haha",20).toBundle()
     Navigation.findNavController(v).navigate(R.id.action_mainFragment_to_mineFragment,bundle)
}

14.实现效果如下:

15.项目demo地址:

https://gitee.com/jackning_admin/flowbus-demo

相关推荐
希忘auto14 小时前
详解Redis的常用命令
redis·1024程序员节
yaosheng_VALVE1 天前
探究全金属硬密封蝶阀的奥秘-耀圣控制
运维·eclipse·自动化·pyqt·1024程序员节
dami_king1 天前
SSH特性|组成|SSH是什么?
运维·ssh·1024程序员节
一个通信老学姐6 天前
专业125+总分400+南京理工大学818考研经验南理工电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节
sheng12345678rui6 天前
mfc140.dll文件缺失的修复方法分享,全面分析mfc140.dll的几种解决方法
游戏·电脑·dll文件·dll修复工具·1024程序员节
huipeng9267 天前
第十章 类和对象(二)
java·开发语言·学习·1024程序员节
earthzhang20217 天前
《深入浅出HTTPS》读书笔记(19):密钥
开发语言·网络协议·算法·https·1024程序员节
爱吃生蚝的于勒8 天前
计算机基础 原码反码补码问题
经验分享·笔记·计算机网络·其他·1024程序员节
earthzhang20218 天前
《深入浅出HTTPS》读书笔记(20):口令和PEB算法
开发语言·网络协议·算法·https·1024程序员节
一个通信老学姐8 天前
专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节