我对kotlin相对较新,并试图在一些片段中构建一个带有数据绑定的项目。我有一个名为UserFraank的片段,其中包含一个Recyclview,如下所示:
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布局文件片段。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;
类型不匹配。必需:MyUser。用户列表视图模型?发现:com。示例。***。用户界面。我的用户。UserListViewModel
基本上,错误是告诉你
binding.viewModel //is a nullable type and there for it expects a nullable
//type to be assigned as well
因此,只需将您的视图模型转换为可空类型,方法是在其delacarion之后添加? simbol(注意后期init类型不允许可空类型)。试试看
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
}
}
第一个错误非常明确:绑定。viewModel需要一个可为空的UserListViewModel吗?并获得一个不可为空的UserListViewModel(参见kotlin null安全文档)。你可以尝试这样的方法来摆脱它:
将viewModel声明为
private var viewModel: UserListViewModel? = null
并按如下方式设置绑定:
viewModel?.let{binding.viewModel = it}
关于第二个错误,您的声明似乎没有问题,但有时Android Studio的缓存会损坏,请尝试使缓存无效/重新启动,这可能会有所帮助。
迁移到androidx后 我在编译时遇到问题 错误:找不到符号androidx。数据绑定。数据绑定组件 符号:类DataBindingComponent位置:包androidx。数据绑定 im使用的Gradle版本为5.4.1 注:androidx中的其他组件。数据绑定包工作正常。像DataBindingUtil 只有DataBindingComponent不工作
我正在开始使用特性。我正面临这方面的问题。
首先,我需要承认这里有一个明显非常相似但不重复的问题。在该线程中提出的解决方案都不起作用。 我的应用程序文件结构如下: 我的应用程序包含 在文本中,错误有不同的和 如果我将datamodel.java从Models包中移出并直接放入[mydomain].[myapplication],那么我会得到不同的结果。它确实在仿真程序中构建和运行,但大部分布局信息没有出现。左上角没有汉堡包菜单,页眉没有标题
attr绑定是用来为html元素绑定属性值的,这种绑定非常有用,例如我们需要想一个元素添加title属性,或者为img标签添加src属性。 示例代码: //.W片段 <a bind-attr="{ href: url, title: details }"> Report </a> //js片段 this.url=justep.Bind.observable("year-end.html"),
submit绑定只能用在form元素中,当form提交的时候被触发,并且默认阻止form的提交。因此我们通常在submit的处理函数中以ajax的方式提交form表单。 示例代码: //.W片段 <form bind-submit="doSomething"> ... form contents go here ... <button type="submit">Submit</butt
with绑定用来创建一个绑定上下文,在子元素内的所有绑定都在这个上下文中进行。 示例代码: //.W片段 <h1 bind-text="city"> </h1> <p bind-with="coords"> Latitude: <span bind-text="latitude"> </span>, Longitude: <span bind-text="longitude"> </span