Add divider between NavigationViews - android

I need to add a divider between two NavigationViews in my app. (navigation_drawer_top and navigation_drawer_bottom).
I have tried this. But this added divider to top of the view. not at end of the first NavigationView(navigation_drawer_top).
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"/>
Here is my code
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="#+id/navigation_drawer_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start">
<android.support.design.widget.NavigationView
android:id="#+id/navigation_drawer_top"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="top"
android:background="#color/menuColor"
android:paddingLeft="50dp"
app:headerLayout="#layout/nav_header_main"
app:itemTextAppearance="#style/NavigationDrawerStyle"
app:itemTextColor="#color/menuTextColour"
app:menu="#menu/menu_navigation_drawer_top"
/>
<android.support.design.widget.NavigationView
android:id="#+id/navigation_drawer_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/menuColor"
android:layout_gravity="bottom"
android:paddingLeft="50dp"
app:itemTextAppearance="#style/NavigationDrawerStyle"
app:itemTextColor="#color/menuTextColour"
app:menu="#menu/menu_navigation_drawer_bottom" />
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>

try this
<View
android:layout_below="#+id/id_of_item_below_which_you_want_it"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"/>

It's quite simple to do. All you need to do is make a group and put unique id to the groups. The unique id is what plays the real trick in adding a divider to your menu.
Here is a quick example for you,
<group android:id="#+id/group1" android:checkableBehavior="single" >
<item
android:id="#+id/item_1"
android:checked="true"
android:icon="#drawable/ic_1"
android:title="#string/title_1" />
</group>
<group android:id="#+id/group2" android:checkableBehavior="single" >
<item
android:id="#+id/item_2"
android:icon="#drawable/ic_2"
android:title="#string/title_2" />
</group>
This will surely add dividers to your menus.
EDIT
As you are adding menus programmatically, you should try to get access to each NavigationMenuView and add a decorator to them.
NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
NavigationMenuView navMenuView = (NavigationMenuView) navigationView.getChildAt(0);
navMenuView.addItemDecoration(new DividerItemDecoration(appContext,DividerItemDecoration.VERTICAL_LIST))
And here is the DividerItemDecoration class for you,
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
#Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
#Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}

Related

View not appearing at bottom of CardView?

Hi I have a simple CardView where I want to show a divider between each Cardview. The issue is, my View that I use as a divider never gets shown on the last CardView in my RecyclerView. Here is my attempt:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp"
card_view:cardElevation="0dp"
android:id="#+id/cv_news_feed">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
...Content...
</RelativeLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/material_color_grey_300" />
</android.support.v7.widget.CardView>
The divider shows up for every CardView except for the last for whatever reason and I'm not sure why this is occurring. Any help would be appreciated, thanks!
EDIT: Image posted:
Remove following view from your item
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/material_color_grey_300" />
and try to use ItemDecoration as follows
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
Utility.ItemDecorationConst);
recyclerView.addItemDecoration(5);
DividerItemDecoration.java
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public DividerItemDecoration(int space) {
this.space = space;
}
#Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.left = space;
outRect.right = space;
outRect.top = space;
outRect.bottom = space;
}
}
CardView is an extension of FrameLayout which means that your divider is not in the bottom of your CardView but in the top.
Place your divider in your LinearLayout instead.
Add layout_gravity attribute to the View:
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="#color/material_color_grey_300" />
You can try to this code , hope this can help you
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/tools"
android:id="#+id/cv_news_feed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp"
card_view:cardElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
...Content...
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="#+id/cv_news_feed"
android:layout_marginTop="5dp"
android:background="#android:color/holo_red_dark" />
</RelativeLayout>
Create a class DividerItemDecoration:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by Lincoln on 30/10/15.
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
Then set the item decoration using addItemDecoration() method before setting the adapter.
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
// set the adapter
recyclerView.setAdapter(mAdapter);
N.B. Remove the view that you are using in cardview layout to show the divider.
Try to put your divider into LinearLayout,i.e.:
You should use like this
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
...Content...
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/material_color_grey_300" />
</LinearLayout>
Because CardView extends FrameLayout so your devider will be behind LinearLayout content.

RecyclerView ItemDecoration: Spacing AND Divider

Below is how I'm doing the spacing for RecyclerView items. It's designed to work with both grids and lists. The spacing works.
What I can't figure out is how to insert a divider line as well. Any help doing so would be greatly appreciated.
SIDE NOTE: if you have a better way to implement the spacing than what I'm currently doing, I'd be very grateful as well :)
public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
private int numOfColumns;
private int listSize;
private int offsetInDp;
private boolean isGridView;
private boolean canScrollHorizontally;
private boolean isBottomRow = false;
public ItemOffsetDecoration(RecyclerView.LayoutManager manager, int listSize, int offsetInDp) {
this(manager, 1, listSize, offsetInDp);
}
public ItemOffsetDecoration(RecyclerView.LayoutManager manager, int numOfColumns, int listSize, int offsetInDp) {
this.numOfColumns = numOfColumns;
this.listSize = listSize;
this.offsetInDp = PixelConversionUtils.dpToPx(offsetInDp);
this.isGridView = manager instanceof GridLayoutManager;
this.canScrollHorizontally = manager.canScrollHorizontally();
}
#Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
// only do left/right spacing if grid or horizontal list
if (isGridView || canScrollHorizontally) {
outRect.left = offsetInDp;
outRect.right = offsetInDp;
}
// only do top/bottom spacing if grid or vertical list
if (isGridView || !canScrollHorizontally) {
int pos = parent.getChildAdapterPosition(view);
boolean isNotTopRow = pos >= numOfColumns;
// Don't add top spacing to top row
if (isNotTopRow) {
outRect.top = offsetInDp;
}
int columnIndex = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
if (pos >= (listSize - numOfColumns) && columnIndex == 0) {
isBottomRow = true;
}
// Don't add bottom spacing to bottom row
if (!isBottomRow && pos < (listSize - numOfColumns)) {
outRect.bottom = offsetInDp;
}
}
}
}
here's a quick visual of what I'm looking to do:
here's what I have:
here's what I want:
You can achieve desired look this way:
first, create a divider Drawable, for this example I've used a simple shape, but you could use default line divider or any other drawable:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="2dp" />
<size android:width="2dp" />
<solid android:color="#000000" />
</shape>
second, in your ItemOffsetDecoration declare Drawable and initialize it:
public class ItemOffsetDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
...
public ItemOffsetDecoration(...) {
mDivider = ContextCompat.getDrawable(context, R.drawable.item_divider);
}
}
third, override onDrawOver() method:
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (isGridView) {
drawVerticalDivider(c, parent);
} else {
drawVerticalDivider(c, parent);
drawHorizontalDivider(c, parent);
}
}
where drawVerticalDivider() & drawHorizontalDivider() are (might be a good idea to refactor them into the single method and control direction of the divider via parameter):
public void drawVerticalDivider(Canvas c, RecyclerView parent) {
if (parent.getChildCount() == 0) return;
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getLeft() - params.leftMargin - offsetInDp;
int right = child.getRight() + params.rightMargin + offsetInDp;
int top = child.getBottom() + params.bottomMargin + offsetInDp;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontalDivider(Canvas c, RecyclerView parent) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin + offsetInDp;
int right = left + mDivider.getIntrinsicWidth();
int top = child.getTop() - params.topMargin - offsetInDp;
int bottom = child.getBottom() + params.bottomMargin + offsetInDp;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
Result for the Linear and Grid LayoutManagers:
Try placing the following XML snippet to get a divider:
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="72dp"
android:layout_marginRight="16dp"
android:background="123e4152"/>
You can put this in the recyclerView's item layout beneath your items. Also Play around with the margins and background to suit your list.
haha……actualy,i had try like this for Divider ,although with a bit funny : first make you recycleview backgroud with Deep color,and make item_view backgroud white,then marginbottom for every item -> I'm serious, do not vote down :)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_marginBottom="2dp"
android:layout_height="wrap_content">
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>

RecyclerView.ItemDecorator left padding

I am using the https://gist.github.com/polbins/e37206fbc444207c0e92 ItemDecorator to draw a separator line under each item in a recycler view. However it draws a straight line from left to right.
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
How can I set a left non absolute padding to the separator like in the picture below? Is this even possible using ItemDecorator?
I could modify this
int left = parent.getPaddingLeft();
but it would be absolute. Any ideas?
How it looks like right now:
Item layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:clickable="true"
android:background="#drawable/recycler_item_background">
<TextView
android:id="#+id/mTextViewIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:padding="10dp"
android:text="#string/fa_search"
android:textSize="18sp"/>
<LinearLayout
android:id="#+id/wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/mTextViewIcon"
android:layout_toEndOf="#id/mTextViewIcon"
android:orientation="vertical"
android:paddingLeft="25dp"
android:paddingStart="25dp">
<TextView
android:id="#+id/mTextViewTitle"
style="#style/Subhead"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/mTextViewSubtitle"
style="#style/SmallGray"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
View holder:
public static class ItemViewHolder extends BaseViewHolder {
#Bind(R.id.mTextViewIcon)
TextView mTextViewIcon;
#Bind(R.id.mTextViewTitle)
TextView mTextViewTitle;
#Bind(R.id.mTextViewSubtitle)
TextView mTextViewSubtitle;
public ParkzoneViewHolder(View mView) {
super(mView);
ButterKnife.bind(this, mView);
}
}
I am using TextView for the icon with a custom typeface, just for you're information.
Give this a try
public static class ItemViewHolder extends BaseViewHolder {
#Bind(R.id.mTextViewIcon)
TextView mTextViewIcon;
#Bind(R.id.mTextViewTitle)
TextView mTextViewTitle;
#Bind(R.id.mTextViewSubtitle)
TextView mTextViewSubtitle;
#Bind(R.id.wrapper)
LinearLayout mWrapperLayout;
public ParkzoneViewHolder(View mView) {
super(mView);
ButterKnife.bind(this, mView);
}
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
// You can get view holders from recyclerview in different ways,
// parent.getChildViewHolder(child)
// im thinking parent.getChildViewHolder(child)
// is reliable and so ill use that here.
// but you be sure to test it with the other methods too
// if the getChildViewHolder doesnt work
View child = parent.getChildAt(i);
ItemViewHolder holder = (ItemViewHolder) parent.getChildViewHolder(child);
int left = parent.getPaddingLeft() + child.getPaddingLeft()
+ holder.mTextViewIcon.getPaddingLeft() + holder.mTextViewIcon.getWidth()
+ holder.mWrapperLayout.getPaddingLeft();
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}

Dividers didn't display in RecyclerView after addItemDecoration() called

A RecyclerView is nested in a CardView. I tried to call addItemDecoration() to add dividers into RecycylerView,but it didn't work.
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="#color/white_color"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
card_view:cardElevation="3dp">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_saleproduct_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_saleproduct_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="visible" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.v7.widget.CardView>
The java code:
mSwipe = (SwipeRefreshLayout)view.findViewById(R.id.swipe_saleproduct_list);
mRecyclerView= (RecyclerView) view.findViewById(R.id.recycler_saleproduct_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(UIUtils.getContext()));
mRecyclerView.addItemDecoration(new DividerItemDecoration(UIUtils.getContext(), DividerItemDecoration.VERTICAL_LIST));
SaleProductListAdapter saleProductListAdapter = new SaleProductListAdapter(UIUtils.getContext(), mRecyclerView, list, mSwipe);
mRecyclerView.setAdapter(saleProductListAdapter);
This below is a good example, take a look at it:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
#Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
#Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
And here is my Fragment, it works well, which divider is clear and thin:
public static class RecyclerViewFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_recyclerview, container, false);
RecyclerView recyclerView = (RecyclerView) root.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST));
RecyclerViewAdapter adapter = new RecyclerViewAdapter(getActivity(), getResources()
.getStringArray(R.array.countries));
recyclerView.setAdapter(adapter);
return root;
}
}
I hope you will be inspired.
The mDivider with System maybe NullPointerException on platform KitKat and below,so I suggest use mDivider with oneself like:
public MyDecoration(Context context, int orientation) {
mDivider = context.getResources().getDrawable(R.drawable.line_divider);
setOrientation(orientation);
}
And Here is the line_divider:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="#color/color_in_common_green" />
</shape>
It maybe work well

