Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EpoxyModel#prebind hook #1225

Merged
merged 3 commits into from
Aug 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,43 @@ public View buildView(@NonNull ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(getLayout(), parent, false);
}

/**
* Hook that is called before {@link #bind(Object)}. This is similar to
* {@link GeneratedModel#handlePreBind(EpoxyViewHolder, Object, int)}, but is intended for
* subclasses of EpoxyModel to hook into rather than for generated code to hook into.
* Overriding preBind is useful to capture state before the view changes, e.g. for animations.
*
* @param previouslyBoundModel This is a model with the same id that was previously bound. You can
* compare this previous model with the current one to see exactly
* what changed.
* <p>
* This model and the previously bound model are guaranteed to have
* the same id, but will not necessarily be of the same type depending
* on your implementation of {@link EpoxyController#buildModels()}.
* With common usage patterns of Epoxy they should be the same type,
* and will only differ if you are using different model classes with
* the same id.
* <p>
* Comparing the newly bound model with the previous model allows you
* to be more intelligent when binding your view. This may help you
* optimize view binding, or make it easier to work with animations.
* <p>
* If the new model and the previous model have the same view type
* (given by {@link EpoxyModel#getViewType()}), and if you are using
* the default ReyclerView item animator, the same view will be
* reused. This means that you only need to update the view to reflect
* the data that changed. If you are using a custom item animator then
* the view will be the same if the animator returns true in
* canReuseUpdatedViewHolder.
* <p>
* This previously bound model is taken as a payload from the diffing
* process, and follows the same general conditions for all
* recyclerview change payloads.
*/
public void preBind(@NonNull T view, @Nullable EpoxyModel<?> previouslyBoundModel) {

}

/**
* Binds the current data to the given view. You should bind all fields including unset/empty
* fields to ensure proper recycling.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public void bind(@SuppressWarnings("rawtypes") EpoxyModel model,
((GeneratedModel) model).handlePreBind(this, objectToBind(), position);
}

// noinspection unchecked
model.preBind(objectToBind(), previouslyBoundModel);

if (previouslyBoundModel != null) {
// noinspection unchecked
model.bind(objectToBind(), previouslyBoundModel);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.airbnb.epoxy

import android.view.View
import android.view.ViewParent
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.inOrder
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class EpoxyViewHolderTest {
private lateinit var epoxyViewHolder: EpoxyViewHolder

@Mock
lateinit var viewParent: ViewParent

@Mock
lateinit var view: View

@Mock
lateinit var model: TestModel

@Mock
lateinit var previousModel: TestModel

@Before
fun setup() {
MockitoAnnotations.openMocks(this)
epoxyViewHolder = EpoxyViewHolder(viewParent, view, false)
}

@Test
fun testBindCallsPreBindWithPrevious() {
epoxyViewHolder.bind(model, previousModel, emptyList(), 0)
val inOrder = inOrder(model)
inOrder.verify(model).preBind(view, previousModel)
inOrder.verify(model).bind(view, previousModel)
}

@Test
fun testBindCallsPreBind() {
epoxyViewHolder.bind(model, null, emptyList(), 0)
val inOrder = inOrder(model)
inOrder.verify(model).preBind(view, null)
inOrder.verify(model).bind(view)
}
}