首页 > 解决方案 > 未解决的参考:使用自定义 Parcelable 参数时:Jetpack Navigation

问题描述

这是我第一次使用 Jetpack Navigation 和 Safe args。使用自定义 Parcelable 作为参数传递给特定片段时出现错误。

这是我在构建时遇到的错误:

com/bhardwaj/ui/FragmentUIDetails.kt: (34, 26): Unresolved reference: uIDetail

这就是我的 FragmentUIDetails 的样子:

class FragmentUIDetails : Fragment(), UIDetailListener {

private lateinit var viewModel: FragmentUIDetailsViewModel
private lateinit var sliderAdapter: ImageSliderAdapter
private lateinit var viewPager: ViewPager2
private lateinit var dotsIndicator: DotsIndicator
private lateinit var uiDetails: UI
private var itemList: ArrayList<Any> = arrayListOf()
private val args: FragmentUIDetailsArgs by navArgs()

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    uiDetails = args.uIDetail
    return inflater.inflate(R.layout.fragment_ui_detail, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewPager = view.findViewById(R.id.viewPager)
    dotsIndicator = view.findViewById(R.id.dotsIndicator)

    viewModel = ViewModelProvider(this).get(FragmentUIDetailsViewModel::class.java)

    itemList.addAll(uiDetails.uiImages!!)
    sliderAdapter = context?.let { ImageSliderAdapter(it, itemList) }!!
    context?.let {
        viewModel.loadNativeAd(it, sliderAdapter)
    }

    viewPager.adapter = sliderAdapter
    dotsIndicator.setViewPager2(viewPager)
}

private fun showInfo() {
    activity?.let {
        AlertDialog.Builder(it).apply {
            val dialogView =
                requireActivity().layoutInflater.inflate(R.layout.dialog_ui_information, null)

            val designer = dialogView.findViewById<TextView>(R.id.tvDesignerInfo)
            val source = dialogView.findViewById<TextView>(R.id.tvSourceInfo)

            designer.text =
                String.format("%s: %s", getString(R.string.designer), uiDetails.author)
            source.text =
                String.format("%s: %s", getString(R.string.source), uiDetails.source)

            setView(dialogView)
        }.create().show()
    }
}

override fun onUIListClick(view: View) {
    when (view.id) {
        R.id.ivInfo -> showInfo()

        R.id.ivShareUI -> viewModel.shareLink(
            uiDetails.downloadUrl.toString(),
            requireActivity()
        )

        R.id.fabButton -> viewModel.download(
            uiDetails.downloadUrl.toString(),
            uiDetails.fileName!!,
            requireContext()
        )
    }
}

}

这就是我的导航图 XML 的样子:

<?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/navigation_main"
app:startDestination="@id/fragmentAdobeXD">

<fragment
    android:id="@+id/fragmentAdobeXD"
    android:name="com.bhardwaj.ui.view.FragmentAdobeXD"
    android:label="fragment_adobe_xd"
    tools:layout="@layout/fragment_adobe_xd">
    <action
        android:id="@+id/action_fragmentAdobeXD_to_fragmentSearch"
        app:destination="@id/fragmentSearch" />
    <action
        android:id="@+id/action_fragmentAdobeXD_to_fragmentUIDetails"
        app:destination="@id/fragmentUIDetails" />
</fragment>
<fragment
    android:id="@+id/fragmentFigma"
    android:name="com.bhardwaj.ui.view.FragmentFigma"
    android:label="fragment_figma"
    tools:layout="@layout/fragment_figma">
    <action
        android:id="@+id/action_fragmentFigma_to_fragmentSearch"
        app:destination="@id/fragmentSearch" />
    <action
        android:id="@+id/action_fragmentFigma_to_fragmentUIDetails"
        app:destination="@id/fragmentUIDetails" />
</fragment>
<fragment
    android:id="@+id/fragmentSketch"
    android:name="com.bhardwaj.ui.view.FragmentSketch"
    android:label="fragment_sketch"
    tools:layout="@layout/fragment_sketch">
    <action
        android:id="@+id/action_fragmentSketch_to_fragmentSearch"
        app:destination="@id/fragmentSearch" />
    <action
        android:id="@+id/action_fragmentSketch_to_fragmentUIDetails"
        app:destination="@id/fragmentUIDetails" />
