我开始使用 android jetpack arch 组件,但遇到了一些困惑。另请注意,数据绑定不是一个选项。
我有一个带有 RecyclerView 的活动。我有一个如下所示的 ViewModel
public class Movie extends ViewModel {
public Movie movie;
public URL logoURL;
private MutableLiveData<Drawable> logo;
public MutableLiveData<Drawable> getLogo() {
if (logo == null) {
logo = new MutableLiveData<>();
}
return logo;
}
public PikTvChannelItemVM(Movie movie, URL logo) {
this.movie = movie;
this.logoURL = logoURL;
}
public Bitmap getChannelLogo() {
//Do some network call to get the bitmap logo from the url
}
}
以上一切都很好,尽管在我的 I recyclerview 中有以下代码。尽管在 onbindviewholder 中,当我尝试观察视图模型实时数据返回的图像时,它需要一个生命周期所有者引用,而我的回收器视图中没有该引用。请帮忙谢谢
public class MovieRecyclerViewAdapter extends RecyclerView
.Adapter<MovieRecyclerViewAdapter.MovieItemViewHolder> {
public List<MovieViewModel> vmList;
public MovieRecyclerViewAdapter(List<MovieViewModel> vmList) {
this.vmList = vmList;
setHasStableIds(true);
}
@Override
public MovieItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MovieView itemView = new MovieView(parent.getContext(), null);
itemView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return new MovieItemViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull MovieItemViewHolder holder, int position) {
vmList.get(position).observe(?????, users -> {
// As you can see I have no reference to the life cycle owner
holder.imageView.setimage(some drawable returned by the View Model)
});
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public int getItemCount() {
return vmList.size();
}
class MovieItemViewHolder extends RecyclerView.ViewHolder {
private MovieView channelView;
public MovieItemViewHolder(View v) {
super(v);
channelView = (MovieView) v;
}
public MovieView getChannelView() {
return channelView;
}
}
}
我会尝试向适配器发送一个列表来显示它。我会观察适配器外部的数据,因为适配器的责任不是观察数据而是显示数据。
您的适配器列表类型不应该是viewmodel。如果您想在不绑定的情况下使用实时数据,则需要在列表更新时自动更改 ui。
你可以这样做:
首先,你的电影类必须是模型类,而不是视图模型。
像平常一样安装适配器。只需添加一个
setList(Movie)
方法即可。此方法应该更新适配器列表。更新列表后不要忘记通知适配器。
然后创建实时数据列表,如
MutableLiveData<List<Movie>> movieLiveData =
new MutableLiveData<>();
您想要的地方。
在 Activity 中观察此列表,并在observe{} 中调用适配器 setList(Movie) 方法。
完成这一切后,如果您的列表更新,
setList(Movie)
方法将被触发,然后您的用户界面将被更新。
你可以这样做:
视图模型:
class MyViewModel : ViewModel() {
private val items = MutableLiveData<List<String>>()
init {
obtainList()
}
fun obtainList() { // obtain list from repository
items.value = listOf("item1", "item2", "item3", "item4")
}
fun getItems(): LiveData<List<String>> {
return items
}
}
您的片段(或活动):
public class ContentFragment extends Fragment {
private MyViewModel viewModel;
private RecyclerView recyclerView;
private MyAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container, false);
recyclerView = root.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 4));
// add observer
viewModel.getItems().observe(this, items -> {
adapter = new MyAdapter(items); // add items to adapter
recyclerView.setAdapter(adapter);
});
return root;
}
适配器:
class MyAdapter(val list: List<String>) : RecyclerView.Adapter<MyAdapter.TextViewHolder {
...
override fun onBindViewHolder(holder: TextViewHolder, position: Int) {
holder.textView.text = list[position] // assign titles from the list.
...
}
}
可以使用任何自定义对象来代替 String 对象。
通常提出以下方案作为解决方案:
Model
调用ViewModel
中的方法;ViewModel
将变量保存在List
中并将更新发送到LiveData
;View
看到 List
中更新的 LiveData
并更新 RecyclerView
。也就是说,当你改变数据时,你通过
List
重新传输LiveData
,重新定义它的值。然而,这会更新整个RecyclerView
。每次。
(或者,您可以在 View
中创建一个复杂的跟踪机制,检查 List
的更改并调用通知。但这很不方便。)
我想使用
RecyclerView.Adapter
中的标准通知方法。
LiveData
的主要问题是你无法订阅动态变化列表中的每个元素,否则LiveData将与列表元素一起不断创建和删除。但是如果我们要一次加载 10K 个项目,需要多少内存呢?
所以我决定将
List
放入包装类中,并能够分配侦听器以添加到 List
:
class ObservableArrayList<Type> : ArrayList<Type>() {
// interface for the change listener
abstract class OnListUpdateListener<Type>(val looper: Looper) {
abstract fun onItemAdded(position: Int, element: Type)
abstract fun onItemRemoved(position: Int)
abstract fun onItemUpdated(position: Int, element: Type)
}
// listeners of changes in the List
private val listeners = mutableListOf<OnListUpdateListener<Type>>()
fun addOnListUpdateListener(listener: OnListUpdateListener<Type>) {
listeners.add(listener)
}
fun removeOnListUpdateListener(listener: OnListUpdateListener<Type>) {
listeners.remove(listener)
}
// data changing
override fun add(element: Type): Boolean {
val result = super.add(element)
// notifying listeners
if (result) listeners.forEach { it.onItemAdded(super.size, element) }
return result
}
override fun add(index: Int, element: Type) {
super.add(index, element)
listeners.forEach { it.onItemAdded(super.size, element) }
}
override fun remove(element: Type): Boolean {
val position = super.indexOf(element)
val result = super.remove(element)
if (result) listeners.forEach { it.onItemRemoved(position) }
return result
}
override fun removeAt(index: Int): Type {
val result = super.removeAt(index)
listeners.forEach { it.onItemRemoved(index) }
return result
}
override fun set(index: Int, element: Type): Type {
val result = super.set(index, element)
listeners.forEach { it.onItemUpdated(index, element) }
return result
}
// can be called from a background thread
// (for pure MVVM, it can only be called in ViewModel methods)
fun postAdd(element: Type): Boolean {
val result = super.add(element)
// notifying listeners
if (result) {
listeners.forEach {
Handler(it.looper).post {
it.onItemAdded(super.size, element)
}
}
}
return result
}
fun postRemoveAt(index: Int): Type {
val result = super.removeAt(index)
// notifying listeners
listeners.forEach {
Handler(it.looper).post {
it.onItemRemoved(index)
}
}
return result
}
fun postSet(index: Int, element: Type): Type {
val result = super.set(index, element)
// notifying listeners
listeners.forEach {
Handler(it.looper).post {
it.onItemUpdated(index, element)
}
}
return result
}
// ... You can override the rest of the methods ...
}
这个类非常容易使用。 我在
LiveData
:中创建了一个
ViewModel
变量
val logsList = MutableLiveData(ObservableArrayList<LogEntry>())
当我从
Model
收到信息时,我只是将其保存到List
:
logsList.value!!.postAdd(LogEntry((/*...*/))
在
View
中,我订阅接收 List
,然后订阅 View
中的更改 List
viewModel.logsList.observe(viewLifecycleOwner) { list ->
// assign a sheet to RecyclerView 1 time (on list init)
recyclerView.adapter = LogsAdapter(list, resources)
// subscribe to the List updates
list.addOnListUpdateListener(object :
ObservableArrayList.OnListUpdateListener<LogEntry>(Looper.getMainLooper()) {
override fun onItemAdded(position: Int, element: LogEntry) {
recyclerView.adapter!!.notifyItemInserted(position)
}
override fun onItemRemoved(position: Int) {
recyclerView.adapter!!.notifyItemRemoved(position)
}
override fun onItemUpdated(position: Int, element: LogEntry) {
recyclerView.adapter!!.notifyItemChanged(position)
}
})
}