How to avoid decorating the first row of recyclerview - android

How can I go about not decorating the first row in my recyclerview so that my Item decorator decorates every other row?

Just check the child position in
#Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
}
}
in your RecyclerView.ItemDecoration class.
Also a nice solution (to learn something more) can be found at https://github.com/yqritc/RecyclerView-FlexibleDivider

In your onBindView() method, test the position var for 0.

Agree with korrekorre's answer.
My code looks like:
#Override public void getItemOffsets(
Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int l, t, r, b;
l = leftMargin;
t = topMargin;
r = rightMargin;
b = bottomMargin;
if (mIgnoreAfterLast && parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() - 1) {
if (mHorizontal) {
r = 0;
} else {
b = 0;
}
}
if (mIgnoreBeforeFirst && parent.getChildAdapterPosition(view) == 0) {
if (mHorizontal) {
l = 0;
} else {
t = 0;
}
}
outRect.set(l, t, r, b);
}

Just check for first position and 'return' in your custom RecyclerView.ItemDecoration. To be more exact, something like this:
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view);
if (position == RecyclerView.NO_POSITION || position == 0) {
return;
}
...The rest of your decorator logic goes here...
}

Related

RecyclerView - Getting adapter position in ItemDecoration's onDraw method?

In itemDecoration's on draw method it has a override method called getItemOffsets which I can get the adapter's position using:
#Override
public void getItemOffsets(#NonNull Rect outRect, #NonNull View view, #NonNull RecyclerView parent, #NonNull RecyclerView.State state) {
int childCount = parent.getChildCount();
int position = parent.getChildAdapterPosition(view);
}
How ever if I try this method inside onDraw:
#Override
public void onDraw(#NonNull Canvas c, #NonNull RecyclerView parent, #NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition( ??? );
}
What do I pass for the view parameters?
In onDraw() You are working on one canvas. To modify one child, You have to iterate by all the parent's children.
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; ++i) {
View child = parent.getChildAt(i);
int childAdapterPosition = parent.getChildAdapterPosition(child);
// ...
}

Decorate only the absolute first item of RecyclerView

I need to add a separator only to the absolute first item of my recycle view.
I have already read How to selectively decorate RecyclerView items , and i understand that
The (onDraw) method loops over all the child views currently in the RecyclerView visible on the screen.
and my problem is exactly that. since it executes every time the views in the RecycleView change, even if i am able to locate and decorate only the first item, as soon as i scroll down, the decoration shifts to the current first item.
In that link the selection is done by the method isDecorated which looks at the instance of the current child's ViewHolder. My guess is that the guy wanted to decorate with respect to the ViewHolder type, which is not my problem since i have only one type of element in my RecyclerView
This is my DividerItemDecoration.java
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDivider;
public DividerItemDecoration(Drawable divider) {
mDivider = divider;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
if (parent.getChildAdapterPosition(view) == 1) {
outRect.top = outRect.top + mDivider.getIntrinsicHeight();
}
return;
}
#Override
public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
int dividerLeft = parent.getPaddingLeft();
int dividerRight = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount - 1; i++) {
if(i==0 ){
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int dividerTop = child.getBottom() + params.bottomMargin;
int dividerBottom = dividerTop + mDivider.getIntrinsicHeight();
mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom);
mDivider.draw(canvas);
}
}
}
}
Please consider that i have searched all day and could only find examples and gists to add decorator to every item, to selectively add it based on the type, but nothing really tackling with the absolute position.
Also, please no libraries like https://github.com/yqritc/RecyclerView-FlexibleDivider, i want to learn , not to copy.
You can check if it's the first element,
Here's an example:
Just implement getItemViewType(), and take care of the viewType parameter in onCreateViewHolder().
So you do something like:
#Override
public int getItemViewType(int position) {
if (position == 0)
return 1;
else
return 2;
}
then in onCreateViewHolder inflate your different layout according to your viewType.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 1) {
// inflate your first item layout & return that viewHolder
} else {
// inflate your other item layout & return that viewHolder
}
}

