【问题标题】:Android kotlin data-binding with fragments error:cannot find symbol import com.example.***.databinding.FragmentUserBindingImpl;Android kotlin 数据绑定与片段错误:找不到符号导入 com.example.***.databinding.FragmentUserBindingImpl;
【发布时间】:2019-03-27 06:34:17
【问题描述】:

我对 kotlin 比较陌生,并尝试在某些片段中构建一个具有数据绑定的项目。我有一个名为 UserFragment 的片段,其中包含一个 Recyclerview,如下所示:

class UserFragment : Fragment() {
  private lateinit var binding: FragmentUserBinding
  private lateinit var viewModel: UserListViewModel
  override fun onCreateView(
      inflater: LayoutInflater, container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View? {
      // Inflate the layout for this fragment
      binding=DataBindingUtil.inflate(inflater,R.layout.fragment_user, container, false)
      binding.userRecycler.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)

      viewModel = ViewModelProviders.of(this).get(UserListViewModel::class.java)
      viewModel.errorMessage.observe(this, Observer {
              errorMessage -> if(errorMessage != null) showError(errorMessage) else hideError()
      })
      binding.mViewModel=viewModel
      return binding.root
      }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      //start add activity
      val i= Intent(activity,AddUserActivity::class.java)
      userFab.setOnClickListener(View.OnClickListener {
          startActivity(i)
      })
  }
  private fun showError(@StringRes errorMessage:Int){
      errorSnackbar = Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_INDEFINITE)
      errorSnackbar?.setAction(R.string.retry, viewModel.errorClickListener)
      errorSnackbar?.show()
  }

  private fun hideError(){
      errorSnackbar?.dismiss()
  }
  }

xml 布局文件 fragment_user.xml 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout
        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">
    <data>
        <variable
                name="mViewModel"
                type="com.example.***.ui.User.UserListViewModel" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <EditText
                android:id="@+id/userDateEditText"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:hint="@string/pick_date"
                android:background="@drawable/roundededittext"
                android:layout_marginEnd="8dp"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginStart="8dp"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent"/>

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/userRecycler"
                android:layout_width="293dp"
                android:layout_height="475dp" android:layout_marginTop="8dp"
                app:layout_constraintTop_toBottomOf="@+id/userDateEditText"
                app:adapter="@{viewModel.getUserListAdapter()}"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="8dp" app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp" android:layout_marginBottom="8dp"
                app:layout_constraintBottom_toBottomOf="parent"/>

        <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/userFab"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" android:layout_marginBottom="48dp"
                app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="8dp"/>
        <ProgressBar
                style="?android:attr/progressBarStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:mutableVisibility="@{viewModel.getLoadingVisibility()}"
                android:id="@+id/userProgressBar" app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="140dp" app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp" android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent" android:layout_marginBottom="8dp"
                app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0.804"
                app:layout_constraintVertical_bias="0.499"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

还有一个类似的适配器类和item_user.xml:

class UserListAdapter : RecyclerView.Adapter<UserListAdapter.ViewHolder>() {
    private lateinit var userModelList:List<UserModel>

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.ViewHolder {
        val binding: ItemUserBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_user, parent, false)
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: UserListAdapter.ViewHolder, position: Int) {
        holder.bind(userModelList[position])
    }

    override fun getItemCount(): Int {
        return if(::userModelList.isInitialized) userModelList.size else 0
    }

    fun updateUserList(userModelList:List<UserModel>){
        this.userModelList = userModelList
        notifyDataSetChanged()
    }

    class ViewHolder(private val binding: ItemUserBinding):RecyclerView.ViewHolder(binding.root){
        private val viewModel = UserViewModel()

        fun bind(userModel: UserModel){
            viewModel.bind(userModel)
            binding.viewModel =viewModel
        }
    }
}

item-user.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
                name="viewModel"
                type="com.example.***.ui.MyUser.UserViewModel" />
    </data>

    <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

        <TextView
                android:id="@+id/user_title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                app:mutableText="@{viewModel.getUserTitle()}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        <TextView
                android:id="@+id/user_description"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                app:mutableText="@{viewModel.getUserDesc()}"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/user_title" />
    </android.support.constraint.ConstraintLayout>
</layout>

请注意,数据绑定已在 gradle 中启用 这里非常重要的问题是在我的片段和适配器中 这一行:

binding.viewModel =viewModel

这样报告类型不匹配:

Type mismatch.
Required:MyUser.UserListViewModel?
Found:com.example.***.ui.MyUser.UserListViewModel

当我构建我的项目时,错误如下:

error: cannot find symbol
import com.example.***.databinding.FragmentUserBindingImpl;