</fragment>
<fragment
    android:id="@+id/fragmentMore"
    android:name="com.bhardwaj.ui.view.FragmentMore"
    android:label="fragment_more"
    tools:layout="@layout/fragment_more" />
<fragment
    android:id="@+id/fragmentSearch"
    android:name="com.bhardwaj.ui.view.FragmentSearch"
    android:label="FragmentSearch">
    <action
        android:id="@+id/action_fragmentSearch_to_fragmentUIDetails"
        app:destination="@id/fragmentUIDetails" />
    <argument
        android:name="SearchDetail"
        android:defaultValue="@null"
        app:argType="com.bhardwaj.ui.models.UI"
        app:nullable="true" />
</fragment>
<fragment
    android:id="@+id/fragmentUIDetails"
    android:name="com.bhardwaj.ui.view.FragmentUIDetails"
    android:label="FragmentUIDetails">
    <argument
        android:name="UIDetail"
        app:argType="com.bhardwaj.ui.models.UI" />
</fragment>

项目中添加的依赖项 -

建造。摇篮(项目) -

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"

建造。摇篮(模块) -

id("androidx.navigation.safeargs.kotlin")
id("kotlin-parcelize")

implementation "androidx.navigation:navigation-fragment-ktx:2.3.5"
implementation "androidx.navigation:navigation-ui-ktx:2.3.5"

这是我的数据类的样子:

@Parcelize
class UI(
 val imageUrl: String? = null,
 val title: String? = null,
 val author: String? = null,
 val downloadUrl: String? = null,
 val fileName: String? = null,
 val source: String? = null,
 val timeStamp: Long? = 0,
 val category: String? = null,
 val tag: List<String>? = null,
 val uiImages: List<String>? = null
) : Parcelable

我所尝试的一切:

  1. 从 androidx.navigation 切换到 android.arch.navigation
  2. 使缓存无效并重新启动。
  3. 重建项目。

更新

这就是 FragmentUIDetailsArgs 类的样子:

  public data class FragmentUIDetailsArgs(
  public val UIDetail: UI
) : NavArgs {
  @Suppress("CAST_NEVER_SUCCEEDS")
  public fun toBundle(): Bundle {
    val result = Bundle()
    if (Parcelable::class.java.isAssignableFrom(UI::class.java)) {
      result.putParcelable("UIDetail", this.UIDetail as Parcelable)
    } else if (Serializable::class.java.isAssignableFrom(UI::class.java)) {
      result.putSerializable("UIDetail", this.UIDetail as Serializable)
    } else {
      throw UnsupportedOperationException(UI::class.java.name +
          " must implement Parcelable or Serializable or must be an Enum.")
    }
    return result
  }

  public companion object {
    @JvmStatic
    public fun fromBundle(bundle: Bundle): FragmentUIDetailsArgs {
      bundle.setClassLoader(FragmentUIDetailsArgs::class.java.classLoader)
      val __UIDetail : UI?
      if (bundle.containsKey("UIDetail")) {
        if (Parcelable::class.java.isAssignableFrom(UI::class.java) ||
            Serializable::class.java.isAssignableFrom(UI::class.java)) {
          __UIDetail = bundle.get("UIDetail") as UI?
        } else {
          throw UnsupportedOperationException(UI::class.java.name +
              " must implement Parcelable or Serializable or must be an Enum.")
        }
        if (__UIDetail == null) {
          throw IllegalArgumentException("Argument \"UIDetail\" is marked as non-null but was passed a null value.")
        }
      } else {
        throw IllegalArgumentException("Required argument \"UIDetail\" is missing and does not have an android:defaultValue")
      }
      return FragmentUIDetailsArgs(__UIDetail)
    }
  }
}

标签: androidandroid-jetpackparcelableandroid-jetpack-navigationandroid-safe-args

解决方案


这就是我解决问题的方法:

问题出在 4.0 以上的 IDE 和北极狐版本中,在我的情况下,因为它默认不引用 navigation-args 文件夹,所以我们需要对位置进行硬编码。

转到 Build.gradle (Module) 并复制它 -

android {
    ...

    sourceSets {
        main {
            java {
                srcDirs += 'build/generated/source/navigation-args'
            }
        }
    }

    ...
}

推荐阅读