How to center RecyclerView in the center (and without scrolling) when possible? - android

This is a short question:
Suppose I have a RecyclerView that has undefined number of items, what should I do to it, so that if there is a small number of items that (all) can fit the screen, they will be centered and the user won't be able to scroll ?
Of course, if there are too many items, that cannot fit the screen, I would like to have the RecyclerView to show them all as normal (from the beginning to how much it can show, and allow to scroll).
To understand what I mean, I think such a thing is possible when using ScrollView (or HorizontalScrollView if in horizontal), together with a LinearLayout that sets the gravity to be centered .

OK, I think I've found a way:
first, we wait for the RecyclerView to finish its layout process, as I've found about here .
Then, you need to check which child views are shown (available in the LayoutManager that you use), and look at the first and last ones.
If both of them are exactly the same as those that of the total items, it means all needed views are shown, so I can add margins on both sides of the RecyclerView (or padding on its container), according to the space that's left.

I found one complicate way to achieve that.
Main concept: set the height of RV dynamicly in code
RecyclerView rv= (RecyclerView) findViewById(R.id.rv);
rv.setAdapter(new MySlideUpAdapter());
rv.setLayoutManager(new LinearLayoutManager(this));
ViewGroup.LayoutParams layoutParams = rv.getLayoutParams();
layoutParams.height=100;
rv.setLayoutParams(layoutParams);
You may need to calculate the RV's height by childcount*childheight to get pricise value. And don't forget to compare the height to the Parent Layout height, make sure RV's height is less than its Parent Layout height.
Here is my Layout
<RelativeLayout...>
<android.support.v7.widget.RecyclerView
android:id="#+id/rv"
android:layout_centerInParent="true"
android:layout_width="match_parent"
<-height will be changed in code, ignore the 50dp->
android:layout_height="50dp">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>

Related

Adding views dynamically to LinearLayout not working as expected

I'm inflating views inside a linearlayout dynamically, however once the linear layout reaches the end of the first row, it cuts off the rest and doesn't start on the second row.
for(int a = 0; a < mSkills.get(i).size(); a++){
View singleSkill = LayoutInflater.from(mContext)
.inflate(R.layout.singleskill, holder.mSkillLayout, false);
TextView skillText = singleSkill.findViewById(R.id.singleskilltext);
skillText.setText(mSkills.get(i).get(a));
holder.mSkillLayout.addView(skillText);
}
For the linear layout I have it set to wrap_content for the height:
<LinearLayout
android:id="#+id/ll_skills"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_below="#+id/tv_description"
android:layout_margin="16dp"/>
I've tried setting it to a defined height e.g 300dp however that doesn't work either.
How can I make the layout start on the second row, once the first is full?
Linear Layout can either fill views horizontally or vertically so the 2nd row you are expecting cant to be done with linear layout only. you can try a horizontal scroll view for that to scroll horizontally. For the exact view-like flow that you described, you can use this 3rd party https://github.com/nex3z/FlowLayout
It can manage the flow of your dynamically inflated view such as if there is no space in the first line then it will put the next view in the second line.
also, you can use material design library chips https://material.io/components/chips/#usage
LinearLayout works exactly how it has to be because you specify it as horizontal. For such behavior, you need RecyclerView With GridLayoutManager or create your own layout;).
Actually it's doing exactly as it should be, LinearLayout is Linear!, and place its subviews in a single horizontal or vertical row.
My advice to you is that create dynamic horizontal LinearLayout as you already doing with TextViews. and put every 3 or 4 textviews (depending on screen size) inside it.
and put all LinearLayouts inside one vertical LinearLayout...
Of course in your case, it's not a good idea, the best thing you can do is to use recycler view. but I consider you have problem with that.

Showing full height layout above recyclerview inside scrollview

As the title says. I'm trying to display extremely complex layout with a full height of the viewport but I need it scrollable because under it there is a simple recyclerview with some items. I already thought about putting everything inside a multi type recyclerview adapter but the logic of the upper layout is so complex that I don't think it's possible.
I tried using NestedScrollView with fillViewport set to true but I'm stuck defining dimensions of this upper layout and recyclerview below it. Everything needs to be inside one layout because scrollview can't have more than one child, but when I put everything in a linearlayout and set the upper layout to match_parent it's showing fullscreen until data loads in the recyclerview below it. Then it's treating this upper layout as if it was wrap_content.
I'm out of ideas how can I do something like this. Preferably best would be to have some sort of ViewGroup which would support scrolling and resize the recyclerview below it as we scroll, but I'm not sure how to do it.
you need to set the layout to something like this:
<NestedScrollView - height:match_parent>
<LinearLayout - height:wrap_content>
<LinearLayout(topview) - height:wrap_content/>
<RecyclerView - height:wrap_content />
</LinearLayout>
</NestedScrollView>
And then you programmatically change the height of the "topview" to equal nestedscrollview.

Horizontal RecyclerView with variable item heights not wrapping properly

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

Make First line height in textview the same as other lines

I have a recyclerview with bunch of textview each as an item as you see in picture below. (Blue lines aren't really there, i added them so you can see each item separately) as you can see everything seems nice and user will not notice the text is separated.
The problem is when user increases the line space(a typical option in app) line height gets bigger except the first and last line of each item and the result seems like second picture.
My question is how to find appropriate padding to set to each item so every line height seen exactly the same?
BTW i can not use just one textview for many reason!
You can increase the divider height of your listview according to linespacing height.
Or you can set an invisible view at the bottom of every row item and increase the height of this view according to linespacing height.
Or you can set padding at the bottom of every row item and increase the value of this padding according to linespacing height.
just add some padding to the parent of your textview in the layout file of your list item .
for example the layout with linearlayout would be like :
<LinearLayout ........
paddingTop=15dp>
<TextView
..........>
</TextView>
</LinearLayout>
and you could adjust the padding dynamically if you want.

Constrain the height of RelativeLayout to specific child view

I there a way to wrap_content on a specific element inside of a parent element? For instance, I have something like the following layout:
<RelativeLayout width:match height:wrap>
<ImageView width:match height:wrap scale:fitXY />
<LinearLayout width:wrap height:wrap>
</RelativeLayout>
The parent wrap constraint is very loose, but I want it to specifically use the matching width, but always match the height of the image view.
The problem here arises when I place this view in another RelativeLayout where each view is aligned above or below another in order to fill a potentially changing superview. LinearLayout didn't really seem to stretch things to fill, so I switched to Relative, but when I did, the view described above stretched vertically when I want it to still match the height of the image view.
Is there a good solution to this problem?
You could try putting the following (pseudocode) in the onResume() method:
if(myRelativeLayout.height > myImageView.height)
myRelativeLayout.setHeight(myImageView.height);
You need to make sure to call myRelativeLayout.measure() before you do this, so the system knows what the size of the Views will be.
Just an idea for you to try, let me know if it works :)

Categories

Resources