【问题讨论】:

    标签: android kotlin data-binding


    【解决方案1】:

    第一个错误非常明确:binding.viewModel 需要一个可为空的 UserListViewModel?并获得一个不可为空的 UserListViewModel(参见 kotlin null safety doc)。 您可以尝试这样的方法来摆脱它:

    将您的 viewModel 声明为

    private var viewModel: UserListViewModel? = null
    

    并以这种方式设置您的绑定:

    viewModel?.let{binding.viewModel = it}
    

    关于第二个错误,您的声明似乎很好,但有时 Android Studio 的缓存会损坏,请尝试 Invalidate Caches/Restart,它可能会有所帮助。

    【讨论】:

      【解决方案2】:

      类型不匹配。 必需:MyUser.UserListViewModel? 找到:com.example.***.ui.MyUser.UserListViewModel

      基本上这个错误告诉你

      binding.viewModel //is a nullable type and there for it expects a nullable 
      //type to be assigned as well
      

      因此,只需在其 delacarion 后添加 ? 符号,将您的视图模型转换为可空类型(注意后期初始化类型不允许可空类型)。试试这样

      private var viewModel: UserListViewModel? =  null
      

      关于第二个错误数据绑定库需要编译才能自动生成绑定类,只要重新构建项目,这个错误就会消失。

      请随意使用此模板作为基础,以避免所有样板代码

      @Retention(AnnotationRetention.RUNTIME)
      @Target(AnnotationTarget.CLASS)
      annotation class ContentView(@LayoutRes val id: Int)
      
      fun ViewGroup.inflate(@LayoutRes layoutId: Int,
      addContainer: Boolean = false): View {                                                     
          return LayoutInflater.from(context).inflate(layoutId,this,addContainer)      
      }
      
      @Suppress("UNCHECKED_CAST")
      abstract class BaseFragment<Model : ViewModel, Binding : ViewDataBinding> : Fragment() {
      /**
       * Data binding class variable all view elements declared on the
       * Xml file will be available within this instance if a view model
       * Is required for the xml to work we will need to bind it on @onBindViewModel
       */
      protected lateinit var binding: WeakReference<Binding?>
      
      /**
       * Fragments view model according to MVVM android architecture
       * Each fragment class should have one , in order to facilitate
       * Live Data and Binding library features, tho we can skip it
       */
      protected lateinit var viewModel: WeakReference<Model?>
      
      /**
       * Here is where most likely you will get not null data , both binding and
       * view model references can be destroyed by garbage collector
       * If this application reaches low memory levels
       *
       * This optional method is used to bind the required view model inside the
       * Xml file, this is optional to use tho is recommended
       * Bind them by calling the view model binding.customViewModel = viewModel
       */
      protected open fun onBindViewModel(viewModel: Model?, binding: Binding?) {}
      
      /**
       * There will be the occasion where custom params will be needed on view model's
       * Constructor in this case will want to override the default creation @see ViewModelFactory
       */
      protected open fun onCreateViewModel(modelType: Class<Model>): Model? = if (modelType != ViewModel::class.java)
          ViewModelProviders.of(requireActivity()).get(modelType) else null
      
      /**
       * Here we will inherit view model and binding values based on the class
       * Parameters and store them in global variables so any class extending
       * From Base activity has access to binding and view model instances by default
       */
      override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
          val layout = this.javaClass.annotations.find { it.annotationClass == ContentView::class } as ContentView?
              ?: throw RuntimeException("You annotate this class with @ContentView and provide a layout resource")
      
          container?.let { binding = WeakReference(DataBindingUtil.bind(it.inflate(layout.id))!!) }
              ?: run { binding = WeakReference(DataBindingUtil.bind(inflater.inflate(layout.id, null))) }
      
          viewModel = WeakReference(
              onCreateViewModel(
                  (this.javaClass.genericSuperclass
                          as ParameterizedType).actualTypeArguments[0] as Class<Model>
              )
          )
      
          setHasOptionsMenu(true)
          onBindViewModel(viewModel.get(), binding.get())
          return binding.get()?.root
      }
      }
      

      就这样使用它(看看有多少样板代码消失了)

      @ContentView(R.layout.fragment_user)
      class UserFragment: BaseFragment<UserListViewModel, FragmentUserBinding> {
      
      override fun onBindViewModel(viewModel: UserListViewModel?, binding: FragmentUserBinding?) {
          binding.viewModel = viewModel
      }
      }
      

      【讨论】:

        猜你喜欢
        • 2019-12-23
        • 2019-05-24
        • 2019-06-27
        • 2017-10-30
        • 2020-05-09
        • 2020-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多