Android - NestedScrollView jump when updating TextView's from LiveData observers - android

I have a NestedScrollView which contains a view that is too large to fit on the screen (hence needing the scroll). There are various TextView elements that get updated fairly often (roughly every five seconds), and when they update the NestedScrollView scrolls to the top, completely forgetting where it was previously.
I have tried the solutions posted in "similar" questions, which primarily involve some version of the following:
In the TextView set android:focusable="true" and android:focusableInTouchMode="true"
In the parent layout (in my case, ConstraintLayout), set android:descendantFocusability="blocksDescendants"
I have updated all of the relevant views to include these, but the view still scrolls to the top whenever the TextView elements are updated. Has anyone had any luck in fixing something like this?

Related

Android layout: scrolling list with header

This is not a design question, I have the item designed. I am confused on how to pull it off.
I am tasked with designing a view for Android that has a view of a user's post and the comments under it. The post contains some extra information and widely different design from the comments, but all of them need to scroll in tandem, like a web page would (Oh, how I am spoiled by my years of web dev...).
My current solution is to nest a LinearLayout (at the top of the view to contain the user's post) and a RecyclerView (below the post to display the comments) inside a vertical ScrollView. The information is actually displayed, but the RecyclerView, of course, scrolls independently of the LinearLayout above it and ruins the functionality of the view.
I wish, if possible, to keep the RecyclerView in use
The best case scenario would be to have the LinearLayout with the post scroll a certain amount, and then the RecyclerView take over. However, I don't want to poison my codebase with 200+ ugly lines of code to achieve this, so if this is a laborious task to complete, I would rather look for alternatives.
The first thing to understand is: do you really need a RecyclerView, or even better, do you really need recycling?
If the answer is yes, the way to go is two different item types in the RecyclerView's Adapter (for more details, see here or here). This concept was already present in the ListView: the main difference is that RecyclerView enforce the use of the View Holder pattern. It is not so complex, and, more importantly, is the way the RecyclerView is supposed to solve that problem. Depending on your UI design, you may also want to have different view types for different types of comments (plain text, images, ...). Remember that, when the RecyclerView is included in a ScrollView, the recycling won't work, because all the items in it will be drawn at once to compute the height of the content of the parent ScrollView.
If the answer is no, then you could just create your views at runtime and add them to a parent LinearLayout in a ScrollView. It is really nothing more than a for loop.
A more fancy approach would be to use an ItemDecoration for the user's post, but I don't see any specific advantage in this case.

Does a RecyclerView with a fixed height still 'recycle' its elements?

I got into a situation where I have a ScrollView with some elements, where one of those elements is a table with many rows. I don't want my app to be laggy or use too much memory, so I thought it would help performance to use a RecyclerView for the table. But we all know the rule: Never put a scrollable view inside another scrollable view. So I searched a bit, and found this question, where the accepted answer was to fix the size of the recyclerview.
Now my question is: Does this still improve performance? As far as I know, a RecyclerView recycles it's elements by reusing the elements that are not visible because the user scrolled down. But does it still do this if we forbid the RecyclerView to scroll and use it's parent (ore parents parent) to scroll?

How to keep views in scrollview (that are inside a linear layout) that are not visible from being created?

I have linear layout inside a scrollview, and i would like to keep those views which are not visible(which are inside the linear layout) from being created until they are either visible or near to being visible. Like adjacent to one that is visible. I don't know if for example if turning off visibility will keep the View from being created(in terms of resources), or if gone could be used.
Update: The reason I am asking this question is because scrolling is taking a heck of a long time when there are say 30 view elements in the linear layout. These are not large view elements either, about what you would find for a record in a listview. I should add that the whole scrollview is inside a fragment.
Thanks
You have a LinearLayout(vertical) with 30 children. If you scroll to the bottom, which views would then be "created"?
If only the visible(or near visible) ones, then you can't measure how far down in the scroll they will be any more.
If at that point you're just saying you should create them when they first appear, and stay around afterward, that could work. However, once you scroll around some, you'll still have a bunch of views, and the same problem you have now.
Scrollviews exist and I happen to be using one.
True, but that doesn't necessarily means it's the best tool for the job. I tried something similar with my first Android project. I didn't know much about ListViews, and ended up rolling my own adapter that sounds eerily similar to what you're doing.
Long story short, the performance was bad. It did work, but once I(finally) switched to a ListView, the difference was nothing short of amazing. I'm just trying to keep others from going down that road. It was days of wasted time trying to get it "just right".

Hiding the Header view in ListView

I'm trying to add a header view to my list, and hide it all the time unless we scroll to it (like the pull-to-refresh mechanism). The problem is: if the list is not tall enough to fill the screen - the header view is shown on top of the list.
Is there a way to hide it, and make it visible only when we scroll to it? I've been trying a lot of stuff, but I can't figure out a good and simple way to do so.
Thanks
Here's a blog post describing a simple way of hiding and showing the header view.
The idea is to to put the content you wish to hide in a LinearLayout that wraps it, and hiding the content only. That way, the wrapping LinearLayout will collapse when its content is hidden, resulting in a headerView that is technically still present, but 0dip high.
Note: If you would try to hide the content without the enclosing layout, you would be left with unwanted space in the header view.
An example layout with a spinner representing the content:
<LinearLayout
xmlns:a="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<ProgressBar
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Then you can hide the spinner (content) as follows:
spinnerLayout.findViewById(R.id.spinner).setVisibility(View.GONE);
You could look into ListView.setOverscrollHeader() or ListView.setOverscrollFooter(). Is this the behavior you are looking for?
If not, could you post some code showing what you have so far?
EDIT:
Ok so I looked into Overscrolling headers/footers and you're right, I don't think that's what you want at all.
Instead you should probably look into the Pull to Refresh mechanism from the Twitter app that others have tried to emulate. You can look into the answers from this question.
The most promising answer seems to be the custom ListView written by Johan Nilson, the code for which can be found here:
https://github.com/johannilsson/android-pulltorefresh
EDIT #2:
I took a look at the PullToRefresh custom ListView and what you want to do is probably possible, though not necessarily easy. Allow me to explain.
The PullToRefreshListView is essentially just a hack that exploits the optional Header in standard ListViews. The hidden "Pull To Refresh" that you see is really just the header of the ListView. When the list is displayed, this line is executed:
setSelection(1);
This scrolls the list to the first item on the list, effectively hiding the Header. When the List is short enough to be displayed entirely on screen, no scrolling is necessary, hence the "Tap to Refresh" button.
When this "Tap to Refresh" is visible, the pull to refresh mechanism is disabled, but it's easy enough to fix that. The pull to refresh effect is accomplished by increasing the top padding of the header view so that it appears that you are pulling the list down (when really it's more accurate to say that the Header is pushing the rest of the list down).
The amount of padding added is controlled by the applyHeaderPadding() function on line 199 of the source code. In that function there is an if statement on line 220 that only applies the padding when the list is in RELEASE_TO_REFRESH mode:
if (mRefreshState == RELEASE_TO_REFRESH)
{
//Some code that eventually adds padding to the header...
}
If you eliminate this condition or change it to apply padding no matter what mode you are in you can drag to refresh even if the list is short and the header says "Tap to Refresh"
if (true)
{
//Some code that eventually adds padding to the header...
}
However, this doesn't exactly create the effect you're looking for. If the list is short, you can drag it down to refresh, but the "Tap to Refresh" header is still shown. Now the problem is "How can I hide the header until the dragging motion begins?" This is a difficult problem on it's own, with several Stack Overflow questions dedicated to it.
If you want a header, you must add it BEFORE you set the adapter for the ListView, otherwise you get all sorts of errors.
I had some success with this, but I haven't come up with anything stable, because my solution is a kind of nasty hack on top of the already hacked PullToRefreshListView. I set an empty FrameLayout as the header and added the original pull to refresh header to that Frame Layout. Then, as I dragged the list, I edited the height in the LayoutParameters of the Frame Layout to grow and shrink much like the padding had originally. It sort of worked, but would eventually force close, and I haven't figured out why yet.
Anyway, if I get it to work I'll post the code, otherwise someone wiser than I might propose a solution based on the info I just provided.
Here is a solution for the current PullToRefreshListView (updated November 4, 2011):
https://github.com/johannilsson/android-pulltorefresh
based on the Hiding Header Views article:
http://pivotallabs.com/users/joe/blog/articles/1759-android-tidbits-6-22-2011-hiding-header-views
1) Copy pull_to_refresh_header.xml from the library's res/layout to your app's res/layout.
2) Edit your app's pull_to_refresh_header.xml. Wrap topmost RelativeLayout in a LinearLayout and then wrap the LinearLayout in a RelativeLayout again. Why? Topmost layout must be RelativeLayout because that's what's expected in code, second level layout must be LinearLayout because that's the only layout that collapses with View.GONE. Third level layout must be the same as original top-level RelativeLayout (except id) to preserve look.
3) Preserve same id on top RelativeLayout (pull_to_refresh_header), give second level LinearLayout an id of your choosing, give third level RelativeLayout another id (pull_to_refresh_header2 for example).
4) Move all padding from the topmost RelativeLayout to the second RelativeLayout.
5) In your code use findViewById and your LinearLayout id to set visibility to View.GONE. The LinearLayout will collapse, and if you moved all padding values appropriately to the inner RelativeLayout the header should take no space at all.

