簡體   English   中英

Android kotlin 數據綁定與片段錯誤:找不到符號導入 com.example.***.databinding.FragmentUserBindingImpl;

[英]Android kotlin data-binding with fragments error:cannot find symbol import com.example.***.databinding.FragmentUserBindingImpl;

我對 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
        }
    }
}

項目-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;

第一個錯誤非常明確:binding.viewModel 需要一個可為空的 UserListViewModel? 並獲得一個不可為空的 UserListViewModel(請參閱kotlin null 安全文檔)。 你可以嘗試這樣的事情來擺脫它:

將您的 viewModel 聲明為

private var viewModel: UserListViewModel? = null

並以這種方式設置您的綁定:

viewModel?.let{binding.viewModel = it}

關於第二個錯誤,您的聲明似乎沒有問題,但有時 Android Studio 的緩存已損壞,請嘗試使緩存無效/重新啟動,它可能會有所幫助。

類型不匹配。 必需: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 之后的 simbol(注意后期初始化類型不允許為空類型)。 像這樣試試

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
}
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM