Giter Site home page Giter Site logo

stickyheaders's Introduction

Android Arsenal

StickyHeaders

Easily add Sticky Headers to your RecyclerView

Setup

Implement StickyHeaderHandler in your Presenter/Adapter/Activity or whatever class has access to your RecyclerView adapter dataset

Make sure the parent of the RecyclerView is a FrameLayout or CoordinatorLayout (this will be verified at runtime). To avoid this runtime check, you can call StickyLayoutManager#disableParentViewRestrictions. This will cause bugs with some ViewParents, such as LinearLayout, so use with caution.

Instantiate a StickyLayoutManager and set that as the LayoutManager for your RecyclerView.

For items in your dataset that you want to act as sticky headers, implement the marker interface StickyHeader.

That's it! See the example app for more in depth details.

Additional Features

Add elevation to your headers (animated in and out) on Lollipop and above:

layoutManager.elevateHeaders(true) OR layoutManager.setElevation(int dp)

Add a listener to be notified when headers are attached/re-bound or detached:

StickyLayoutManager#setStickyHeaderListener

You will be passed the instance of the view that was either attached/re-bound or detached, as well as the adapter position of the data that view represents. It is important to note that the adapter position passed to headerDetached may not be the current position in the data set that the view was originally bound with. This can happen if the data has changed since that header was made sticky.

The adapter position passed in headerAttached will always be correct at that moment.

StickyHeaders

Add to your Gradle dependencies (Check badge at top for latest version):

dependencies {
    implementation 'com.brandongogetap:stickyheaders:0.6.2'
}

License

Copyright 2016 Brandon Gogetap

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

stickyheaders's People

Contributors

bgogetap avatar jaynewstrom avatar nkontizas avatar umfsimke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stickyheaders's Issues

How to support a SwipeRefreshLayout?

Hi, Just came across this neat library and tried to integrate it to my app. My RecyclerView Layout looks like this

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             xmlns:tools="http://schemas.android.com/tools"
             android:layout_width="match_parent"
             android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_scan"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"
            tools:listitem="@layout/item_list_wifi"/>

    </android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>

But when I run the app I get the following error
RecyclerView parent must be either a FrameLayout or CoordinatorLayout

Why must RecyclerView have a FrameLayout or CoordinatorLayout as a parent?
How Am I going to support a SwipeRefreshLayout if that's the case?

Double sticky headers showing at top of screen and start of RV

