I currently have this Recyclerview with a solid line ItemDecoration separating the elements.:
but want to have a RecyclerView ItemDecoration with a style like this one:
This is my decoration with just solid vertical and horizontal lines without line spaces like my example :
public class GridDividerDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = {android.R.attr.listDivider};
private Drawable mDivider;
private int mInsets;
public GridDividerDecoration(Context context) {
TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
mInsets = context.getResources().getDimensionPixelSize(R.dimen.card_insets);
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
drawVertical(c, parent);
drawHorizontal(c, parent);
}
public void drawVertical(Canvas c, RecyclerView parent) {
if (parent.getChildCount() == 0) return;
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.getLeft() - params.leftMargin - mInsets;
final int right = child.getRight() + params.rightMargin + mInsets;
final int top = child.getBottom() + params.bottomMargin + mInsets;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
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 + mInsets;
final int right = left + mDivider.getIntrinsicWidth();
final int top = child.getTop() - params.topMargin - mInsets;
final int bottom = child.getBottom() + params.bottomMargin + mInsets;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(mInsets, mInsets, mInsets, mInsets);
}
}
anyone can help how to set some spaces between lines ?
You just want to have slightly shorter lines so just amend your bounds.
e.g:
For the vertical line, you have:
mDivider.setBounds(left, top, right, bottom);
so just change the top and bottom offsets like this:
mDivider.setBounds(left, top + 10, right, bottom - 10);
Do a similar thing for the horizontal lines but change the left and right offsets.
You can experiment with the actual values until you get the effect you want.
Related
I couldn't achieve rotated version of horizontal divider in my recyclerview. When i use LinearLayoutManager.HORIZONTAL i need a vertical line between items. I tried to rotate or use different drawable.xml but no success.
You can find the code below
public class Divider extends RecyclerView.ItemDecoration {
private Drawable mDivider;
private int orientation;
public Divider(Context context,int orientation) {
this.orientation = orientation;
mDivider = ContextCompat.getDrawable(context,R.drawable.line_divider);
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if(orientation == LinearLayoutManager.VERTICAL) {
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);
}
}else{
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
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;
int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
c.rotate(90, mDivider.getIntrinsicWidth() / 2, mDivider.getIntrinsicHeight() / 2);
mDivider.draw(c);
}
}
}
}
My Drawable
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetTop="2dip"
android:insetBottom="2dip"
android:visible="true" >
<shape
android:shape="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="#cfcfcf" />
</shape>
What should i do?
I have just started using ItemDecoration. I am trying to achieve a divider in the middle of a two-column RecyclerView managed by StaggeredGridLayoutManager.
Here's my ItemDecoration code :
public class LobbyItemDecoration extends RecyclerView.ItemDecoration {
private int margin;
private Drawable drawable;
public LobbyItemDecoration(int margin, Drawable drawable){
this.margin = margin;
this.drawable = drawable;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view);
StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams)view .getLayoutParams();
int spanIndex = lp.getSpanIndex();
if(position >= 0){
if (position < ((LobiAdapter)parent.getAdapter()).getmDataset().size()){
if(spanIndex == 1){
outRect.left = margin;
((LobiAdapter)parent.getAdapter()).getmDataset().get(position).left = false;
} else {
outRect.right = margin;
((LobiAdapter)parent.getAdapter()).getmDataset().get(position).left = true;
}
outRect.bottom = margin;
}
}
}
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (drawable == null) { super.onDrawOver(c, parent, state);return; }
if(parent.getItemAnimator() != null && parent.getItemAnimator().isRunning()) {
return;
}
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i=1; i < childCount; i++) {
final View child = parent.getChildAt(i);
StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams)child .getLayoutParams();
int spanIndex = lp.getSpanIndex();
int size = drawable.getIntrinsicWidth();
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int ty = (int)(child.getTranslationY() + 0.5f);
final int tx = (int)(child.getTranslationX() + 0.5f);
final int leftIndex1 = child.getLeft() - (margin * 4 / 3) - tx;
final int rightIndex1 = child.getLeft() - (margin * 2 / 3) - tx;
final int leftIndex0 = child.getRight() + (margin * 2 / 3) + tx;
final int rightIndex0 = child.getRight() + (margin * 4 / 3) + tx;
if(spanIndex == 1){
// drawable.setBounds(100, top, 5, bottom);
drawable.setBounds(leftIndex1, top, rightIndex1, bottom);
drawable.draw(c);
} else {
drawable.setBounds(leftIndex0, top, rightIndex0, bottom);
drawable.draw(c);
// drawable.setBounds(5, top, 100, bottom);
}
}
}
}
Problem is as title said, whenever I load new item or scroll, the decoration is sometimes gone or misplaced. By gone I mean, literally gone, it's not there anymore until I scroll down or up to recycle the View.
And by misplaced I mean, when I scroll down, and the loading ViewHolder is in the left column, the divider "sticks" to the left column. If the loading ViewHolder is in the right column, it is normal.
Just in case : I use a ViewHolder to be a progress indicator by adding one null item and check it in getItemViewType() then remove it later after finishing the load from server.
This is how I add :
for (PostModel postModel :
dataSet) {
this.mDataset.add(postModel);
notifyItemInserted(mDataset.size() - 1);
}
StaggeredGridLayoutManager is buggy. For me this helped:
diffResult.dispatchUpdatesTo(this);
recyclerView.postDelayed(() -> {
layoutManager.invalidateSpanAssignments(); // to fix first item in second column issue
recyclerView.invalidateItemDecorations(); // to fix wrong spacing
}, 50);
Is there any way to removing divider after a footer in a Recyclerview.I am using item decoration to add divider to the adapter.I am adding footer in the adapter.divider is showing below the footer also.i want to remove it.
This is my code for Divider
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 rect, View view, RecyclerView parent, RecyclerView.State state) {
if (Orientation == VERTICAL_LIST) {
rect.set(0, 0, 0, Divider.getIntrinsicHeight());
} else {
rect.set(0, 0, Divider.getIntrinsicWidth(), 0);
}}
You have to add a check on getItemOffsets() method:
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
// set the rect's size
}
}
You can find an example of this implementation on my RecyclerViewDivider library on Github
Or you can simply add it as a Gradle dependency and check the javadoc:
dependencies {
...
compile 'com.github.fondesa:recycler-view-divider:1.1.3'
}
And use:
RecyclerViewDivider.with(context)
.addTo(recyclerView)
.visibilityFactory(new VisibilityFactory() {
#Override
public boolean displayDividerForItem(int listSize, int position) {
return position != listSize - 1;
}
})
.build()
.attach()
I use a RecyclerView with some dividers between items. I use a DividerDecorator to draw them. But on older android versions (API 8) it draws the complete background in my divider color. Here is my code:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private final Drawable mDivider;
private final int mHeight;
private int mOrientation;
public DividerItemDecoration( final Context context, final int orientation) {
mDivider = new ColorDrawable( context.getResources().getColor( R.color.divider) );
mHeight = dpToPx( 1f, context.getResources() );
setOrientation(orientation);
}
private int dpToPx( final float dp, final Resources resource ) {
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resource.getDisplayMetrics() );
return Math.max( 1, (int) px );
}
public void setOrientation( final int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
#Override
public void onDraw( final Canvas c, final RecyclerView parent, final RecyclerView.State state) {
if( mOrientation == VERTICAL_LIST ) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical( final Canvas c, final RecyclerView parent ) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
View child;
RecyclerView.LayoutParams params;
int top;
int bottom;
for( int position = 0; position < childCount; position++) {
child = parent.getChildAt(position);
params = (RecyclerView.LayoutParams) child.getLayoutParams();
top = child.getBottom() + params.bottomMargin;
bottom = top + mHeight;
mDivider.setBounds( left, top, right, bottom );
mDivider.draw(c);
}
}
public void drawHorizontal( final Canvas c, final RecyclerView parent ) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
View child;
RecyclerView.LayoutParams params;
int left;
int right;
for( int position = 0; position < childCount; position++) {
child = parent.getChildAt( position );
params = (RecyclerView.LayoutParams) child.getLayoutParams();
left = child.getRight() + params.rightMargin;
right = left + mHeight;
mDivider.setBounds( left, top, right, bottom );
mDivider.draw( c );
}
}
#Override
public void getItemOffsets( final Rect outRect, final View view, final RecyclerView parent, final RecyclerView.State state) {
if (mOrientation == VERTICAL_LIST) {
outRect.set( 0 /*left*/, 0 /*top*/, 0 /*right*/, mHeight /*bottom*/ );
} else {
outRect.set( 0 /*left*/, 0 /*top*/, mHeight /*right*/, 0 /*bottom*/ );
}
}
}
Here is my log from divider coordinates and they look correct:
DividerItemDecoration﹕ divider=0, left=0, top=66, right=240, bottom=67
DividerItemDecoration﹕ divider=1, left=0, top=133, right=240, bottom=134
DividerItemDecoration﹕ divider=2, left=0, top=200, right=240, bottom=201
DividerItemDecoration﹕ divider=3, left=0, top=267, right=240, bottom=268
I'm using this ItemDecoration class for GridLayout -> https://github.com/devunwired/recyclerview-playground/blob/master/app/src/main/java/com/example/android/recyclerplayground/GridDividerDecoration.java
But the problem is, my first row in GridLayout is an image and I set the span to 2.
You can view my screen as per screenshot below :
How to skip the first row so that ItemDecoration didn't draw on the Image?
Below is the code that I'm using to add the ItemDecoration :-
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new GridLayoutManager(getActivity(), 2);
mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return position == 0 ? 2 : 1;
}
});
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter.setHighlightsCallbacks(this);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mRecyclerView.addItemDecoration(new GridDividerDecoration(getActivity()));
I also modified the drawHorizontal method in GridDividerDecoration class to only draw if imageview is null but still not working :-
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);
if (child.findViewById(R.id.home_imgHeader) == null) {
final RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + params.rightMargin + mInsets;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
}
Any help?
Assuming your items don't individually have solid backgrounds (i.e. the white color is from the parent), the simplest fix would be to move the code from onDrawOver() to onDraw(). This will draw the gridlines underneath your child view contents and it will be hidden under the header image.
Otherwise, the code you are using assumes that the grid lines are always drawn to the top of the view in drawHorizontal() (notice top is a value that never changes). You would need to modify that function to account for the bottom of the first item and start drawing there. Something like:
public void drawHorizontal(Canvas c, RecyclerView parent) {
final View topView = parent.findViewHolderForAdapterPosition(0);
int viewBottom = -1;
if (topView != null) {
final RecyclerView.LayoutParams params =
(RecyclerView.LayoutParams) child.getLayoutParams();
viewBottom = child.getBottom() + params.bottomMargin + mInsets;
}
final int top = viewBottom > parent.getPaddingTop() ? viewBottom : parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
… /* Remaining Code */
}