How to connect Adapter with Presenter in MVP? - android

In RecyclerView I have a ViewHolder with VideoView and Button "Like" with the state (selected or not).
My Presenter have a method which will update "Like" status in the model - VideoEntity.
In callback I need to update View form Presenter, so I call getView().updateItem(VideoEntity entity).
After that, I should find recyclerView entity position, and invalidate item.
So I want to avoid that.
In classic architecture, i can call some method from ViewHolder, get some callback there and update changed data.
How to migrate this pseudo-code to MVP pattern?
#Override
public void onBindViewHolder(ContainerViewHolder holder, int position) {
...
rx.subscribe(result -> holder.update(result));
}
#Override
public void onViewRecycled(ContainerViewHolder holder) {
if (haveBackgroundRequests) { rx.unsubscribe(holder); }
}

One presenter for one viewHolder.
I found solution here: https://github.com/remind101/android-arch-sample

Related

How to stop the RecyclerView OnBindViewHolder() function iterating through all items of my ArrayList every time a new item is added?

I'm trying to make a simple chat application for my own learning - no firebase involved (the messages won't be stored between sessions). I've implemented a RecyclerView to show all the messages. The problem is that every time I add a new message, the RecyclerView Adapter will iterate through all previous messages before populating the latest one. Whilst this isn't causing any major bugs, it does seem very inefficient. The relevant functions in my adapter class are shown below:
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
MessageItem newMsgItem = messages.get(position);
holder.txtMsgContent.setText(newMsgItem.getMsgContent());
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) holder.msgParentView.getLayoutParams();
if (newMsgItem.isSent()) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
holder.msgParentView.setLayoutParams(layoutParams);
holder.msgParentView.setCardBackgroundColor(0xFF03DAC5);
} else {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
holder.msgParentView.setLayoutParams(layoutParams);
holder.msgParentView.setCardBackgroundColor(0xFF67706F);
}
}
// boolean sent: false = received, true = sent
public void addMessage (boolean sent, String msgContent) {
messages.add(new MessageItem(sent, msgContent));
notifyDataSetChanged();
}
I could implement a condition-check like below, but that isn't a satisfying solution as it only masks the problem - i.e. the program is still iterating unnecessarily through all previous messages:
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if (position == messages.size() - 1) {
//... do function
}
}
Is there a way to make the program only call onBindViewHolder for the newest item that's been added? I also saw this forum, but as I'm a beginner I couldn't tell if they were having the same issue as me.
RecyclerView populating each item every time i add a new item
notifyDataSetChanged() always reloads the whole view. Use notifyItemInserted() instead.
public void addMessage (boolean sent, String msgContent) {
messages.add(new MessageItem(sent, msgContent));
notifyItemInserted(messages.size()-1);
}
Don't use notifyDataSetChanged() method, you can use notifyItemInserted() method, this will not refresh every time.
public void addMessage (boolean sent, String msgContent) {
messages.add(new MessageItem(sent, msgContent));
notifyItemInserted(messages.size()-1);}

Listening for button click and linking fragment with view model

Im trying to learn view models and implement them in my app. I have been following a tutorial on getting me started but, I have a couple questions.
How do i listen for a button click? Since all the business logic is suppose to be stored in the view model would I put an OnClick listener there? Or would i put it with my onChange method in the activity that launches the fragment?
How to tell the fragment to use the view model?
Update was looking at this guys tutorial Location of click event in MVVM architecture . Isn't the whole point of mvvm to eliminate the need of interfaces?
Update 2: Found where you can use data binding to shove OnClick listener into button here: Handle onClick event with Databinding and MVVM and Using DataBinding library for binding events
Live data observe code from activity launching fragment
//private BattleRhythmViewModel battleModel;
battleModel = ViewModelProviders.of(this).get(BattleRhythmViewModel.class);
battleModel.getEvents().observe(this, new Observer<ArrayList<Event>>() {
#Override
public void onChanged(#Nullable ArrayList<Event> events) {
// Add newly created events to array/recycler view
// Another one for pushing new platform/content to database
}
});
}
View model for fragment
public class BattleRhythmViewModel extends ViewModel {
private MutableLiveData<ArrayList<Event>> battleRhythmEvents;
private MutableLiveData<ArrayList<TableData>> battleRhythmExtra;
public LiveData<ArrayList<Event>> getEvents()
{
return battleRhythmEvents;
}
public LiveData<ArrayList<TableData>> getExtras()
{
return battleRhythmExtra;
}
}

Why would getItem in onBindViewHolder ever be null?

So, i'm trying to follow the paging library. and in most examples, they have something like:
#Override
public void onBindViewHolder(#NonNull PokemonViewHolder pokemonViewHolder, int i) {
Pokemon pokemon = getItem(i);
if (pokemon != null) { // <-- why this check here?
pokemonViewHolder.bind(pokemon);
}
}
Why do you have to check for the item in the adapter being null? I'm not understanding the internals of the PagedListAdapter flow. Could anyone please explain?
My guess is that we have an observer on the adapter that "nukes" the adapter's content from the UI thread at some point, as soon as the datasource is updated, and thus this item position is outdated?
The PagedList always has the full size of the dataset. The
PagedList will by default return null for data that isn't loaded
yet.
This means that in our adapter we do need to remember to check for
null in our bind method.
http://blog.abnormallydriven.com/2017/09/30/introducing-the-paging-library/
It's explained in the official docs:
#Override
public void onBindViewHolder(UserViewHolder holder, int position) {
User user = getItem(position);
if (user != null) {
holder.bindTo(user);
} else {
// Null defines a placeholder item - PagedListAdapter will automatically invalidate
// this row when the actual object is loaded from the database
holder.clear();
}
}
https://developer.android.com/reference/android/arch/paging/PagedListAdapter

Notifying adapter from model

I'm trying to make changes to my android application by adopting the MVP pattern, but I'm having trouble with where to notify the adapter the recyclerview is using.
What I'm currently doing is giving a reference to the adapter in my model and notifying it as changes are made when click events happen, like so:
public class MyModel {
private MyAdapter adapter;
...
public void setAdapter(MyAdapter adapter) { this.adapter = adapter; }
public void action() {
// make changes to model and notify adapter as changes are
// made to individual items
...
adapter.notifyItemChanged(position)
}
}
I'm wonder what the conventional way of handling this kind of behaviour is using the MVP pattern.
The Observer pattern might be what you are looking for. When changes are made to the model you can notify the Observers (The presenters) so they can then update the view.
http://en.wikipedia.org/wiki/Observer_pattern

Android view not updating on main thread

I have an array of data model items in the application class. My first activity has a broadcast receiver that alters the values of items in the list (via push if you are curious).
I have two kinds of visual representation for a model item and I want to be able to update the view whenever I get a push, regardless of where I am in the application.
My solution was that every model item has an interface reference to their view classes, so when I update the model, the model tells the interfaces to update the views.
My problem is that the views do not update, and I have verified, the code is running on the main thread.
public interface IWidget {
void setValue(int value);
}
public class MyView implements IWidget {
#Override
void setValue(int value){
}
}
public class Model{
int value;
IWidget iWidget;
public void setWidget(IWidget iWidget){
this.iWidget = iWidget;
}
public void setValue(int value){
this.value = value;
iWidget.setValue(value);
}
}
I was generating a view twice on 2 fragments of view pager, so I was not looking at the right one. The instance that was updating was the last one (not on the fragment I was initially looking on).

Categories

Resources