Android ScrollView jumps around when resized

I have a ScrollView that contains an number of other views (TextViews, ImageViews, etc.). The ScrollView is taller than the screen. I have an AsyncTask that updates the children of the ScrollView based on an http response.
I've discovered an interesting behavior that I can't figure out how to work around. If I set any of the children's visibilities to View.INVISIBLE as part of the AsyncTask.onPostExecute(), everything works fine.
However, if I set any of the children's visibilities to View.GONE, the ScrollView jumps down from the top when onPostExecute() is called. Exactly how far seems to vary. I'm guessing that re-laying out the ScrollView is causing it to scroll away from the top for some reason.
So the question is: is there a way to either prevent or work around this behavior?
PS. Using ScrollView.jump(FOCUS_UP) as a workaround isn't ideal since that'll force the user to the top even if they had intended to scroll down.
EDIT: Actually, I was wrong. The problem wasn't with a child view being marked gone, the problem was with a sibling view being marked gone and the ScrollView getting resized. My ScrollView is inside a LinearLayout that also contains a Button. When the button is set to GONE, the ScrollView gets resized to take up the available space, causing it to scroll away from the top. Different cause, still looking for a workaround though if possible.
I had the same problem. If layout jumping add tha following in ur top lever layout
android:descendantFocusability="blocksDescendants"
Example:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:descendantFocusability="blocksDescendants" >

Categories

Resources