airbnb / epoxy

Epoxy is an Android library for building complex screens in a RecyclerView
https://goo.gl/eIK82p
Apache License 2.0
8.5k stars 733 forks source link

Why refresh the data when RecyclerView slides into the middle #208

Closed e-hai closed 7 years ago

e-hai commented 7 years ago

mDatas.clear(); mDatas.addAll(photoList); mController.setData(mDatas);

e-hai commented 7 years ago

RecyclerView slides into the item with the same data

elihart commented 7 years ago

Sorry, I don't understand the question. Can you rephrase it maybe?

e-hai commented 7 years ago

@elihart q: "Normal" item data can be changed, "img" item data is the same.

(1) Initialize the state, 'img' item outside the screen. epox2

(2) After refresh data,why 'img' item in the screen. epox1

I want recyclerView to refresh after still showing the first item.

e-hai commented 7 years ago

1. activity

public class EpoxyActivity extends AppCompatActivity implements DemoController.AdapterCallbacks {
private RecyclerView recyclerView;
private DemoController controller;
private List<EpoxyData> dataList = new ArrayList<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_epoxy);

    findViewById(R.id.refresh).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            refreshData();
        }
    });

    recyclerView = (RecyclerView) findViewById(R.id.epoxy_rv);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    controller = new DemoController(this);
    controller.setData(dataList);
    recyclerView.setAdapter(controller.getAdapter());
    refreshData();
}

private int j = 0;

private void refreshData() {
    recyclerView.scrollToPosition(0);
    dataList.clear();
    int max = j + 15;
    for (int i = j; i < max; i++) {
        EpoxyData normalData = new EpoxyData();
        normalData.setId(i);
        normalData.setTxt("normal item=" + i);
        normalData.setColor(Color.GRAY);
        dataList.add(normalData);
        j++;
    }

    EpoxyData imgData = new EpoxyData();
    imgData.setTxt("img");
    imgData.setId(1000);
    imgData.setColor(Color.RED);
    dataList.add(9, imgData);
    controller.setData(dataList);
}

@Override
public void onAddCarouselClicked() {
}
}

2. EpoxyController

public class DemoController extends TypedEpoxyController<List<EpoxyData>> {
public interface AdapterCallbacks {
    void onAddCarouselClicked();
}

private final AdapterCallbacks callbacks;

public DemoController(AdapterCallbacks callbacks) {
    this.callbacks = callbacks;
}

@Override
protected void buildModels(List<EpoxyData> carousels) {

    for (int i = 0; i < carousels.size(); i++) {
        EpoxyData item = carousels.get(i);
        add(new TextModel_()
                .color(item.getColor())
                .txt(item.getTxt())
                .id(carousels.get(i).getId()
                )
        );
    }
}

@Override
protected void onExceptionSwallowed(RuntimeException exception) {
    // Best practice is to throw in debug so you are aware of any issues that Epoxy notices.
    // Otherwise Epoxy does its best to swallow these exceptions and continue gracefully
    throw exception;
}
}

3. data

public class EpoxyData {
private int id;
private String txt;
private int color;

public String getTxt() {
    return txt;
}
public void setTxt(String txt) {
    this.txt = txt;
}
public int getId() {
    return id;
}
public void setId(int id) {
    this.id = id;
}
public int getColor() {
    return color;
}
public void setColor(int color) {
    this.color = color;
}
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof EpoxyData)) return false;
    EpoxyData epoxyData = (EpoxyData) o;
    if (getId() != epoxyData.getId()) return false;
    if (getColor() != epoxyData.getColor()) return false;
    return getTxt() != null ? getTxt().equals(epoxyData.getTxt()) : epoxyData.getTxt() == null;
}
@Override
public int hashCode() {
    int result = getId();
    result = 31 * result + (getTxt() != null ? getTxt().hashCode() : 0);
    result = 31 * result + getColor();
    return result;
}
}

4.model

@EpoxyModelClass(layout = R.layout.model_text_layout)
public abstract class TextModel extends EpoxyModel<TextContentView> {
@EpoxyAttribute
String txt;

@EpoxyAttribute
@ColorInt
int color;

@EpoxyAttribute
boolean isSelect;

@EpoxyAttribute(DoNotHash)
View.OnClickListener clickListener;

@Override
public void bind(TextContentView view) {
    view.setTitle(txt);
    view.setTxtBgColor(color);
}
}

5.View

public class TextContentView extends RelativeLayout {
private TextView title;
public TextContentView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private void init() {
    inflate(getContext(), R.layout.view_txt_content, this);
    title = (TextView) findViewById(R.id.model_txt_tv);
}

public void setTitle(String txt) {
    title.setText(txt);
}

public void setTxtBgColor(int color) {
    title.setBackgroundColor(color);
}
}
elihart commented 7 years ago

Thanks for explaining. I think this is because of how RecyclerView processes its item change animations. I would guess that it does the removals and then insertions, and since the red item is the top item after the removals it is kept in view after the insertions.

I would think you need to configure recyclerview/layoutmanager to anchor or animate views, but I'm not sure the way to do that.

The easiest solution I can think of is to use 'scrollToPosition' after the changes to move where you want. Since models are built async you'd have to make sure to call after models are rebuilt.

Another option might be to use a custom item animator to change the animation duration or other details of the removal/insertion animation.

Maybe look into disabling setPreserveFocusAfterLayout on the recycler view

e-hai commented 7 years ago

Thanks for the answer.Because i do not need animation,so add code:

   recyclerView.setItemAnimator(null);

Solved my question! :+1:

elihart commented 7 years ago

glad you got it figured out! Thanks for reporting back

fobidlim commented 5 years ago

I had same issue on 2.16.3

recyclerView.setItemAnimator(null);

Above codes not working on me.

But I resolve issue with this code:

epoxy_recycler_view.post { epoxy_recycler_view.scrollToPosition(position) }