How to make sticky section headers (like iOS) in Android?

My specific question is: How I can achieve an effect like this: http://youtu.be/EJm7subFbQI
The bounce effect is not important, but i need the "sticky" effect for the headers. Where do I start?, In what can I base me? I need something that I can implement on API 8 to up.
Thanks.
There are a few solutions that already exist for this problem. What you're describing are section headers and have come to be referred to as sticky section headers in Android.
Sticky List Headers
Sticky Scroll Views
HeaderListView
EDIT: Had some free time to add the code of fully working example. Edited the answer accordingly.
For those who don't want to use 3rd party code (or cannot use it directly, e.g. in Xamarin), this could be done fairly easily by hand.
The idea is to use another ListView for the header. This list view contains only the header items. It will not be scrollable by the user (setEnabled(false)), but will be scrolled from code based on main lists' scrolling. So you will have two lists - headerListview and mainListview, and two corresponding adapters headerAdapter and mainAdapter. headerAdapter only returns section views, while mainAdapter supports two view types (section and item). You will need a method that takes a position in the main list and returns a corresponding position in the sections list.
Main activity
public class MainActivity extends AppCompatActivity {
public static final int TYPE_SECTION = 0;
public static final int TYPE_ITEM = 1;
ListView mainListView;
ListView headerListView;
MainAdapter mainAdapter;
HeaderAdapter headerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainListView = (ListView)findViewById(R.id.list);
headerListView = (ListView)findViewById(R.id.header);
mainAdapter = new MainAdapter();
headerAdapter = new HeaderAdapter();
headerListView.setEnabled(false);
headerListView.setAdapter(headerAdapter);
mainListView.setAdapter(mainAdapter);
mainListView.setOnScrollListener(new AbsListView.OnScrollListener(){
#Override
public void onScrollStateChanged(AbsListView view, int scrollState){
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// this should return an index in the headers list, based one the index in the main list. The logic for this is highly dependent on your data.
int pos = mainAdapter.getSectionIndexForPosition(firstVisibleItem);
// this makes sure our headerListview shows the proper section (the one on the top of the mainListview)
headerListView.setSelection(pos);
// this makes sure that headerListview is scrolled exactly the same amount as the mainListview
if(mainAdapter.getItemViewType(firstVisibleItem + 1) == TYPE_SECTION){
headerListView.setSelectionFromTop(pos, mainListView.getChildAt(0).getTop());
}
}
});
}
public class MainAdapter extends BaseAdapter{
int count = 30;
#Override
public int getItemViewType(int position){
if((float)position / 10 == (int)((float)position/10)){
return TYPE_SECTION;
}else{
return TYPE_ITEM;
}
}
#Override
public int getViewTypeCount(){ return 2; }
#Override
public int getCount() { return count - 1; }
#Override
public Object getItem(int position) { return null; }
#Override
public long getItemId(int position) { return position; }
public int getSectionIndexForPosition(int position){ return position / 10; }
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = getLayoutInflater().inflate(R.layout.item, parent, false);
position++;
if(getItemViewType(position) == TYPE_SECTION){
((TextView)v.findViewById(R.id.text)).setText("SECTION "+position);
}else{
((TextView)v.findViewById(R.id.text)).setText("Item "+position);
}
return v;
}
}
public class HeaderAdapter extends BaseAdapter{
int count = 5;
#Override
public int getCount() { return count; }
#Override
public Object getItem(int position) { return null; }
#Override
public long getItemId(int position) { return position; }
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = getLayoutInflater().inflate(R.layout.item, parent, false);
((TextView)v.findViewById(R.id.text)).setText("SECTION "+position*10);
return v;
}
}
}
A couple of things to note here. We do not want to show the very first section in the main view list, because it would produce a duplicate (it's already shown in the header). To avoid that, in your mainAdapter.getCount():
return actualCount - 1;
and make sure the first line in your getView() method is
position++;
This way your main list will be rendering all cells but the first one.
Another thing is that you want to make sure your headerListview's height matches the height of the list item. In this example the height is fixed, but it could be tricky if your items height is not set to an exact value in dp. Please refer to this answer for how to address this: https://stackoverflow.com/a/41577017/291688
Main layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
<ListView
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="48dp"/>
<ListView
android:id="#+id/list"
android:layout_below="#+id/header"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
Item / header layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="48dp">
<TextView
android:id="#+id/text"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
Add this in your app.gradle file
compile 'se.emilsjolander:StickyScrollViewItems:1.1.0'
then my layout, where I have added android:tag ="sticky" to specific views like textview or edittext not LinearLayout, looks like this. It also uses databinding, ignore that.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="temp"
type="com.lendingkart.prakhar.lendingkartdemo.databindingmodel.BusinessDetailFragmentModel" />
<variable
name="presenter"
type="com.lendingkart.prakhar.lendingkartdemo.presenters.BusinessDetailsPresenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.lendingkart.prakhar.lendingkartdemo.customview.StickyScrollView
android:id="#+id/sticky_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- scroll view child goes here -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="5dp"
card_view:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
style="#style/group_view_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/businessdetailtitletextviewbackground"
android:padding="#dimen/activity_horizontal_margin"
android:tag="sticky"
android:text="#string/business_contact_detail" />
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="7dp">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/comapnyLabel"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/contactLabel"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/emailLabel"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/NumberOfEmployee"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="5dp"
card_view:cardUseCompatPadding="true">
<TextView
style="#style/group_view_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/businessdetailtitletextviewbackground"
android:padding="#dimen/activity_horizontal_margin"
android:tag="sticky"
android:text="#string/nature_of_business" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="5dp"
card_view:cardUseCompatPadding="true">
<TextView
style="#style/group_view_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/businessdetailtitletextviewbackground"
android:padding="#dimen/activity_horizontal_margin"
android:tag="sticky"
android:text="#string/taxation" />
</android.support.v7.widget.CardView>
</LinearLayout>
</com.lendingkart.prakhar.lendingkartdemo.customview.StickyScrollView>
</LinearLayout>
</layout>
style group for the textview looks this
<style name="group_view_text" parent="#android:style/TextAppearance.Medium">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textColor">#color/edit_text_color</item>
<item name="android:textSize">16dp</item>
<item name="android:layout_centerVertical">true</item>
<item name="android:textStyle">bold</item>
</style>
and the background for the textview goes like this:(#drawable/businessdetailtitletextviewbackground)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/edit_text_color" />
</shape>
</item>
<item android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#color/White" />
</shape>
</item>
</layer-list>
For those looking for a solution in 2020, I have quickly created a solution extending the Layout Manager from ruslansharipov project (Sticky Header) and combining it whith the RecycleView Adapter from lisawray Groupie project (Expandable RecycleView).
You can see my example here
Result Here
You can reach this effect using SuperSLiM library. It provides you a LayoutManager for RecyclerView with interchangeable linear, grid, and staggered displays of views.
A good demo is located in github repository
It is simply to get such result
app:slm_headerDisplay="inline|sticky"
or
app:slm_headerDisplay="sticky"
I have used one special class to achieve listview like iPhone.
You can find example with source code here. https://demonuts.com/android-recyclerview-sticky-header-like-iphone/
This class which has updated listview is as
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.RelativeLayout;
public class HeaderListView extends RelativeLayout {
// TODO: Handle listViews with fast scroll
// TODO: See if there are methods to dispatch to mListView
private static final int FADE_DELAY = 1000;
private static final int FADE_DURATION = 2000;
private InternalListView mListView;
private SectionAdapter mAdapter;
private RelativeLayout mHeader;
private View mHeaderConvertView;
private FrameLayout mScrollView;
private AbsListView.OnScrollListener mExternalOnScrollListener;
public HeaderListView(Context context) {
super(context);
init(context, null);
}
public HeaderListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mListView = new InternalListView(getContext(), attrs);
LayoutParams listParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
listParams.addRule(ALIGN_PARENT_TOP);
mListView.setLayoutParams(listParams);
mListView.setOnScrollListener(new HeaderListViewOnScrollListener());
mListView.setVerticalScrollBarEnabled(false);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mAdapter != null) {
mAdapter.onItemClick(parent, view, position, id);
}
}
});
addView(mListView);
mHeader = new RelativeLayout(getContext());
LayoutParams headerParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
headerParams.addRule(ALIGN_PARENT_TOP);
mHeader.setLayoutParams(headerParams);
mHeader.setGravity(Gravity.BOTTOM);
addView(mHeader);
// The list view's scroll bar can be hidden by the header, so we display our own scroll bar instead
Drawable scrollBarDrawable = getResources().getDrawable(R.drawable.scrollbar_handle_holo_light);
mScrollView = new FrameLayout(getContext());
LayoutParams scrollParams = new LayoutParams(scrollBarDrawable.getIntrinsicWidth(), LayoutParams.MATCH_PARENT);
scrollParams.addRule(ALIGN_PARENT_RIGHT);
scrollParams.rightMargin = (int) dpToPx(2);
mScrollView.setLayoutParams(scrollParams);
ImageView scrollIndicator = new ImageView(context);
scrollIndicator.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
scrollIndicator.setImageDrawable(scrollBarDrawable);
scrollIndicator.setScaleType(ScaleType.FIT_XY);
mScrollView.addView(scrollIndicator);
mScrollView.setVisibility(INVISIBLE);
addView(mScrollView);
}
public void setAdapter(SectionAdapter adapter) {
mAdapter = adapter;
mListView.setAdapter(adapter);
}
public void setOnScrollListener(AbsListView.OnScrollListener l) {
mExternalOnScrollListener = l;
}
private class HeaderListViewOnScrollListener implements AbsListView.OnScrollListener {
private int previousFirstVisibleItem = -1;
private int direction = 0;
private int actualSection = 0;
private boolean scrollingStart = false;
private boolean doneMeasuring = false;
private int lastResetSection = -1;
private int nextH;
private int prevH;
private View previous;
private View next;
private AlphaAnimation fadeOut = new AlphaAnimation(1f, 0f);
private boolean noHeaderUpToHeader = false;
private boolean didScroll = false;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mExternalOnScrollListener != null) {
mExternalOnScrollListener.onScrollStateChanged(view, scrollState);
}
didScroll = true;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mExternalOnScrollListener != null) {
mExternalOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if (!didScroll) {
return;
}
firstVisibleItem -= mListView.getHeaderViewsCount();
if (firstVisibleItem < 0) {
mHeader.removeAllViews();
return;
}
updateScrollBar();
if (visibleItemCount > 0 && firstVisibleItem == 0 && mHeader.getChildAt(0) == null) {
addSectionHeader(0);
lastResetSection = 0;
}
int realFirstVisibleItem = getRealFirstVisibleItem(firstVisibleItem, visibleItemCount);
if (totalItemCount > 0 && previousFirstVisibleItem != realFirstVisibleItem) {
direction = realFirstVisibleItem - previousFirstVisibleItem;
actualSection = mAdapter.getSection(realFirstVisibleItem);
boolean currIsHeader = mAdapter.isSectionHeader(realFirstVisibleItem);
boolean prevHasHeader = mAdapter.hasSectionHeaderView(actualSection - 1);
boolean nextHasHeader = mAdapter.hasSectionHeaderView(actualSection + 1);
boolean currHasHeader = mAdapter.hasSectionHeaderView(actualSection);
boolean currIsLast = mAdapter.getRowInSection(realFirstVisibleItem) == mAdapter.numberOfRows(actualSection) - 1;
boolean prevHasRows = mAdapter.numberOfRows(actualSection - 1) > 0;
boolean currIsFirst = mAdapter.getRowInSection(realFirstVisibleItem) == 0;
boolean needScrolling = currIsFirst && !currHasHeader && prevHasHeader && realFirstVisibleItem != firstVisibleItem;
boolean needNoHeaderUpToHeader = currIsLast && currHasHeader && !nextHasHeader && realFirstVisibleItem == firstVisibleItem && Math.abs(mListView.getChildAt(0).getTop()) >= mListView.getChildAt(0).getHeight() / 2;
noHeaderUpToHeader = false;
if (currIsHeader && !prevHasHeader && firstVisibleItem >= 0) {
resetHeader(direction < 0 ? actualSection - 1 : actualSection);
} else if ((currIsHeader && firstVisibleItem > 0) || needScrolling) {
if (!prevHasRows) {
resetHeader(actualSection-1);
}
startScrolling();
} else if (needNoHeaderUpToHeader) {
noHeaderUpToHeader = true;
} else if (lastResetSection != actualSection) {
resetHeader(actualSection);
}
previousFirstVisibleItem = realFirstVisibleItem;
}
if (scrollingStart) {
int scrolled = realFirstVisibleItem >= firstVisibleItem ? mListView.getChildAt(realFirstVisibleItem - firstVisibleItem).getTop() : 0;
if (!doneMeasuring) {
setMeasurements(realFirstVisibleItem, firstVisibleItem);
}
int headerH = doneMeasuring ? (prevH - nextH) * direction * Math.abs(scrolled) / (direction < 0 ? nextH : prevH) + (direction > 0 ? nextH : prevH) : 0;
mHeader.scrollTo(0, -Math.min(0, scrolled - headerH));
if (doneMeasuring && headerH != mHeader.getLayoutParams().height) {
LayoutParams p = (LayoutParams) (direction < 0 ? next.getLayoutParams() : previous.getLayoutParams());
p.topMargin = headerH - p.height;
mHeader.getLayoutParams().height = headerH;
mHeader.requestLayout();
}
}
if (noHeaderUpToHeader) {
if (lastResetSection != actualSection) {
addSectionHeader(actualSection);
lastResetSection = actualSection + 1;
}
mHeader.scrollTo(0, mHeader.getLayoutParams().height - (mListView.getChildAt(0).getHeight() + mListView.getChildAt(0).getTop()));
}
}
private void startScrolling() {
scrollingStart = true;
doneMeasuring = false;
lastResetSection = -1;
}
private void resetHeader(int section) {
scrollingStart = false;
addSectionHeader(section);
mHeader.requestLayout();
lastResetSection = section;
}
private void setMeasurements(int realFirstVisibleItem, int firstVisibleItem) {
if (direction > 0) {
nextH = realFirstVisibleItem >= firstVisibleItem ? mListView.getChildAt(realFirstVisibleItem - firstVisibleItem).getMeasuredHeight() : 0;
}
previous = mHeader.getChildAt(0);
prevH = previous != null ? previous.getMeasuredHeight() : mHeader.getHeight();
if (direction < 0) {
if (lastResetSection != actualSection - 1) {
addSectionHeader(Math.max(0, actualSection - 1));
next = mHeader.getChildAt(0);
}
nextH = mHeader.getChildCount() > 0 ? mHeader.getChildAt(0).getMeasuredHeight() : 0;
mHeader.scrollTo(0, prevH);
}
doneMeasuring = previous != null && prevH > 0 && nextH > 0;
}
private void updateScrollBar() {
if (mHeader != null && mListView != null && mScrollView != null) {
int offset = mListView.computeVerticalScrollOffset();
int range = mListView.computeVerticalScrollRange();
int extent = mListView.computeVerticalScrollExtent();
mScrollView.setVisibility(extent >= range ? View.INVISIBLE : View.VISIBLE);
if (extent >= range) {
return;
}
int top = range == 0 ? mListView.getHeight() : mListView.getHeight() * offset / range;
int bottom = range == 0 ? 0 : mListView.getHeight() - mListView.getHeight() * (offset + extent) / range;
mScrollView.setPadding(0, top, 0, bottom);
fadeOut.reset();
fadeOut.setFillBefore(true);
fadeOut.setFillAfter(true);
fadeOut.setStartOffset(FADE_DELAY);
fadeOut.setDuration(FADE_DURATION);
mScrollView.clearAnimation();
mScrollView.startAnimation(fadeOut);
}
}
private void addSectionHeader(int actualSection) {
View previousHeader = mHeader.getChildAt(0);
if (previousHeader != null) {
mHeader.removeViewAt(0);
}
if (mAdapter.hasSectionHeaderView(actualSection)) {
mHeaderConvertView = mAdapter.getSectionHeaderView(actualSection, mHeaderConvertView, mHeader);
mHeaderConvertView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
mHeaderConvertView.measure(MeasureSpec.makeMeasureSpec(mHeader.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
mHeader.getLayoutParams().height = mHeaderConvertView.getMeasuredHeight();
mHeaderConvertView.scrollTo(0, 0);
mHeader.scrollTo(0, 0);
mHeader.addView(mHeaderConvertView, 0);
} else {
mHeader.getLayoutParams().height = 0;
mHeader.scrollTo(0, 0);
}
mScrollView.bringToFront();
}
private int getRealFirstVisibleItem(int firstVisibleItem, int visibleItemCount) {
if (visibleItemCount == 0) {
return -1;
}
int relativeIndex = 0, totalHeight = mListView.getChildAt(0).getTop();
for (relativeIndex = 0; relativeIndex < visibleItemCount && totalHeight < mHeader.getHeight(); relativeIndex++) {
totalHeight += mListView.getChildAt(relativeIndex).getHeight();
}
int realFVI = Math.max(firstVisibleItem, firstVisibleItem + relativeIndex - 1);
return realFVI;
}
}
public ListView getListView() {
return mListView;
}
public void addHeaderView(View v) {
mListView.addHeaderView(v);
}
private float dpToPx(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
}
protected class InternalListView extends ListView {
public InternalListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected int computeVerticalScrollExtent() {
return super.computeVerticalScrollExtent();
}
#Override
protected int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
#Override
protected int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
}
}
XML usage
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.parsaniahardik.listview_stickyheader_ios.HeaderListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/lv">
</com.example.parsaniahardik.listview_stickyheader_ios.HeaderListView>

Categories

Resources