My application has a lot of nested recycler views , In one of those I have a layout which holds an image and a textview like this.
The text view is an indicator which would disappear when the user opens that specific course.
And it would become something like this :
Since the indicator is present only on some of the courses , the indicator might disappear (view.gone) and appear (view.visible) on certain conditions.
This is the recyclerview code that holds these courses.
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_video_packs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin_16dp"
android:clipToPadding="false"
android:paddingEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/rv_continue_watching" />
Notice that the recycler view is a wrap content.
When there is a case of multiple courses , some courses might not contain the new video indicator. And if those courses with no indicator is present on the screen then the recyclerview would wrap its height to only hold on to the course image , something like this.
but the issue is , when we scroll horizontally , some courses might contain the indicator , meaning that the recyclerview should increase its height accordingly to accommodate the indicator as well .
Instead recyclerview fails to update its height to wrap around both the view hence resulting in this incomplete view.
This is happening because the recyclerview was unable to update its height when scrolling hence making the view cut in half.
I have tried multiple ways to fix this like ,
Setting setHasFixedSize to false etc . But didnt seem to work
This is happening only sometimes.
The only viable solution now is to fix the recycler view height to a specific height so that it holds both the views.
But I want a more scalable and optimum solution if possible for this .
Is there any way to make recyclerview recalculate its height ?
Recyclerview Item view code.
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true"
android:orientation="vertical"
>
<ImageView
android:id="#+id/iv_learn_item"
android:visibility="invisible"
android:layout_width="134dp"
android:layout_height="200dp"
android:adjustViewBounds="true"
android:layout_marginStart="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:foreground="?attr/selectableItemBackground"
/>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/video_update_tv_guideline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintGuide_begin="195dp"
android:orientation="horizontal"
/>
<TextView
android:id="#+id/video_update_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="#id/iv_learn_item"
app:layout_constraintEnd_toEndOf="#id/iv_learn_item"
app:layout_constraintTop_toBottomOf="#+id/video_update_tv_guideline"
android:layout_marginStart="#dimen/dimen_11dp"
android:layout_marginEnd="#dimen/dimen_11dp"
style="#style/content_update_pill"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Adapter code
rvVideoPacks.layoutManager =
LinearLayoutManager(itemView.context, LinearLayoutManager.HORIZONTAL, false)
rvVideoPacks.setHasFixedSize(false)
rvVideoPacks.itemAnimator = null
rvVideoPacks.isNestedScrollingEnabled = true
rvVideoPacks.adapter = videoPackAdapter
EDIT :
I have also tried to make the indicator invisible , but that just creates an extra space at the bottom , making it look bad.
I want the recyclerview to adjust its height accordingly.
One way to fix this issue is give fix height to main ConstraintLayout if possible for your layout.
Related
I have some issue with follow structure:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/white_rectangle_radius10">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/first"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
/// many views here
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/second"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/first"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
/// many views here
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/third"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/second"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
/// many views here
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Programatically it can be 3 inner constraints or 4 or 2.
Root Constraint has round border radius.
Programatically when one of inner items clicked I set to this item background color, BUT when its first or last it overlap root border radius.
Note:
No need to suggest cardview as root element (not my option)
"Smart play" with margins/paddings also doesn't suit me because it should be changed programatically.
Is there some option to do it via XML?
When I added background color to first element it's top start/end corners not rounded.
First it is not recommended to nest layouts if you are going to use constraintLayout.
Second is how to design your view smoothly?
one of constraintlayout benefits is using the drag and drop option, therefore, start by doing that and ensure to place every item in its right place (but do not apply any constraint yet).
once all items are on the layout, click any item and look at the tool bar above the mobile screen, there is a magic wound, click on it, the constraint will be generated automatically based on the way you orgnized the view on the screen.
once that is done, now you might need to adjust the margins a bit according to the requirements.
also please assign id to each view before clicking the magic wound.
I hope I was helpful.
I have a chat app and i'm trying to ensure that when the user opens the screen the items display from the bottom with the most recent right above the input area. I've used a custom view for the input area and both the recycler and the custom view are within a ConstraintLayout.
My problem is that when I load items into the list, if the number of items is greater than the size of the recycler, it will not fully show the last item. When I make the input area visibility = Gone then the items display properly at the bottom. It's almost like the recyclers LinearLayoutManager thinks that the height of the Recycler is of the screen without the input field. I've manually printed out the size of the views and used layout inspector to ensure that the recycler is indeed drawn in the correct location (above the input and below the navigation bar).
What could be causing such an issue? I should note that whenever you click on a Linkified text in a chat bubble that the list scrolls a small amount equal to the offset that's incorrect when you open the screen. Clearly something is not measuring right here and not sure where to begin.
I should also note that if I try to add a post with smoothScroll it will go to the end of the list but then whenever a new item appears in the list from sending a message the items above the most recently added one seem to jump up a little with an unnecessary animation. It's like the last item in the list is in some special state?
if you're curious this is my scrolling function:
private fun scrollToFirstItem(dueToKeyboard: Boolean = false) {
val stackingFromEnd = (recyclerView.layoutManager as LinearLayoutManager).stackFromEnd
if (stackingFromEnd) {
val firstPosition = recyclerView.adapter?.itemCount?: 0 - 1
if (dueToKeyboard ) {
recyclerView.scrollBy(0, Integer.MAX_VALUE)
} else {
recyclerView.scrollToPosition(firstPosition)
}
recyclerView.post { recyclerView.smoothScrollToPosition(firstPosition) }
} else {
recyclerView.scrollToPosition(0)
}
}
And my xml for my fragment:
<androidx.constraintlayout.widget.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">
<include
android:id="#+id/searchView"
layout="#layout/compose_new_message_include"/>
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="#+id/conversationEpoxyRV"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scrollbars="vertical"
app:layout_constraintTop_toBottomOf="#id/searchView"
app:layout_constraintBottom_toTopOf="#id/composeView"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
tools:listitem="#layout/conversation_item_inbound"/>
<include layout="#layout/conversation_pip_view"
android:id="#+id/selectedMediaContainer"/>
<****.ComposeView
android:id="#+id/composeView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Any help is appreciated, I'm so lost..
To me this feels like more of an issue in the layout rather than in the scrolling function. I hope that this can be resolved easily if you use a Relative Layout instead of a Linear Layout. So in case if it may be helpful, i'll add my solution below using a Relative Layout.
<RelativeLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/input_field">
</androidx.recyclerview.widget.RecyclerView>
<EditText
android:id="#+id/input_field"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
So in case if it is not clear what i have done, an EditText element is set to align to the bottom of parent which is a Relative Layout. Then a RecyclerView is added so that RecylerView will be always constraint above the EditText but not overlapped.
Everything looks fine, except the restrictions you added to recycleview design, I see that you are setting the recycleview to top of searchView app:layout_constraintTop_toBottomOf="#id/searchView" while the searchView view is not restricted as should it be.
It's better when using ConstraintLayout to restrict every view inorder avoid unexpected behaviors, because every view has a relation with other view will be effected with other view (x, y), therefore your searchView should be look like:
<include
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
layout="#layout/compose_new_message_include"/>
Give the size of arraylist of your data to smoothScrollToPosition
var linearLayoutManager = LinearLayoutManager(context)
recyclerView.layoutManager=linearLayoutManager
recyclerView.adapter=adapter
linearLayoutManager.setStackFromEnd(true)
adapter.notifyDataSetChanged()
recyclerView.smoothScrollToPosition(dataMsgs.size())
I had a similar problem some years back and I solved it with scrollToPositionWithOffset (support library). Of course this was before constraint was used....
I kept my newer items at the bottom, too. The code I used for scrolling after inserting an item was:
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(getItemCount() - 1, 0);
To scroll and adjust the awkward positioning after removing an item, I used:
View vTopCard = layoutManager.getChildAt(0);
// Get its adapter position
final int topCardPos = layoutManager.getPosition(vTopCard);
// Get top of card position
int topOffset = layoutManager.getDecoratedTop(vTopCard);
int cardMargin = (int) getResources().getDimension(R.dimen.card_vertical_margin);
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(topCardPos, topOffset - cardMargin);
The getDecoratedTop helps with the "bounce" and final positioning, as does factoring the vertical margin.
I hope this helps (at least part of your issue)! Like I said, this is old code (when I was learning to program Android and Java at the same time), so if I left something out, let me know and I'll reexamine the app's code in more detail (though, I'll have to find the time).
Good luck!
I want to document the problems I ran into and the solution for the benefit of others, and find help with the key flaw in my solution.
I want a RecyclerView with an arbitrary number of rows in a layout with a number of other Views. The RecyclerView and other Views should be in a ScrollView that scrolls, but the RecyclerView itself should not scroll.
Since the number of rows in the RecyclerView is unknown, and I'm required to have other views immediately underneath the RecyclerView, I can't use a fixed height or match_parent.
I experienced some odd problems: when I would update the RecyclerView data (using an AsyncListDiffer) and the UI was supposed to update, the entire RecyclerView would jump above the view it was constrained underneath, straight to the top of the parent. This is not how a ConstraintLayout is supposed to behave.
Then I was able to stop that from happening, but Views underneath the RecyclerView would disappear--appearing once the RecyclerView data updated.
The solution:
Put the RecyclerView and other Views inside a ConstraintLayout inside a ScrollView (or NestedScrollView)
Set the RecyclerView to have a height of wrap_content (and adding app:layout_constrainedHeight="true" doesn't hurt)
Make sure the rows in the RecyclerView have a fixed height and do not have a height of wrap_content
This was helpful to me after banging my head against the wall and trying some proposed solutions that did not work: make RecyclerView's height to "wrap_content" in Constraint layout
This solution is pretty simple. The layout can be something like:
<ScrollView
android:id="#+id/scrollView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- other Views -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/otherView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/anotherView"
/>
<!-- other Views -->
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
And then the layout for a row:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight">
<!-- other views -->
</androidx.constraintlayout.widget.ConstraintLayout>
Here's the problem: I'm not a fan of fixed-height rows. What if other content needs to go in there? What if the user changes their text size? I much prefer flexible layouts that can adapt in size. But if I do that, then the RecyclerView takes up a screen's worth of height inside the ScrollView, shoving all lower views off the bottom of the screen, no matter how few rows it contains.
The alternatives I can think of are to make all the other Views outside the RecyclerView become rows of the RecyclerView, or to avoid a RecyclerView altogether and programmatically add Views to a LinearLayout. These are much uglier approaches.
Is there a way to fix it so the rows of the RecyclerView can have a height of wrap_content?
I have been given the task to implement a feature on Mobile application. Previously I had no experience with Android nor Java.
What I need is one screen (one fragment) to display two lists of events with uneven number of members one after another, let's call them ListX and ListY.
I've made layout containing two RecyclerViews and two labels (one label to act as header for each of recycler views).
It is working as it is now and I have events displayed, but ListX is scrollable even though its layout_height is set to wrap_content (only 6 items displayed at the time) while ListY is not scrollable and is displaying all events in collection.
What I need is all items from ListX displayed in first RecyclerView (without scroll), and after that ListY displayed in second RecyclerView without scroll too.
Layout:
<FrameLayout
android:id="#+id/resultsView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/label_daily" />
<android.support.v7.widget.RecyclerView
android:id="#+id/list_daily"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/label_daily"
tools:listitem="#layout/fragment_timetracking_event_item" />
<TextView
android:id="#+id/label_unfinished" />
<android.support.v7.widget.RecyclerView
android:id="#+id/list_unfinished"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/label_unfinished"
tools:listitem="#layout/fragment_timetracking_event_item" />
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
As you can see from XML both RecyclerViews have layout_height set to wrap_content yet first RecyclerView is not obeying.
Here is sketch of how it should look like
Is there any way to put both lists in one RecyclerView and make custom separators? Or any other way to anchor beginning of one list to the end of the other and make them show all items without scroll?
I hope to get help here.
You don't need 2 recyclerViews, you can achieve using single recylcerView.
You should read about getItemViewType & using same in bindViewHolder & createViewHolder
You can specify these three types
Header
DailyItem
FinishItem
Create your list like this
add Daily HEADER
add Daily ITEMS
add Unfinished HEADER
add Unfinished ITEMS
Now you can render both lists in single recyclerview
Your problem
...other way to anchor beginning of one list to the end of the other and make them show all items without scroll?
has a simple solution without rewriting your code to the one RecycleView - just put android.support.v4.widget.NestedScrollView instead of ScrollView in your xml layout. It seems to be a bug in the ScrollView widget, which is fixed in android.support.v4.widget.NestedScrollView.
P.S. After solving this problem you will face with other problem - Android RecycleView scrolls with no momentum, which is resolved in "RecyclerView within NestedScrollView Scrolling Issue". It is fine only for short item list in RecycleView because as it is mentioned in comments to above article
This is not a good solution since disabling nested scrolling will disable cell reuse as well, therefore loading all cells at once.
which will have impact on the memory consumption of your app in the case of long item list.
I have a simple layout with a recycler. I feed it with Adapter and set GridLayoutManager for the recycler.
<android.support.v7.widget.RecyclerView
android:id="#+id/scripts_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="50dp"
android:verticalSpacing="20dp"
android:clipToPadding="false"
android:orientation="vertical"
app:layoutManager="android.support.v7.widget.GridLayoutManager"
tools:listitem="#layout/tmpl_script"
android:paddingBottom="20dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:paddingTop="30dp"
app:layout_gravity="center" />
Activity
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.scripts_recycler_view);
recyclerView.setAdapter(adapter);
int columns = getResources().getInteger(R.integer.scripts_columns);
recyclerView.setLayoutManager(new GridLayoutManager(this, columns));
recyclerView.setHasFixedSize(true);
Item
<LinearLayout
android:layout_width="300dp"
android:layout_height="300dp"
android:background="#drawable/game_border"
android:gravity="center"
android:orientation="vertical">
I have two problems:
there is no space between rows
items are not centered within the row
I tried all properties in Android Studio but they do not have any effect. I googled, opened many questions but I still have no luck. What magic property shall I use?
As you can see items are positioned on left side of the screen. I want to move it to center. And there is no space between first and second row.
The easiest solution for (1) is to add top and/or bottom margin, like #suraj said.
Regarding (2) - the critical issue is that your items are square. To get it working, set android:layout_width="match_parent" and change the design of your items to use the actual smaller dimension (width or height) for both dimensions (maybe all you need to do in your case is to make the background square). Then display the content centered within the item's LinearLayout (you are already doing this). You have to do it this way because GridLayoutManager does not support layout_gravity at this time.
For 1.there is no space between rows
Try android:layout_marginBottom="10dp" in the linear layout of item.This should provide the spacing below each item.
For 2.items are not centered within the row
Looks like you have given right margin to your recyclerview and no left margin.
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
should give equal spacing on both sides.
or set
app:layout_gravity="center"
in linear layout of the item.