I have an AppBarLayout which takes up a certain portion of the screen, say 300dp. The RecyclerView starts after that with the first header, but the one header is initially shown at the top of the screen covering the AppBarLayout and another where the RecyclerView starts. It seems like there's always a header on top of the screen, but shouldn't be there until the first header hits the top when scrolling.

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            <!-- Stuff in here -->
        </LinearLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipToPadding="false"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android.support.design.widget.CoordinatorLayout>```

Header remains after setting a new adapter

I set a new adapter for my recyclerview, one that I do not intent to use sticky headers with, but the header still remains on top of the recyclerview. In this particular example I am using just one sticky header, so I do not know if this issue applies to other use cases as well.

Or is there a method to remove headers?

Negative ViewHolder position

Please add on readme to be careful of ViewHolder position, after an analyse of the source code, I discovered that the first header has a negative ID, this value can be a problem if you check it during the method onBindViewHolder, my code used to cancel the process if the ID is negative. I had problems to fill the copy header view and I solved it just treating this negative ID.

I was doing something like this:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {

    if (holder.adapterPosition < 0) {
      return
    }

    //Continue binding the ViewHolder...
}

Thank you.

Header jumps when user scroll up and higer header have more text than lower

Hi!
At first I would like to thank you for this super library :) I found a little bug which makes glitch as mentioned in title. To reproduce just replace com.brandongogetap.stickyheaders.demo.MainActivity#compileItems method with this code:

private static boolean doubler; private List<Item> compileItems() { List<Item> items = new ArrayList<>(); for (int i = 0; i < 100; i++) { if (i == 2 || (i % 4 == 0 && i > 0)) { doubler = !doubler; if (doubler) { items.add(new HeaderItem("Header at " + i, "header text header text header text header text header text header text header text header text header text")); }else { items.add(new HeaderItem("Header at " + i, "header text")); } } else { items.add(new Item("Item at " + i, "Item description at " + i)); } } return items; }

and then scroll to bottom and slowly scroll up. If this is not enough I can make a little video with this. Can I ask if this is little effort for you and can you fix this easily or I should fix it myself and then make pull request?
Thanks!

Header remains after using notifyDataSetChanged()

Hi,
I have a search feature and a filter feature in my list.
I replace the items in my list and I call notifyDataSetChanged() when I use these features. I don't replace the adapter.
The problem is when I scroll down the list and there is a header that is almost not visible, it remains visible when I call notifyDataSetChanged(), even if it shouldn't be because the list doesn't contain anymore this header.

Wrong behavior after list got updated

I detected now that when the items are removed from the list there is something wrong, if 1 item is removed or added then the item after/before the second header is detected as header, if 2 then the second item and so on. Is there a way to update the layoutManager? I suppose this should work automatically?

onBinViewHolder sometimes not called for headers

I have an issue that's mostly always reproducible where onBindViewHolder wouldn't be called for the sticky headers, do you have an idea if this error could come from the lib itself? Because it only happens with the headers. I can reproduce it in landscape mode where I have a list with:

  • Header
    -4 items visible
    -1 item part visible
    -1 item not visible
  • Header
    -4 items more

When I scroll a few times sometimes the first header would have the content of the second one when scrolling up again and sometimes the second header will have the content of the first one the first time I scroll down.

Scrolling to position doesn't always work

I believe this to be a limitation of the layout manager, but when I call "scrollToPosition" on the recycler with any value other than 0, it doesn't seem to want to go to the position.

Not sticky header

Hello.

The issue is when the gap between 2 header items is too huge. The next header will not stick if you have too much regular items after the first header.

Version 0.3.0 fix the issue.

Sync Header View and Normal View

@bgogetap : I want to sync both the views i.e view in normal state and view in header state.Lets suppose there is a radio group on that view and i want to sync the selected radio button in synced state.

Sticky header does not work if there is a view above RecyclerView

fix this bug by add those code below:
StickyLayoutManager.java

   RecyclerView recyclerView;
    @Override
    public void onAttachedToWindow(RecyclerView view) {
        super.onAttachedToWindow(view);
        recyclerView = view;
    }

ViewRetriever.java

final class RecyclerViewRetriever implements ViewRetriever {

        private RecyclerView.Recycler recycler;
        private RecyclerView recyclerView;

        RecyclerViewRetriever() {
        }

        RecyclerViewRetriever setRecycler(RecyclerView.Recycler recycler, RecyclerView recyclerView) {
            this.recycler = recycler;
            this.recyclerView = recyclerView;
            return this;
        }

        @Override
        public View getViewForPosition(int position) {
            View view = recycler.getViewForPosition(position);
            int[] positions = new int[2];
            recyclerView.getLocationInWindow(positions);
            Rect rectF = new Rect();
            recyclerView.getLocalVisibleRect(rectF);
            int[] positions2 = new int[2];
            recyclerView.getLocationOnScreen(positions2);
            ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            marginLayoutParams.topMargin = positions[1] - DecorUtils.getStratusBarHeight(view.getContext());
            recycler = null;
            return view;
        }
    }

DecorUtils.java

public static int getStratusBarHeight(Context context) {
        return getDimenSize(context, "status_bar_height");
    }

    private static int getDimenSize(Context context, String key) {
        int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
        return resourceId > 0 ? context.getResources().getDimensionPixelSize(resourceId) : 0;
    }

Don't really worked

I started using this lib but it has some problems. In the search for a better one I found this one, but I found it to be more complicated to be implemented, much more steps/changes required. At the end the headers are not being sticked, don't know why, I tried it with a LinearLayout, CoordinatorLayout as root and then a fragment pointing to the RecyclerView, but it does not work.

getAdapter position returns INVALID_POSITION

Click to expansion and collapse has been added to the source .But when i tap header after few scroll in child list getAdapterPosition return -1. for the click.but sticky header keeps its position as expected. When i scroll back and the first child will be bellow the header it return the adapter position as expected.

Is there any way to get the visible header position when scrolled

Headers are not in proper possition when user scroll very fast

Hi,
I noticed another issue: when there are few headers and user scroll very fast then header is on higher position. This looks like that:

Code I'm using:

private List<Item> compileItems() { List<Item> items = new ArrayList<>(); for (int i = 0; i < 100; i++) { if (i == 2 || (i % 50 == 0 && i > 0)) { items.add(new HeaderItem("Header at " + i, "")); } else { items.add(new Item("Item at " + i, "Item description at " + i)); } } return items; }

Please consider removing android:allowBackup="true" from library Manifest

Having the android:allowBackup="true" attribute in the library Manifest forces projects using this library to override this value using the tools:replace in the main project section if the project referencing this library has the android:allowBackup value set to false. It would be helpful if this was removed from the library Manifest to make the Manifest merge and build process cleaner. Thanks!

sticky header with background not working

I followed below code but my header is not sticky and even its not showing any background color in UI.

List items = compileItems();
RecyclerAdapter adapter = new RecyclerAdapter(items);
StickyLayoutManager layoutManager = new TopSnappedStickyLayoutManager(this, adapter);
layoutManager.elevateHeaders(true);
layoutManager.elevateHeaders(10);
rvHeader.setLayoutManager(layoutManager);
rvHeader.setAdapter(adapter);

private List compileItems() {
List items = new ArrayList<>();
for (int i = 0; i < 100; i++) {
if (i == 2 || (i % 4 == 0 && i > 0)) {
items.add(new HeaderItem("Header at " + i, ""));
} else {
items.add(new Item("Item at " + i, "Item description at " + i));
}
}
return items;
}

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.BaseViewHolder> implements StickyHeaderHandler {

@Override
public List<Item> getAdapterData() {
    return data;
}

private final List<Item> data;

RecyclerAdapter(List<Item> data) {
    this.data = data;
}

@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = from(parent.getContext()).inflate(R.layout.item_view, parent, false);
    final BaseViewHolder viewHolder;
    if (viewType == 0) {
        viewHolder = new MyViewHolder(view);
    } else {
        viewHolder = new MyOtherViewHolder(view);
    }
    view.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            // This is unsafe to do in OnClickListeners attached to sticky headers. The adapter
            // position of the holder will be out of sync if any items have been added/removed.
            // If a click listener needs to be set on a sticky header, it is recommended to identify the header
            // based on its backing model, rather than position in the data set.
            int position = viewHolder.getAdapterPosition();
            if (position != NO_POSITION) {
                data.remove(position);
                notifyDataSetChanged();
            }
        }
    });
    return viewHolder;
}

@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
    Item item = data.get(position);
    holder.titleTextView.setText(item.title);
    holder.messageTextView.setText(item.message);
    if (position != 0 && position % 16 == 0) {
        holder.itemView.setPadding(0, 100, 0, 100);
    } else {
        holder.itemView.setPadding(0, 0, 0, 0);
    }
    if (item instanceof StickyHeader) {
        holder.itemView.setBackgroundColor(Color.CYAN);
    } else {
        holder.itemView.setBackgroundColor(Color.TRANSPARENT);
    }
}

@Override public int getItemCount() {
    return data != null ? data.size() : 0;
}

@Override public int getItemViewType(int position) {
    if (position != 0 && position % 16 == 0) {
        return 1;
    }
    return 0;
}

private static final class MyViewHolder extends BaseViewHolder {

    MyViewHolder(View itemView) {
        super(itemView);
    }
}

private static final class MyOtherViewHolder extends BaseViewHolder {

    MyOtherViewHolder(View itemView) {
        super(itemView);
    }
}

static class BaseViewHolder extends RecyclerView.ViewHolder {

    TextView titleTextView;
    TextView messageTextView;

    BaseViewHolder(View itemView) {
        super(itemView);
        titleTextView = (TextView) itemView.findViewById(R.id.tv_title);
        messageTextView = (TextView) itemView.findViewById(R.id.tv_message);
    }
}

}

No shadow/divider between headers

What I could see now is that when 2 headers encounter themselves the shadow of the top one disappears. Is this intentionally? In the Google i/o app the shadow remains while one header pushes the other one.

Improvements

  1. Why do I need TopSnappedStickyLayoutManager?
  2. Isn't the default elavation of 5dp to much? The Toolbar have like 4dp I think and the if you check the Google I/O App they use 2dp for StickyHeaders, maybe you want to set the default also to 2dp?
  3. In the docu you say the RecyclerView should be in a FrameLayout or CoordinatorLayout, why is this? I am using it in fragment->CardView->LL->LL->LL and it seems to work.
  4. Instead of needing to implement StickyHeader which implies adding a new class to my code why not extending the adapter with a callback isThisStickyHeader? I think this would be more practical.

Crash when use scrollToPosition

(Sorry for my English skill @@)
Yes as title, i get crash when use scrollToPosition
Have any solution for fix it? Thank you so much.
Here my log
ava.lang.ClassCastException: android.widget.FrameLayout$LayoutParams cannot be cast to android.support.v7.widget.RecyclerView$LayoutParams at android.support.v7.widget.RecyclerView.getChildViewHolderInt(RecyclerView.java:3979) at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:7719) at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3388) at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3135) at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3568) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344) at android.widget.FrameLayout.onLayout(FrameLayout.java:281) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1742) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585) at android.widget.LinearLayout.onLayout(LinearLayout.java:1494) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1080) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344) at android.widget.FrameLayout.onLayout(FrameLayout.java:281) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1767) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:131) at android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42) at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1364) at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:852) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1080) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344) at android.widget.FrameLayout.onLayout(FrameLayout.java:281) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1742) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585) at android.widget.LinearLayout.onLayout(LinearLayout.java:1494) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344) at android.widget.FrameLayout.onLayout(FrameLayout.java:281) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1742) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585) at android.widget.LinearLayout.onLayout(LinearLayout.java:1494) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344) at android.widget.FrameLayout.onLayout(FrameLayout.java:281) at com.android.internal.policy.PhoneWindow$DecorView.onLayout(PhoneWindow.java:3193) at android.view.View.layout(View.java:17938) at android.view.ViewGroup.layout(ViewGroup.java:5812) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2666) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2367)

Pinned item not updated when notifyDataSetChange on adapter

For my implementation of the library i need to change the content of the RecyclerView ofter, so I've implemented an adapter that simply refresh the data when needed and call notifyDataSetChange(). It works, but is not refreshing the pinned item attached on the top screen, is only updating it when it goes outside of the screen (and the recyclerView refresh it).

I've tried with notifyRangeChanged and others but still the same result.

After I'm updating the reference to the list inside my adapter, I'm getting that new list of items and passing it into the StickyHeaderHandler (getAdapterData) in order to keep it updated..
So the steps that i'm doing are:

  • Instantiate the adapter with empty array list of data
  • The StickyHeaderHandler gets the same empty array list of data from the adapter
  • New data comes; adapter: clear the list ->addAll -> notify()
  • The StickyHeaderHandler gets the new array list of data from the adapter

Those steps works, but the pinned item is the only one not been refreshed properly.

First entry set:

Changing entry set the first item is not up to date 'Header 0 (7)':

Changin again not up to date:

After scrolling:

sticky header can't move down when first item is a sticky header

i found another bug :

private List<Item> compileItems() {
        List<Item> items = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            if (i % 4 == 0 /*&& i > 0*/) {
                items.add(new HeaderItem("Header at " + i, ""));
            } else {
                items.add(new Item("Item at " + i, "Item description at " + i));
            }
        }
        return items;
    }

Not working at all

I tried using your lib, but I encountered the following problem:

  • I added jCenter() to gradle file under buildscript/repositories
  • I added the compile statement compile 'com.brandongogetap:stickyheaders:0.4.4' to grade file
    => Suddenly my recycler view stops working and says: E/RecyclerView: No adapter attached; skipping layout

If I revert my gradle file, everything works again.

Don't know whats going wrong here ...

Header not positioned after configuration changed

The header is not being added correctly after a configuration change (or any change that causes a layout pass for the StickyLayoutManager).

This can be fixed by disabling the logic in the StickyHeaderPositioner#reset method, but getting rid of that logic creates other issues, like the header still being sticky after that specific item has been removed from the dataset. It also can lead to an NPE in the layoutChildren method of the RecyclerView parent that is hosting the header (Header is removed after parent ViewGroup has counted it as a child). The StickyHeaderPositioner#safeDetachHeader method fixes that issue, but something else is causing the header to not show.

Header Layout is not refreshed when adapter data changed

Hi there ! :)
First of all, you've done a great job with this library, one of simplest way to implement Sticky Headers.

I just noticed a small issue but a bit annoying for my use case.
When the adapter data change (like using adapter filters) if the position of the current header is the same than the new one (after data has been filtered), you do NOT rebind the header layout.

I just modified the code of StickyHeaderPositioner.java and made the following modification as a workaround:

 void reset(int orientation, int firstVisiblePosition) {
        this.orientation = orientation;
        // Don't reset/detach if same header position is to be attached
        if (getHeaderPositionToShow(firstVisiblePosition, null) == lastBoundPosition) {
            // I JUST ADDED THE FOLLOWING LINE TO FORCE THE REBIND OF THE NEW HEADER LAYOUT
            updateCurrentHeader = true;
            return;
        }
        if (fallbackReset) {
            lastBoundPosition = INVALID_POSITION;
        }
    }

I did not find any side effect yet and I would like to have your opinion about this.
Thanks for your great work anyway.

Dynamically change sticky header

In my app I dynamically change two TextView with numbers in sticky header by clicking overflow menu (in screenshot). Then I insert a number with length more then 1, and TextView don't display the whole number. Is there another way to fix it? without setting new adapter and StickyLayoutManager, because this way refreshing the whole RecyclerView, and I don't need it :)

TextView has layout_width="wrap_content" parameter
(Sorry for bad language)
sticky header

However, one of the simplest process to add sticky header :) great work!

requestLayout() improperly called by AppCompatTextView

Hello. I have this issue that infinitely print in log:

W/View: requestLayout() improperly called by android.support.v7.widget.AppCompatTextView{b2debd V.ED..C.. ........ 36,47-348,120 #7f090275 app:id/tvTitle} during second layout pass: posting in next frame
W/View: requestLayout() improperly called by android.widget.RelativeLayout{2d66426 V.E...C.. ......I. 0,0-1080,168 #7f090163 app:id/header_view} during second layout pass: posting in next frame

You know something about it?

Crash on StickyLayoutManager.onLayoutChildren

java.lang.IllegalArgumentException: view is not a child, cannot hide android.support.constraint.ConstraintLayout{bad2621 V.E...C.. .......D 0,2183-1440,2408}
	at android.support.v7.widget.ChildHelper.unhide(ChildHelper.java:352)
	at android.support.v7.widget.RecyclerView$Recycler.getScrapOrHiddenOrCachedHolderForPosition(RecyclerView.java:6098)
	at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5601)
	at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5563)
	at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5559)
	at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2229)
	at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1556)
	at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1516)
	at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:608)
	at com.brandongogetap.stickyheaders.StickyLayoutManager.onLayoutChildren(StickyLayoutManager.java:83)
	at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3644)
	at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:3103)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
	at android.view.View.measure(View.java:21046)
	at android.support.constraint.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:934)
	at android.support.constraint.ConstraintLayout.onMeasure(ConstraintLayout.java:973)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
	at android.view.View.measure(View.java:21046)
	at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
	at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:714)
	at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:91)
	at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1361)
	at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:784)
	at android.view.View.measure(View.java:21046)
	at android.support.constraint.ConstraintLayout.internalMeasureChildren(ConstraintLayout.java:934)
	at android.support.constraint.ConstraintLayout.onMeasure(ConstraintLayout.java:973)
	at android.view.View.measure(View.java:21046)
	at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1060)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
	at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
	at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
	at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
	at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
	at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6460)
	at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
	at com.android.internal.policy.DecorView.onMeasure(DecorView.java:785)
	at android.view.View.measure(View.java:21046)
	at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2562)
	at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1629)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1878)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1509)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7051)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:927)
	at android.view.Choreographer.doCallbacks(Choreographer.java:702)
	at android.view.Choreographer.doFrame(Choreographer.java:638)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:913)
	at android.os.Handler.handleCallback(Handler.java:751)
	at android.os.Handler.dispatchMessage(Handler.java:95)
	at android.os.Looper.loop(Looper.java:154)
	at android.app.ActivityThread.main(ActivityThread.java:6692)
	at java.lang.reflect.Method.invoke(Method.java)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)

Crash on FrameLayout layoutChildren

Recently I wrapped my RecyclerView with FrameLayout so I can use StickyHeaders. Since then from time to time I notice this strange crash. I extracted the log from Fabric. I can't reproduce it but I it could it's related to onNotifyDataSetChanged when data goes from N to 0 items and list was scrolled to the half height.

<android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/refreshView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/resultDateView">

            <FrameLayout
                android:layout_width="wrap_content"
                android:id="@+id/stickyHolder"
                android:layout_height="wrap_content">

                <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
                    android:id="@+id/live_rw_liveMatchList"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:scrollbars="vertical" />

            </FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
Fatal Exception: java.lang.NullPointerException
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:405)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
       at android.view.View.layout(View.java:15749)
       at android.view.ViewGroup.layout(ViewGroup.java:4880)
       at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:596)
       at android.view.View.layout(View.java:15749)
       at android.view.ViewGroup.layout(ViewGroup.java:4880)

Do you know what is going on here ?

New header instance is not created

Hello,

Thank you for a very convenient library. The sample app works just fine, but when I try to incorporate the library in my app, I see 'java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first'. I do not attach my view to the parent when inflating the xml in onCreateViewHolder(). The recycler view works perfectly fine without the library. Any ideas?
Also, in StickyHeaderPositioner.java, I see this.currentHeader = currentViewHolder.itemView;. I am curious to know how and when a new instance is created.

Nullpointer Exception in StickyLayoutManager

Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.brandongogetap.stickyheaders.c.a(java.util.List)' on a null object reference
at com.brandongogetap.stickyheaders.StickyLayoutManager.cacheHeaderPositions(SourceFile:97)
at com.brandongogetap.stickyheaders.StickyLayoutManager.onLayoutChildren(SourceFile:78)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(SourceFile:3583)
at android.support.v7.widget.RecyclerView.dispatchLayout(SourceFile:3312)
at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(SourceFile:1618)
at android.support.v7.widget.RecyclerView$ViewFlinger.run(SourceFile:4702)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5665)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:799)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:689)

okay seems like the positioner in StickyLayoutManager is null while attempting to call it
I saw positioner is instantiated in onAttachWindows, and seems like layoutChildren is called before onAttachToWindows which causes afterwards the crash

its our second most occurring bug in our production, please fix it
thanks

Positioner does not account for insets applied by parent layout

If a RecyclerView is nested inside a CoordinatorLayout, the sticky header may not be stuck at the right position depending on insets applied.

Reproduction steps:

<android.support.design.widget.CoordinatorLayout>
    <android.support.design.widget.AppBarLayout>
        <LinearLayout>
            /..stuff../
        </LinearLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView/>
</android.support.design.widget.CoordinatorLayout>

Header's decorators are not also sticky.

I've setup a recycler view to use the StickyHeaderAdapter. It's working perfectly. However when I then add an ItemDecoration to the recycler view, the item decorations on the header do not stick with the header.

MyAdapter adapter = new MyAdapter(); // implements StickyHeaderHandler
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new StickyLayoutManager(getContext(), adapter));
Drawable dividerDrawable = ContextCompat.getDrawable(getContext(), R.drawable.line_divider);
recyclerView.addItemDecoration(new DividerItemDecoration(dividerDrawable));

I'm using a library for the item decoration, instead of rolling my own, but that shouldn't make a difference.

example

StickyHeaders with coordinatorlayout and appbar has a strange behaviour

Nice work! This is so far the best StickyHeaders library I've tried.

Found a little bit of a bug though, or I implemented it wrongly. The first sticky header is shown on top of the toolbar and appbar in my coordinator layout. Then while scrolling the sticky header disappears, but the sticky headers the hide behind the toolbar as can be seen in the gif below. Any ideas?

app

This is the layout file I'm using:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/root_view"
    xmlns:tools="http://schemas.android.com/tools">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="?attr/actionBarSize"
                android:orientation="vertical"
                app:layout_collapseMode="parallax">

                <fragment
                    android:id="@+id/map"
                    android:name="com.google.android.gms.maps.SupportMapFragment"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    tools:context="com.mobile.races.RacesActivity"/>

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="56dp">

                    <ImageView
                        android:id="@+id/event_country"
                        android:layout_width="32dp"
                        android:layout_height="32dp"
                        android:layout_centerVertical="true"
                        android:layout_marginLeft="24dp"/>

                    <TextView
                        android:id="@+id/event_location"
                        android:layout_centerVertical="true"
                        android:textSize="18sp"
                        android:layout_marginLeft="96dp"
                        android:layout_marginRight="16dp"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"/>

                </RelativeLayout>

                <TextView
                    android:layout_marginLeft="96dp"
                    android:layout_marginRight="16dp"
                    android:layout_marginBottom="24dp"
                    android:textSize="13sp"
                    android:lineSpacingMultiplier="1.2"
                    android:id="@+id/event_description"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"/>

            </LinearLayout>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="@color/primary"
                android:theme="@style/AppTheme.ActionBarTheme"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_collapseMode="pin" >

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_vertical"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/header_event_name"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:includeFontPadding="false"
                        android:maxLines="1"
                        android:ellipsize="end"
                        android:textStyle="bold"
                        android:textColor="@color/tracTracBlue"
                        android:textSize="17sp" />

                    <TextView
                        android:id="@+id/header_dates_name"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:maxLines="1"
                        android:includeFontPadding="false"
                        android:textSize="12sp" />

                </LinearLayout>

            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:scrollbars="vertical"
        android:focusableInTouchMode="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android.support.design.widget.CoordinatorLayout>

Putting the recyclerview in its own framlayout makes the stickyheaders work and respect the given space, but the coordinatorlayout scrolling stops to work.

Optional "stickiness"

I don't know how much sense it has but it would be interesting feature if we would add ability for the developer to decide in runtime if something should be sticky or not. There may be a case that user has inner elements of same view type but that have different properties. For example, if we assume app for teachers that handles list of their schedule. Schedule can contain groups or singles (like private classes). Each group has list of students. Then they would have a list with following elements (where each group has : 1) Group A, 1.1) John, 1.2) Jack, 2) Emily, 3) Bob.

This example would require developer to have multiple classes for inner rows and top level student rows, although they are essentially same and differ in single flag. What about adding additional method, which can return default value if Java 8 is used? Or maybe investigating other approaches, I'm just now brainstorming :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.