Modify selected item in Numberpicker Android

I'm trying to figure some way to achieve the next kind of view. At the moment I have tried to create a Listview and just make bigger the selected item. But I cannot make the selected item always be in the middle of my view. So now I'm trying to get this with a numberpicker.
But I didn't find any way to hide the divider bar, and make different the selected item and the rest of the view. The idea is get something like in the bottom image.
I think that the ListView may be more configurable than the NumberPicker.
What you can do is use different row layouts dependind if it is the middle one or the others, so your getView(...) method would look like this:
public View getView(int position, View convertView, ViewGroup parent) {
if (position == 1) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.focused_layout, parent, false);
// Do whatever with this view
} else {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.not_focused_layout, parent, false);
// Do whatever with this view
}
return convertView;
}
This way you can customize both layouts both in XML and code. Yo can change the condition if you want the "special" item any other way.
Following is a number picker with custom display values:
final NumberPicker aNumberPicker = new NumberPicker(context);
List<Integer> ids = getIds();
aNumberPicker.setMaxValue(ids.size()-1);
aNumberPicker.setMinValue(0);
mDisplayedIds = new String[ids.size()];
for (int i = 0; i < ids.size(); i++) {
mDisplayedIds[i] = "Nombre"+String.valueOf(ids.get(i)) ;
}
aNumberPicker.setDisplayedValues(mDisplayedIds);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(50, 50);
RelativeLayout.LayoutParams numPickerParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
numPickerParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
relativeLayout.setLayoutParams(params);
relativeLayout.addView(aNumberPicker, numPickerParams);
Also, you can check out some open source library like this one AndroidPicker
You can implement this using RecyclerView with one Holder for Normal Item and one Holder for Selected Item.
Inside your RecyclerView Adapter
private static int SELECTED_ITEM_POSITION = 2;
private static int NORMAL_ITEM = 1;
private static int SELECTED_ITEM = 2;
#Override
public int getItemViewType(int position)
{
if(position == SELECTED_ITEM_POSITION)
return SELECTED_ITEM;
else
return NORMAL_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if(viewType == SELECTED_ITEM)
{
YourSelectedViewHolder selectedViewHolder = (YourSelectedViewHolder)layoutInflater.inflate(R.layout.selected_item_layout, parent, false);
return selectedViewHolder;
}
else //viewType == NORMAL_ITEM
{
YourNormalViewHolder normalViewHolder = (YourNormalViewHolder)layoutInflater.inflate(R.layout.normal_item_layout, parent, false);
return normalViewHolder;
}
}
I wanted to achieve a pretty similar effect on one of my project, where I wanted the middle item of my recycler view to be more prominent.
In my case, that said item is only z-translated to give an impression of focus, but the result is pretty similar to what you're describing.
I'll post my code here, in case it could help you go in the right direction :
//We're on the onCreateView in a fragment here
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//First I find the first visible element
int firstVisiblePosition = mLayoutManager.findFirstVisibleItemPosition();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (firstVisiblePosition != -1) {
int lastVisiblePosition = mLayoutManager.findLastVisibleItemPosition();
int itemHeight = mLayoutManager.getChildAt(0).getMeasuredHeight();
int itemTop = mLayoutManager.getChildAt(0).getTop();
//We use a '+' as itemTop will be negative
int delta = itemHeight + itemTop;
int currentItemToBeFocused = (delta < (itemHeight / 2)) ? 1 : 0;
//Reset the z-translation of other items to 0
for (int i = 0, last = (lastVisiblePosition - firstVisiblePosition); i <= last; ++i) {
if (mLayoutManager.getChildAt(i) != null) {
mLayoutManager.getChildAt(i).setTranslationZ(0);
}
}
//And set the z-translation of the current "centered" item
if (mLayoutManager.getChildAt(currentItemToBeFocused) != null) {
mLayoutManager.getChildAt(currentItemToBeFocused).setTranslationZ(10);
}
}
}
}
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
});

How to find if the RecyclerView is being Scrolled up or down

I have a RecyclerView with GridLayoutManager , i want to find inside the method getItemOffsets of the Class RecyclerView.ItemDecoration if the user is scrolling up or down . Below is my code inside the class GridDividerDecoration where i draw borders between the items.
public class GridDividerDecorations extends RecyclerView.ItemDecoration {
private int mInsets;
List<Integer> itemsHeaderPos= new ArrayList<>();
List<Integer> itemsPos= new ArrayList<>();
List<GadgetItem> mList = new ArrayList<>();
int lastItem;
int itemPosition, itemposition2 = 0;
public GridDividerDecorations(Context context,List<GadgetItem> mList ) {
this.mList = mList;
mInsets = 6;
}
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
itemPosition = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition();
if ((parent.getAdapter().getItemViewType(itemPosition) == 0))
itemsHeaderPos.add(itemPosition);
if((parent.getAdapter().getItemViewType(itemPosition) != 0))
itemsPos.add(itemPosition);
outRect.top = mInsets;
GridLayoutManager layoutManager = (GridLayoutManager)parent.getLayoutManager();
if(itemPosition == layoutManager.findLastVisibleItemPosition())
outRect.right = mInsets;
if (itemPosition % 2 == 0 && itemsHeaderPos.get(itemsHeaderPos.size() - 1) % 2 == 0) {
if (parent.getAdapter().getItemViewType(itemPosition) != 0)
outRect.left = mInsets;
} else if (itemPosition % 2 == 0 && itemsHeaderPos.get(itemsHeaderPos.size() - 1) % 2 != 0) {
if (parent.getAdapter().getItemViewType(itemPosition) != 0) {
outRect.right = mInsets;
}
}
}
}
The thing is that when I scroll up the dividers are painted upside down, so finding if the user scroll ups i can revert my implementation. Thank you!
You can determine if scrolled up or down with an OnScrollChangeListener:
recycler.setOnScrollChangeListener(new View.OnScrollChangeListener() {
#Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if(oldScrollY - scrollY > 0) {
// do stuff
}
else {
// do stuff
}
}
});

Checking if Android Gridview is scrolled to its top

I want to determine if my Gridview is scrolled to its top.
Right now I'm using getChildAt(0).getTop() to do this. I save the value of getChildAt(0).getTop() on first draw and compare to getChildAt(0).getTop() on subsequent draws .
However, this seems hacky and seems to sometimes give me incorrect results.
Any better ideas?
Try using the onScrollListener
setOnScrollListener(this);
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(firstVisibleItem == 0){
//do stuff here
}
}
The getChildAt(0) returns you the first visible item of the GridView and I guess that it's not what you want.
If you use yourGridView.getFirstVisiblePosition() method it will return the first visible position your data adapter, and that is what you want.
Hope this can help, it works for me, good luck.
yourGridView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(SCROLL_STATE_IDLE == scrollState) {
View view = view.getChildAt(0);
if(view != null) {
float y = view.getY();
Log.d("Tag", "first view Y is " + y);
if(y == 0) {
// do what you want
}
}
}
}
This's my answer:
mLayoutManager = new GridLayoutManager(getActivity(), 2);
mListRV= (RecyclerView).findViewById(R.id.list_rv);
mListRV.setLayoutManager(mLayoutManager);
mListRV.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
View v = mLayoutManager.getChildAt(0);
int offsetTop = v.getTop();
int offsetBottom = v.getBottom();
int height = (offsetBottom - offsetTop);
if(offsetTop >= -height/2) {
mListRV.smoothScrollBy(0, offsetTop);
} else {
mListRV.smoothScrollBy(0, height + offsetTop);
}
}
}
});

Categories

Resources