Reputation: 2307
I want to animate a textView
inside a recyclerView
to transition upwards when I call notifyItemChanged(position)
. This is my onBindViewholder()
that is being called automatically when a change in the adapter occurs:
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
if (unfoldedIndexes.contains(position)) { // if already unfolded
bindUnfoldedState(holder, holder.getLayoutPosition());
}else{ // show the folded state}
}
public void bindUnfoldedState(ViewHolder holder, int position){
if(lc.isBtnEnabled()){
holder.Price.setText("text");
holder.Price.animate().alpha(1.0f).setDuration(500);
holder.Price.animate().translationY(-38).setDuration(500);
holder.checkText.animate().translationY(38).setDuration(500);
}else {
holder.Price.animate().alpha(0.0f).setDuration(500);
holder.Price.animate().translationY(0).setDuration(500);
holder.checkText.animate().translationY(0).setDuration(500);
}
}
my notifyItemChanged()
is being called from a fragment
onClick
like so:
ok.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
// some changes to the data
adapter.getLcns().get(Position).setBtnEnabled(true);
adapter.notifyItemChanged(Position);
}
}
isBtnEnabled()
is true if setBtnEnabled(true)
is called.
The problem is that the animation is not happening. With logs I can confirm that onBindViewHolder()
is being called and the animation should appear but it is not happening.
I was using anormal listView
before I converted to a recyclerView
and the animations were working in on getView()
Upvotes: 9
Views: 2192
Reputation: 114
You can set a global flag(say boolean) in Adapter, make the boolean false when Adapter is called first time, and make a public method which set the flag true and notifies at that position.
The code may look like this
private boolean animation=false;
public notifyAtPosition(int position){
animation=true;
notifyItemChanged(position);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
if (unfoldedIndexes.contains(position)) { // if already unfolded
bindUnfoldedState(holder, holder.getLayoutPosition());
}else{ // show the folded state}
}
public void bindUnfoldedState(ViewHolder holder, int position){
if(animation){
holder.Price.setText("text");
holder.Price.animate().alpha(1.0f).setDuration(500);
holder.Price.animate().translationY(-38).setDuration(500);
holder.checkText.animate().translationY(38).setDuration(500);
}else {
holder.Price.animate().alpha(0.0f).setDuration(500);
holder.Price.animate().translationY(0).setDuration(500);
holder.checkText.animate().translationY(0).setDuration(500);
}
}
Hope, you got the answer.
Upvotes: 0
Reputation: 10012
notifyItemChange()
without payload will force the item to redraw whereas you want to animate existing View
. The idea is to get the View
from RecyclerView
and then perform the animation directly onto it. e.g.
ok.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Adapter.ViewHolder holder = (Adapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(Position);
adapter.getLcns().get(Position).setBtnEnabled(true);
if (holder != null){
holder.Price.setText("text");
holder.Price.animate().alpha(1.0f).setDuration(500);
holder.Price.animate().translationY(-38).setDuration(500);
holder.checkText.animate().translationY(38).setDuration(500);
}
}
});
Note the line
Adapter.ViewHolder holder = (Adapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(Position);
here the findViewHolderForAdapterPosition
is returning the RecyclerView.ViewHolder
which you need to typecast into your ViewHolder
or
use the nofityItemChanged with payload:
from the docs
Client can optionally pass a payload for partial change. These payloads will be merged and may be passed to adapter's onBindViewHolder(ViewHolder, int, List) if the item is already represented by a ViewHolder and it will be rebound to the same ViewHolder
Upvotes: 0
Reputation: 7425
Try to invoke setHasStableIds(true)
in the adapter constructor. It is much easier to animate in onBindViewHolder
when ids are stable.
Upvotes: 0
Reputation: 17258
Use Adapter.notifyItemChanged(position, payload)
to deploy an update to already visible ViewHolders:
OnClick:
ok.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
// some changes to the data
adapter.getLcns().get(Position).setBtnEnabled(true);
adapter.notifyItemChanged(Position, true); // "true" is the payload
}
}
Adapter:
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, List<Object> payloads) {
if(payloads != null && !payloads.isEmpty()){ // update payloads, partial rebind
Object lastPayload = payloads.get(payloads.size -1)); // in this case only last payload is meaningful
if(lastPayload instanceof Boolean)
bindUnfoldedState(holder, lastPayload);
}else
onBindViewHolder(holder, position); // regular binding of item
}
public void bindUnfoldedState(ViewHolder holder, boolean unfold){
if(unfold){ ... }
else { ... }
}
// you should also consider adding this override to prevent animation leaking when viewholder
// is recycled and reused for another position:
@Override
public void onViewRecycled(ViewHolder holder){
holder.Price.animate().cancel();
holder.checkText.animate().cancel();
}
Upvotes: 1