-
Notifications
You must be signed in to change notification settings - Fork 554
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
First Sticky header remains sticky and following header do not replace it #314
Comments
@henengel, thank you for your congratulations! Then, the item web should also belongs to the same section, you need to add section as well. I don't remember well now, but if there are gaps stickyHeader will not work well. If so you should add section to it. As last thing, |
@davideas Thank you for your time and reply.
I would very much appreciate your input. The relevant code: Main RecyclerView (where I tried to add onScrollAnimation): public class MainFeedFragment extends Fragment {
/**
* Constants
*/
public static final String TAG = "MainFeedFragment";
/**
* General Obj
*/
private Context mContext;
private DataUtils mDataUtils;
private RecyclerView mRecyclerView;
private FlexibleAdapter<AbstractItem> mAdapter;
private ArrayList<AbstractItem> mResults;
private GeneralUtils mGeneralUtils;
private AnalyticsUtils mAnalyticsUtils;
/**
* ===========
* Constructor
* ===========
*/
public static MainFeedFragment newInstance(Context context, ArrayList<AbstractItem> results) {
MainFeedFragment fragment = new MainFeedFragment();
fragment.mContext = context;
fragment.mResults = results;
fragment.mGeneralUtils = GeneralUtils.getInstance();
fragment.mAnalyticsUtils = AnalyticsUtils.getInstance();
fragment.mDataUtils = DataUtils.getInstance();
return fragment;
}
/**
* =================
* Lifecycle methods
* =================
*/
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
/* Create rootView */
View rootView = inflater.inflate(R.layout.fragment_main_feed, container, false);
/* Init main feed */
initFeed(rootView);
/* Send screen name to analytics */
mAnalyticsUtils.sendScreenTracker(AnalyticsScreenName.SCREEN_VIEW_FEED, mDataUtils.getGeneral().getMainFeedUrl());
return rootView;
}
/**
* ==============
* Helper methods
* ==============
*/
private void initFeed(View rootView) {
/* Get recyclerView from layout */
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewMain);
/* Save views to cache to prevent reload of them*/
mRecyclerView.setItemViewCacheSize(80);
mRecyclerView.setHasFixedSize(true);
/* Create the adapter */
mAdapter = new FlexibleAdapter<>(mResults);
/* Create pre cache layout manager to load views before displayed to user */
PreCachingLayoutManager layoutManager = new PreCachingLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
/* Set the space of views that are not visible, to pre load them */
layoutManager.setExtraLayoutSpace(mGeneralUtils.getScreenHeight(getActivity()));
mRecyclerView.setLayoutManager(layoutManager);
mAdapter.setAnimationOnScrolling(true);
DefaultItemAnimator animator = new DefaultItemAnimator();
mRecyclerView.setItemAnimator(animator);
/* Set adapter */
mRecyclerView.setAdapter(mAdapter);
mAdapter.setDisplayHeadersAtStartUp(true).setStickyHeaders(true);
}
public void onDataChange(final ArrayList<AbstractItem> newResults) {
/* Save scroll position */
Parcelable recyclerViewState;
recyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState();
/*mAdapter.onDataChanged(newResults);*/
MyDiffUtil myDiffUtil = new MyDiffUtil(mResults, newResults);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(myDiffUtil);
mResults.clear();
mResults.addAll(newResults);
diffResult.dispatchUpdatesTo(mAdapter);
/* Restore scroll position */
mRecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
/* Set new results to default */
mResults = newResults;
}
public RecyclerView getRecyclerView(){
return this.mRecyclerView;
}
} One of the VH that I overrode scrollAnimaor (all VH have the same override): public class SmallArticleViewHolder extends FlexibleViewHolder{
public TextView title, subtitle, time;
public ImageView feedArticleVideoButton, image, feedArticleCommentsIcon;
public ImageButton feedArticleShareButton;
public CustomTextView feedArticleCommentsNumber;
public LinearLayout smallArticleRootView;
public SmallArticleViewHolder(View convertView, FlexibleAdapter adapter) {
super(convertView, adapter);
this.image = (ImageView) convertView.findViewById(R.id.feedSmallArticleMainImage);
this.title = (TextView) convertView.findViewById(R.id.feedSmallArticleTitle);
this.subtitle = (TextView) convertView.findViewById(R.id.feedSmallArticleSubtitle);
this.time = (TextView) convertView.findViewById(R.id.feedSmallArticleTime);
this.feedArticleVideoButton = (ImageView) convertView.findViewById(R.id.feedSmallArticleVideoButton);
this.feedArticleCommentsNumber = (CustomTextView) convertView.findViewById(R.id.feedSmallArticleCommentsNumber);
this.feedArticleCommentsIcon = (ImageView) convertView.findViewById(R.id.feedSmallArticleCommentsIcon);
this.feedArticleShareButton = (ImageButton) convertView.findViewById(R.id.feedSmallArticleShareButton);
this.smallArticleRootView = (LinearLayout) convertView.findViewById(R.id.smallArticleRootView);
}
@Override
public void scrollAnimators(@NonNull List<Animator> animators, int position, boolean isForward) {
AnimatorHelper.slideInFromLeftAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f);
}
} Section RecyclerView init (where 2 sticky headers are displayed when opening the same Section for the second time): public class SectionFeedFragment extends Fragment {
/**
* Constants
*/
public static final String TAG = "SectionFeedFragment";
/**
* General Obj
*/
private Context mContext;
private RecyclerView mRecyclerView;
private FlexibleAdapter mAdapter;
private ArrayList<AbstractItem> mResults;
private GeneralUtils mGeneralUtils;
private AnalyticsUtils mAnalyticsUtils;
private String mSectionUrl;
/**
* ===========
* Constructor
* ===========
*/
public static SectionFeedFragment newInstance(Context context, ArrayList<AbstractItem> results, String sectionUrl) {
SectionFeedFragment fragment = new SectionFeedFragment();
fragment.mContext = context;
fragment.mGeneralUtils = GeneralUtils.getInstance();
fragment.mResults = results;
fragment.mAnalyticsUtils = AnalyticsUtils.getInstance();
fragment.mSectionUrl = sectionUrl;
return fragment;
}
/**
* =================
* Lifecycle methods
* =================
*/
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
/* Create rootView */
View rootView = inflater.inflate(R.layout.fragment_section_feed, container, false);
/* Init main feed */
initFeed(rootView);
/* Send screen name to analytics */
mAnalyticsUtils.sendScreenTracker(AnalyticsScreenName.SCREEN_VIEW_FEED, mSectionUrl);
return rootView;
}
/**
* ==============
* Helper methods
* ==============
*/
private void initFeed(View rootView) {
/* Get recyclerView from layout */
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewSection);
/* Save views to cache to prevent reload of them*/
mRecyclerView.setItemViewCacheSize(80);
mRecyclerView.setHasFixedSize(true);
/* Create the adapter */
mAdapter = new FlexibleAdapter<>(mResults);
/* Create pre cache layout manager to load views before displayed to user */
PreCachingLayoutManager layoutManager = new PreCachingLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
/* Set the space of views that are not visible, to pre load them */
layoutManager.setExtraLayoutSpace(mGeneralUtils.getScreenHeight(getActivity()));
mRecyclerView.setLayoutManager(layoutManager);
/* Set adapter */
mRecyclerView.setAdapter(mAdapter);
mAdapter.setDisplayHeadersAtStartUp(true).setStickyHeaders(true);
}
public void onDataChange(ArrayList<AbstractItem> newResults) {
/* Save scroll position */
Parcelable recyclerViewState;
recyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState();
/*mAdapter.onDataChanged(newResults);*/
MyDiffUtil myDiffUtil = new MyDiffUtil(mResults, newResults);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(myDiffUtil);
mResults.clear();
mResults.addAll(newResults);
diffResult.dispatchUpdatesTo(mAdapter);
/* Restore scroll position */
mRecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
/* Set new results to default */
mResults = newResults;
}
} A short video display of the sticky issue: If you need more information like the list created or anything else, I will be more then happy to provide you that. Again thank you very much for your time and effort. |
@henengel, about your first issue, I tried your exact order of configuration even from Then you don't need DiffUtil at all, just call About your second issue, this seems much more complicated. I understood you create a new Fragment in the section you click? But why you load another RV and so another adapter? can you create expandable items with subItems in it? |
@davideas About my first issue, you were correct. By changing the layout manager, the animation started to work. As for my second issue:
You are correct.
When you say "another reference", by creating another adapter, don't they get another reference?
In my case, every Section and it's few items represent a category with dedicated items in that category.
Thank you |
@davideas Hi, I think I issue has something to do with the ID's. |
I mean another adapter item reference, each adapter displayed simultaneously should have items with their own flags.
I see 2 ways, now you display a big item with a fragment in it with another recyclerview in it. One solution is to use the category as expandable that contains subitems, you start with all items collapsed. Another solution is to open a new activity from a side that enters as a subfolder as Dropbox is doing.
For the moment, you need to review your architecture because it looks complicated and 2 adapters add another level of complex.
Well, it is certainly nice from your side, actually when I started this project was I did not thought at it took this success. I have to think at it. |
@davideas Fixing the id's didn't solve the issue.
If I understand correctly this is already implemented. Can you elaborate on the Iholder interface? |
@davideas After debugging a bit, I found that the double Header item is probably a result of not preparing the data for the adapter properly.
Reproduce the issue: Relevant code: private void parsDataResultsForSectionFeed(final String response, final VolleyOnFeedDataSuccessCallback callback) {
/* Create a new object that contains a list of gson array's */
mGsonResults = mGsonParser.fromJson(response, ItemObjectResults.class);
/* If SectionId equals null, this SectionFeed is created for the first time.
* If SectionId equals SectionId from response, try and update current Section, else
* create a new SectionFeed from response */
if (mSectionId != null
&& mSectionId.equals(mGsonResults.getSectionId())) {
/* If saved last updated id equals the one from response, the data of this SectionFeed
* wasn't changed, and return, else update the data */
if (mLastUpdatedSection != mGsonResults.getLastUpdated()) {
/* Parse gson array's to an ArrayList */
mResultsSection = parseResultsToItems(mGsonResults.getItems(), false);
/* Get taboola items */
mAdsUtils.getTaboolaFromServerForFeed(mResultsSection, mNumberOfTaboolaItems, new VolleyOnTaboolaSuccessCallback() {
@Override
public void onTaboolaSuccess() {
mResultsSection = mAdsUtils.getResults();
callback.onFeedDataSuccess(mResultsSection, true);
}
});
/* Set current section id */
mSectionId = mGsonResults.getSectionId();
/* Update last updated id */
mLastUpdatedSection = mGsonResults.getLastUpdated();
} else {
/* Feed data wasn't updated, return */
callback.onFeedDataSuccess(mResultsSection, false);
}
} else {
/* Create Feed for the first time */
/* Parse gson array's to an ArrayList */
mResultsSection = parseResultsToItems(mGsonResults.getItems(), false);
mAdsUtils.getTaboolaFromServerForFeed(mResultsSection, mNumberOfTaboolaItems, new VolleyOnTaboolaSuccessCallback() {
@Override
public void onTaboolaSuccess() {
mResultsSection = mAdsUtils.getResults();
callback.onFeedDataSuccess(mResultsSection, true);
}
});
/* Set current section id */
mSectionId = mGsonResults.getSectionId();
/* Update last updated */
mLastUpdatedSection = mGsonResults.getLastUpdated();
}
} private ArrayList<AbstractItem> parseResultsToItems(List<Item> gsonResults, boolean showSectionArrow) {
/** =============================================================================================
* BigArticleItem(itemType, name, title, subtitle, pubDate, wapLink, finalUrl, imageUrl,
* hasVideo, hasComments, itemCategoryColor, comments, categoryId, targetingCategory)
*
* SmallArticleItem(itemType, name, title, subtitle, pubDate, wapLink, finalUrl, imageUrl,
* hasVideo, hasComments, itemCategoryColor, comments, categoryId, targetingCategory)
*
* WebItem(itemType, wvUrl, wvWidth, wvHeight)
*
* SectionItem(itemType, name, color, finalUrl, url, categoryId, targetingUrl, title, subtitle)
*
* UrlBannerItem(itemType, type, iu, width, height)
*
* XmlBannerItem(itemType, type, iu, width, height)
*
* TaboolaItem(itemType, title, pubDate, imageUrl, finalUrl, itemResponseId)
* ============================================================================================== */
ArrayList<AbstractItem> items = new ArrayList<>();
SectionItem currentSection = null;
for (int i = 0; i < gsonResults.size(); i++) {
Item itemObject = gsonResults.get(i);
if (itemObject.getItemType().equals("ARTICLE - big")) {
BigArticleItem item = new BigArticleItem(mContext, currentSection, itemObject.getItemId(), itemObject.getItemType(), itemObject.getName(), itemObject.getTitle(), itemObject.getSubtitle(),
itemObject.getPubDate(), itemObject.getWapLink(), itemObject.getFinaleUrl(), itemObject.getImage(), itemObject.getHasVideo(),
itemObject.getHasComments(), itemObject.getItemCategoryColor(), itemObject.getComments(), itemObject.getCategoryID(),
itemObject.getTargetingCategory(), itemObject.getArticleID());
items.add(item);
} else if (itemObject.getItemType().equals("ARTICLE")) {
SmallArticleItem item = new SmallArticleItem(mContext, currentSection, itemObject.getItemId(), itemObject.getItemType(), itemObject.getName(), itemObject.getTitle(), itemObject.getSubtitle(),
itemObject.getPubDate(), itemObject.getWapLink(), itemObject.getFinaleUrl(), itemObject.getImage(), itemObject.getHasVideo(),
itemObject.getHasComments(), itemObject.getItemCategoryColor(), itemObject.getComments(), itemObject.getCategoryID(),
itemObject.getTargetingCategory(), itemObject.getArticleID());
items.add(item);
} else if (itemObject.getItemType().equals("web")) {
WebViewItem item = new WebViewItem(currentSection, itemObject.getItemId(), itemObject.getItemType(), itemObject.getWvUrl(), itemObject.getWvWidth(), itemObject.getWvHeight());
items.add(item);
} else if (itemObject.getItemType().equals("sectionTitle")) {
if (showSectionArrow == false) {
SectionItem item = new SectionItem(itemObject.getItemId(), itemObject.getItemType(), itemObject.getName(), itemObject.getColor(), itemObject.getFinaleUrl(), itemObject.getUrl(),
itemObject.getCategoryID(), itemObject.getTargetingCategory(), itemObject.getTitle(), itemObject.getSubtitle(), false);
currentSection = item;
} else if (showSectionArrow == true) {
SectionItem item = new SectionItem(itemObject.getItemId(), itemObject.getItemType(), itemObject.getName(), itemObject.getColor(), itemObject.getFinaleUrl(), itemObject.getUrl(),
itemObject.getCategoryID(), itemObject.getTargetingCategory(), itemObject.getTitle(), itemObject.getSubtitle(), true);
currentSection = item;
}
} else if (itemObject.getItemType().equals("banner") && itemObject.getType().equals("url")) {
UrlBannerItem item = new UrlBannerItem(mContext, currentSection, itemObject.getItemId(), itemObject.getItemType(), itemObject.getType(), itemObject.getIu(), itemObject.getWidth(), itemObject.getHeight());
items.add(item);
} else if (itemObject.getItemType().equals("banner") && itemObject.getType().equals("xml")) {
XmlBannerItem item = new XmlBannerItem(mContext, currentSection, itemObject.getItemId(), itemObject.getItemType(), itemObject.getType(), itemObject.getIu(), itemObject.getWidth(), itemObject.getHeight());
items.add(item);
} else if (itemObject.getItemType().equals("taboola")) {
TaboolaItem item = new TaboolaItem(mContext, currentSection, itemObject.getItemId(), itemObject.getItemType(), itemObject.getTaboolaPublisherName(), itemObject.getTitle(), itemObject.getPubDate(), itemObject.getImage(), itemObject.getFinaleUrl(),
itemObject.getTaboolaItemResponseId());
items.add(item);
mNumberOfTaboolaItems++;
}
}
return items;
} Dataset when creating the dataset for the first time: Dataset when clicking the same Section/Header for the second/third time: |
@davideas Hi, it's been a while since we last communicated. |
@davideas Hi is everything OK? you haven't responded in a while... |
LOL, I'm sure you have!!! |
Hi there,
First I would like to congratulate you on very impressive work!
One remark if I may.
I am relatively a new dev, and the current sample app is very difficult to understand.
It would be helpful if along with the current app that shows the full potential of the adapter, there would be a simple one that shows basic setup. I covered several times the wiki pages and couldn't find all of my answers and solutions there.
To our issue, I created a new project to test the adapter and figure out how the adapter works.
The three items models I created are these:
And the MainActivity:
Can you please explain to me in short why i'm having to problem mentioned in the title?
Thank you
The text was updated successfully, but these errors were encountered: