I have 2 column grid inside RecyclerView. I would like to use different margins on items in left column and different margins on items in right column.
Inside method bindViewHolder(VH holder, int position) of class RecyclerView.Adapter, how can I know if current item is on left side or right side?
I tried using position argument however sometimes it starts filling items from right side (image below, look at item 5) so it is no use.
You can get the column index through the itemView of the viewHolder.
StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
int position = lp.getSpanIndex();
I have 2 column grid inside RecyclerView. I would like to use
different margins on items in left column and different margins on
items in right column.
what about using different paddings for your RecyclerView? use different padding value for paddingLeft and paddingRight.
Related
I need to create a recycler view in which the items spacing should be in below format:
Each row should have 3 items.
The left margin of 1st item should be 16dp
Margin between each items should be 10dp
The right margin of last item should be 16dp
The margins are fixed but items should stretch to occupy the width to maintain margins.
How can i add the spacing constraints should grid layout be used or will work for linear too?
Try following things
In your recycle item use width match_parent and give a 10dp margin to the parent layout. (In your Adaptor Item Layout)
In your recycle view provide a 6dp margin. (In your XML having recycle view)
For show items in Grid obviously you need to use GridLayoutManager with your Adaptor.
I am trying to replicate a view from iOS so that user have same look and feel throughout the android application as well.
I am having a RecyclerView with LinearLayoutManager and horizontal orientation. So far so good.
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:listitem="#layout/item_recycler_view" />
With the output design:
However, in case of iOS design we have items starting from center however the horizontal view is completely scrollable (meaning the scrolling can be done to full width even if the item loading from center).
I know there is no use of adding padding/margin or using a different view like HorizontalScrollView. How can we obtain such behaviour so that i give nearly same experience to users.
Let me know if there is anything that i can provide to clarify the problem statement.
Quick solution
Add an empty item on the beginning and one on the end of your list, and make your index access account for those two extra items. That should help you get the desired effect.
Not so quick solution
Android allows us to write our own custom Layout Managers for RecyclerView. It comes with three types that will cover most of the user cases:
LinearLayoutManger (For lists in general);
GridLayoutManager (For grids);
StaggeredGridLayoutManager (For grids with items with custom sizes).
I believe you could write one to always start placing the first item on the center of the screen. That will require more work, but it won't mess with your data indexes.
Read this, and this, on how to create custom Layout Managers. Also, take a look at the docs. That should be a good place to start.
There are two ways you could do this. The simplest by far would be to add horizontal padding to your RecyclerView and set the view to not clip based on padding. Something like this:
android:paddingLeft="100dp"
android:paddingRight="100dp"
android:clipToPadding="false"
The other way would be to create an ItemDecoration and add it to your RecyclerView. You could then override the getItemOffsets() method to add a left-hand offset to your first item and a right-hand offset to your last item.
This second approach is better because it won't affect the RecyclerView's scrollbars, but it is a little more complex. Here's an example to get you started:
private static class MyItemDecoration extends RecyclerView.ItemDecoration {
#Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int parentWidth = parent.getWidth();
int childWidth = view.getWidth();
int margin = (parentWidth - childWidth) / 2;
int position = parent.getChildAdapterPosition(view);
outRect.left = position == 0 ? margin : 0;
outRect.right = position == (parent.getAdapter().getItemCount() - 1) ? margin : 0;
}
}
I think the only way will be adding different layout for first and last position in adapter of recyclerview.
It can be done using viewType parameter in createViewHolder(ViewGroup parent, int viewType)
What I intend to achieve
The item view should occupy the entire height of the item
It could be that the item height is lesser than the height of the tallest item in the recyclerview, in which case it should just stick to the top like in the screenshot above.
The bug I'm running into
As in the screenshot above, views are getting truncated.
What I've tried so far
Initially I went with wrap_content on the recyclerview, now that it is supported. It didn't work when none of the views visible on the screen at the time were the tallest. This makes sense in how the view hierarchy is laid out. How can the height of something which hasn't even been bound to any data yet be calculated if the height is dependent on that data?
Workaround time :S
Instead of trying a custom layoutmanager, I first went with what I felt needed to be done - laying out all item views at the beginning to figure out their height.
There's a progressbar and an animation playing in the upper part of the screen to catch the user's attention while all this happens with recyclerview visibility set to invisible. I use two things, one didn't suffice - I've attached an observer in the adapter's onViewAttached() call and I've used a scroll change listener as well. There's a LinearSnapHelper attached to the recycler view to snap to adjacent (next or previous, depending on the scroll direction) position on scroll.
In this setup,
I'm going to each position in the recyclerview using layoutManager.smoothScrollToPosition()
Getting the child view height using
View currentChildView = binding.nextRv.getChildAt(layoutManager.findFirstCompletelyVisibleItemPosition());
if (currentChildView != null) {
currentChildHeight = currentChildView.getHeight();
}
in scroll change listener on RecyclerView.SCROLL_STATE_IDLE or by passing the height to the view attached observer mentioned above in the adapter's onViewAttachedToWindow()
#Override
public void onViewAttachedToWindow(BindingViewHolder holder) {
if (mObserver != null) {
mObserver.onViewAttached(holder.binding.getRoot().getHeight());
}
}
Storing a maxHeight that changes to the max of maxHeight and new child's height.
As is evident, this is ugly. Plus it doesn't give me the current view's height - onAttached means it's only just attached, not measured and laid out. It is the recycled view, not the view bound to current data item. Which presents problems like the truncation of view illustrated above.
I've also tried wrap_content height on the recycler view and invalidating from recycler's parent till the recycler and the child on scroll coming to SCROLL_STATE_IDLE. Doesn't work.
I'm not sure how a custom layoutmanager can help here.
Can someone guide me in the right direction?
I could not accept #Pradeep Kumar Kushwaha's answer because against one solution, I do not want different font sizes in the list. Consistency is a key element in design. Second alternative he gave couldn't work because with ellipsize I would need to give a "more" button of some sort for user to read the entire content and my text view is already taking a click action. Putting more some place else would again not be good design.
Changing the design with the simple compromise of resizing the recyclerview when the tallest, truncated item comes into focus, it turns into the simple use case of notifyItemChanged(). Even for the attempt I made using the view attached observer and scroll state listener, notifyItemChanged could be used but that approach is just too hacky. This I can live with in both code and design. Here goes the code required.
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int position = ((LinearLayoutManager) binding.nextRv.getLayoutManager())
.findFirstVisibleItemPosition();
if (position != nextSnippetAdapter.getItemCount() - 1) {
binding.nextRv.getAdapter().notifyItemRangeChanged(position, 2);
} else {
binding.nextRv.getAdapter().notifyItemChanged(position);
}
}
}
For my particular setup, calling for just these two elements works. It can further be optimized so as to call for single element at position + 1 in most cases, and checking and calling for the appropriate one in corner (literal) cases.
Inside your adapter where I can find two cards one on top and another on bottom
How I would have defined my layout is like this:
Cardview1
LinearLayout1 --> orientation vertical
cardview2 (Top card where text is written)
Linearlayout2 (where I can see icons such as like etc)-->orientation horizontal
Now fix the height of Linearlayout2 by setting it to wrap content.
And the height of cardview2 should be 0dp and add weight = 1
Now inside cardview2 add a TextView1 to matchparent in height and width.
Better inside textview1 add ellipsize to end and add max lines
If you want to show all lines try to find autoresizetextview library it can be founded here --> AutoResizeTextView
Hope it helps.
I think the recyclerview can be set to height wrap_content. And the items can be make like height to match_parent.
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layput_height="wrap_content"/>
Item as:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
// your coode
</androidx.constraintlayout.widget.ConstraintLayout>
I had little more requirement than the question. Even my problem solved in the way.
Remember I am using:
androidx.recyclerview:recyclerview:1.0.0-beta01
dependency for the project
I would like to implement the UI like the below image.
I layout the UI using GridView and I got only the result like the below image with the same horizontal spacing.
I would like to know that does Android GridView support difference spacing size between grid item? If there is any solution or third party lib, please help. Thank you. Sorry for my poor in english.
Edit:
According to sgadde and Gary Bak's answers, I calculate and add right margin to items in second column in adapter's getView method with the following code.
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 20, 0);
viewHolder.numberTextView.setLayoutParams(params);
and I got the following result.
My code to add margin of grid item shrinked the size of grid item. Is something wrong? Please help how to add spacing. Thank you.
You should calculate and add right margin to items in second column, left margin to items in third column dynamically. You can do it in getView() of your adapter.
In your adapter's getView method you have the position, when you hit the position for 2, 6, 10, etc.. then you can add addition spacing on the right.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
My answer will be a little tricky.
First you need to reduce horizontalSpacing to adjust to be square.
Now, you need to add margin for all columns.
There is four columns, and so
column 1: left margin
column 2: right margin
column 3: left margin
column 4: right margin
This will be like what you want.
I am trying to make the rows of my listview such that clicking on any one expands that row in place and shows extra options. I use an extra layout (let's call it expandView) contained with the row layout (call it rowView) with height set to zero initially and use a valueanimator to expand it to the final height when rowView is clicked.
Problem is, I have no idea what the final height is going to take until the user actually clicks the rowView. So I cannot specify some fixed end value for the animation, nor can I sum up the heights of expandView's children on rowView click since they too return zero when expandView's height is zero.
I really want to avoid hardcoding the height values for expandView or it's children here. Any pointers?
Figured it out. This answer given here helped. I have to call call measure() followed by getMeasuredHeight() to get the final height of my expandView.