davideas / FlexibleAdapter

Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
Apache License 2.0
3.55k stars 553 forks source link

StackOverflowError when use adapter.setDisplayHeadersAtStartUp(true) or adapter.showAllHeaders(); #122

Closed 54binge closed 8 years ago

54binge commented 8 years ago

hi@davideas ,so sorry to disturb you,i try to make a sticky grid header,it can show items well when without adapter.setDisplayHeadersAtStartUp(true) or adapter.showAllHeaders(), but if i set one, it crashed, Caused by: java.lang.StackOverflowError: stack size 8MB at android.view.ViewGroup.resetResolvedLayoutDirection(ViewGroup.java:6335)

here is my code, wish you'r help

public class TestActivity2 extends BaseActivity { private static final String TAG = "TestActivity2";

HashMap<String, HeaderItem> headerMap = new HashMap<>();

private List<GridItem> dataList = new ArrayList<>();

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

    RecyclerView rcv = (RecyclerView) findViewById(R.id.rcv);
    rcv.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));

    getLocalData();

    FlexibleAdapter adapter = new FlexibleAdapter(dataList);
    rcv.setAdapter(adapter);
    rcv.addItemDecoration(new DividerItemDecoration(this, R.drawable.horizontal_line));
    adapter.setDisplayHeadersAtStartUp(true);
    adapter.showAllHeaders();
    adapter.enableStickyHeaders();
}

private void getLocalData() {
    AlbumUtil albumUtil = new AlbumUtil(this);
    ArrayList<AlbumFile> list = albumUtil.getLocalAlbumFiles();
    Collections.sort(list);

    for (AlbumFile albumFile : list) {
        if (headerMap.containsKey(albumFile.dayDate)) {
            HeaderItem headerItem = headerMap.get(albumFile.dayDate);
            GridItem gridItem = new GridItem(albumFile, headerItem);

            dataList.add(gridItem);
        } else {
            HeaderItem headerItem = new HeaderItem(albumFile.dayDate, false);
            headerMap.put(albumFile.dayDate, headerItem);

            GridItem gridItem = new GridItem(albumFile, headerItem);

            dataList.add(gridItem);
        }
    }
}

class HeaderItem extends AbstractFlexibleItem<HeaderItem.HeaderViewHolder> implements IHeader<HeaderItem.HeaderViewHolder> {
    String title;
    boolean isShowSelect;

    public HeaderItem(String title, boolean isShowSelect) {
        this.title = title;
        this.isShowSelect = isShowSelect;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isShowSelect() {
        return isShowSelect;
    }

    public void setShowSelect(boolean showSelect) {
        isShowSelect = showSelect;
    }

    @Override
    public boolean equals(Object inObject) {
        if (inObject instanceof HeaderItem) {
            HeaderItem inItem = (HeaderItem) inObject;
            return this.getTitle().equals(inItem.getTitle());
        }
        return false;
    }

    @Override
    public int getLayoutRes() {
        return R.layout.album_sticky_date_header;
    }

    @Override
    public HeaderViewHolder createViewHolder(FlexibleAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
        return new HeaderViewHolder(inflater.inflate(getLayoutRes(), parent, false), adapter);
    }

    @Override
    public void bindViewHolder(FlexibleAdapter adapter, HeaderViewHolder holder, int position, List payloads) {
        if (payloads.size() > 0) {

        } else {
            holder.dateTV.setText(title);
        }
    }

    class HeaderViewHolder extends FlexibleViewHolder {
        TextView dateTV;
        TextView selectTV;

        public HeaderViewHolder(View view, FlexibleAdapter adapter) {
            super(view, adapter);
            dateTV = (TextView) view.findViewById(R.id.date_tv);
            selectTV = (TextView) view.findViewById(R.id.select_all_tv);
            selectTV.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    v.setSelected(!v.isSelected());
                    showShortMessage(v.isSelected() ? "selectAll" : "unselectAll");
                }
            });
        }
    }
}

private class GridItem extends AbstractFlexibleItem<GridItem.GridViewHolder> implements ISectionable<GridItem.GridViewHolder, HeaderItem> {

    private AlbumFile albumFile;
    private HeaderItem headerItem;

    public GridItem(AlbumFile albumFile, HeaderItem headerItem) {
        this.albumFile = albumFile;
        this.headerItem = headerItem;
    }

    public AlbumFile getAlbumFile() {
        return albumFile;
    }

    public void setAlbumFile(AlbumFile albumFile) {
        this.albumFile = albumFile;
    }

    @Override
    public boolean equals(Object inObject) {
        if (inObject instanceof GridItem) {
            GridItem inItem = (GridItem) inObject;
            return getAlbumFile() == inItem.getAlbumFile();
        }
        return false;
    }

    @Override
    public int getLayoutRes() {
        return R.layout.album_grid_item;
    }

    @Override
    public GridViewHolder createViewHolder(FlexibleAdapter adapter, LayoutInflater inflater, ViewGroup parent) {
        return new GridViewHolder(inflater.inflate(getLayoutRes(), parent, false), adapter);
    }

    @Override
    public void bindViewHolder(FlexibleAdapter adapter, GridViewHolder holder, int position, List payloads) {
        AlbumItem albumItem = (AlbumItem) holder.itemView;
        albumItem.setAlbumFile(getAlbumFile());
    }

    @Override
    public HeaderItem getHeader() {
        return headerItem;
    }

    @Override
    public void setHeader(HeaderItem header) {
        this.headerItem = header;
    }

    public class GridViewHolder extends FlexibleViewHolder {

        public GridViewHolder(View view, FlexibleAdapter adapter) {
            super(view, adapter);

        }
    }
}

}

davideas commented 8 years ago

Hi @54binge, you don't have to call showAllHeaders() because that method is already called in setDisplayHeadersAtStartUp().

Also for the HeaderViewHolder, you need to pass true to the super constructor, to avoid some crashing issues when you will scroll.

54binge commented 8 years ago

Hi @davideas ,thanks for your reply. In fact, the application crashed immediately when setDisplayHeadersAtStartUp() is called,there is nothing to do with the scrolling. And I can't find where to pass "true" in the HeaderViewHolder constructor. looking forward to your next reply!

davideas commented 8 years ago

@54binge, indeed scrolling is a different thing, but if you don't find the new parameter means that you are using an old version, please use beta7, and you actually need it. (I've updated a little the Wiki for beta7).

Regarding the main problem, I think it's the double call, but I need to check better, in the meantime, please remove showAllHeaders() and try it out.

davideas commented 8 years ago

Another important thing, you have to provide a copy of dataList. At the moment that is necessary.

davideas commented 8 years ago

@54binge, I've tried your code, I removed the creation of dataList because I don't have the objects. But I used my own list and everything works fine, just the header is duplicated because duplicated is the call... Can you please enable logs and paste them here, and also can you try to run it debug mode?

54binge commented 8 years ago

@davideas it works fine use handler,i need research it more. @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test2);

    RecyclerView rcv = (RecyclerView) findViewById(R.id.rcv);
    rcv.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));

    getLocalData();

    final FlexibleAdapter adapter = new FlexibleAdapter(dataList);
    rcv.setAdapter(adapter);
    rcv.addItemDecoration(new DividerItemDecoration(this, R.drawable.horizontal_line));
    baseHandler.post(new Runnable() {
        @Override
        public void run() {
            adapter.showAllHeaders();
        }
    });

    adapter.enableStickyHeaders();

}
davideas commented 8 years ago

Well, it is a workaround, it would be interesting to see your logs, maybe there's a problem somewhere else.

davideas commented 8 years ago

@54binge, ok I've seen the logs and deleted the message, not useful. I need that you enable the logs of the Adapter and send that to me. Also put some logs in the various methods of your Activity so I can know in which point the execution is.

And please apply all the instructions that I told you previously. Thanks.

54binge commented 8 years ago

@davideas when i see the logs,i also think it not useful,even if i call FlexibleAdapter.enableLogs(true); just now, i put the method setAdapter() after method adapter.setDisplayHeadersAtStartUp(true), all become normal and works very well. thank you for these days!

davideas commented 8 years ago

Cool, so the problem is the order of the set? I wonder why the demo App doesn't crash too, can you put here the logs of when it crashes? Because I'm rewriting the Wiki pages just now and I need to be sure that the call setDisplayHeadersAtStartUp() can be after or must be before setting the Adapter to the RV. Thanks.

54binge commented 8 years ago

I pasted all logs when it crashed,when it became normal,i tried the order of method setDisplayHeadersAtStartUp(true),in fact,it is not the reason,i think it is better to call it after setAdapter(),maybe it is the time to restart android studio or reboot my computer :)

davideas commented 8 years ago

I understood that could be something related with the setContentView and view hierarchy even before the Adapter since you don't see any logs from it when you enableLogs().