I have problems doing a small game which adds a lot of views in a ConstraintLayout (I already tested with FrameLayout, RelativeLayout and LinearLayout. First two with same result and Linear with very rare behaviour) and changes the size and position of the views.
Each game loop (33ms) some of the views are changing it's size and position, so I do this on the LayoutParams variable applied on the View on each loop with the new size.
params.width = realWidth;
params.height = realHeight;
and I do this for the position:
view.setX(realX);
view.setY(realY);
The problem is that the change is not reflected if I didn't call view.requestLayout() and that is a huge problem because requestLayout() is repaiting the parent layout and all its childrens, slowing down the game.
How can the size changes of a view be reflected without calling requestLayout? I read that you can achieve that with view.layout(). So I changed my code with this:
view.layout((int)realX, (int)realY, (int)realX+realWidth, (int)realY+realHeight);
The problem is that it works without calling requestLayout() but the view has a very rare behaviour using layout(), and when I add more views, the views become some milliseconds invisible and appear on the left top corner, after that they appear in the correct position, but suddenly they become invisible again and again and again etc in a very rare behaviour loop.
ConstraintLayout is slow, particularly if it contains many Views and much slower if it contains nested ConstraintLayouts.
Because of you are manually setting the position for your Views, you can use a different ViewGroup than ConstraintLayouts and set the absolute position with setX(), setY(), setTop(), etc.
Related
I have a frame layout with 2 children. A custom view and a progress bar.
Only 1 at a time is visible. Works as I need.
I want to also add some error view which will display an error and the other components would be hidden.
Is using a FrameLayout a good idea or should I switch to RelativeLayout?
If the layout works for you I would stick to FrameLayout. Since there are only 3 children (and assuming that none of them has children) the difference in performance should be really minimal (if any), but one difference I noticed digging into the source code of both FrameLayout and RelativeLayout is in the onMeasure method, FrameLayout will iterate twice all of its children, RelativeLayout iterates from 3 to 7 times (!)you can check FrameLayout onMeasure and RelativeLayout onMeasure yourself
Is it possible to animate RecyclerView height, or otherwise change it programmatically? For example, if I have a large header view that is sometimes visible, and other times not - I would want to animate the RecyclerView height to fill the screen when the header is animated out.
Changing LayoutParams.height does not seem to work. LinearLayout animateLayoutChanges causes a crash.
<LinearLayout>
<RelativeLayout (header)>
<RecyclerView>
</LinearLayout>
I want to make the RelativeLayout animate out the top (translationY) and then at the same time make the recyclerview animate to be taller to fit.
There are possible options to tackle this:
Follow suggestion from #Ari to start animation and on every animation tick update layout params. This will make an effect of recyclerview changing its size. However, this is a horrible idea from performance stand point. Layout and Measure process is quite expensive, so generally you want to minimize calls which trigger layout. Call to setLayoutParams will trigger layout & measure process for RecyclerView + all its children which means that on every single frame you will do really expensive work which most likely will lead to framedrop and bad user experience
There is another way though. It might not work in all cases - it all depends on your final layout, but still that's what I would recommend doing. The idea - is to make your recyclerView taller before you start animation. It requires some advanced Android skills though. Basically you need to override onMeasure & layout methods in your RecyclerView (you actually need to extend RecyclerView class to do that).
You can introduce some flag to your recyclerView to measure itself a bit taller than normal (how much taller - the exact height of your header view)
when you need to animate header out - set your flag to true and request new layout. This will re-layout recyclerView with some invisible part at the bottom.
Now you can just animate y translations of both RecyclerView & Header so header moves out of the screen and recyclerview goes higher. This will make user feel like recyclerview "expands"
Once animation is done - set your custom flag to false and change visibility of your header to GONE since it is off screen now
Here is some information about implementing custom onMeasure logic:
https://medium.com/android-news/perfmatters-introduction-to-custom-viewgroups-to-improve-performance-part-2-f14fbcd47c
I have solved a weird issue, and I would like to know why it happens.
My application creates screens with FrameLayout as the root element. For Simplicity, let´s say I have a Framelayout with some scrolling elements inside, for example a ScrollView with a TextView, and a GridView.
These child elements are positioned with LayoutParams so I assign them a Width, a Height, and a Margin Left / Margin Top. The usual case is their sizes do NOT match the parent, that is, they are floating inside the parent FrameLayout.
For non-scrolling elements, everything is cool.
But for scrolling elements, when scrolling, I can see all kinds of leftovers OUTSIDE the child bounds. Inside the bounds the scrolling is perfect, but the elements are not clipped properly.
I did a lot of tests, played with setClipChildren / setClipToBounds, changed the root element from FrameLayout to RelativeLayout, etc etc, without success.
I only succeeded when I wrapped the scrolling elements inside another Framelayout/RelativeLayout without any margins, and then assigned the scrolling elements MATCH_PARENT/MATCH PARENT.
The scroll is perfect now, clipping is properly done, but I have added another level of complexity to the layout.
Why does this happen?
For example, top level LinearLayout view has 300 child view. But device screen dimension only show 11 child view once. How android compute how many child views can show once? How a view know that it will be draw?
Edited
In my work, one case like this:
An parent LinearLayout view may be has hundreds child view. In order to better performance , my solution like this:like lazy load.
List list = new ArrayList();//contain entity object that use construct View object
Default load 5 child view.
Parent LinearLayout view last child view is custom Loading View, I have override it`s onDraw() method. If loading View is draw, that means i need get next 5 child view(get next 5 object from list,and create correspond view).
I want to know how android framework handle this case?
Have u used scroll bar inside the top level LinearLayout view and add child view on that layout that's simple...
An parent LinearLayout view may be has hundreds child view. In order
to better performance , my solution like this:like lazy load.
LinearLayout with(possible) hundreds of child views kind of contradicts better performance. I think what you're looking for is a ListView with an endless adapter(a simple google search will show how to implement it, not something that difficult). This adapter will start loading views(with a loading view showing while the new content loads) as soon as you get to the last loaded element.
If you still want to use a LinearLayout:
If you just want to make the LinearLayout fill the content of the screen when it's first laid out you could post a Runnable on one of your views in the onCreate method (if this is where you'll first populate the LinearLayout). In that Runnable find the height of the LinearLayout and compare it with the combined height of its currently present children. If the combined child height is smaller then the LinearLayout height then add more views to compensate.
If the LinearLayout is in a ScrollView and you want to add additional children when the loading view becomes visible then monitor the scrolling of the ScrollView and when the loading view becomes visible add new children. Seeing when the loading view becomes visible would be done by checking how much the user has scrolled compared with the combined height of the currently present children of the LinearLayout.
Initial response:
Your question is a bit ambiguous regarding what you want to know. If you have a specific problem you should start with that.
For example, top level LinearLayout view has 300 child view. But
device screen dimension only show 11 child view once.
Please don't get yourself in a scenario like this. That number of views is to big an will result in poor performance or even the app crashing if you run out of memory(as all those views will be kept in memory).
How android compute how many child views can show once?
Each View and ViewGroup has the onMeasure method to measure itself and its children if available. The LinearLayout will have its onMeasure method called and in this method it will measure its children(with the measure method) giving them some suggestions on how big should they be(the LinearLayout receives some suggestions on how big it should be from its parent). If you want to see how this is done have a look at the source code of the LinearLayout.
How a view know that it will be draw?
I don't understand what you want to know. To draw the view on the screen its onDraw method will be called.
Let me explain the scenario that I want to achieve:-
Consider the below as the Layout I have inside a Parent_Linearlayout:
[Linear Layout] (Fill_Parent, Wrap_Content)
[ScrollView]
Activity's setContentView is set to the Parent_Linearlayout
In the application, when a condition is met, I want the Scrollview to be removed from the screen and instead put another View in its place.
I've been able to do this, & when I remove the ScrollView, I'm applying translate Animation to it so that it seems as if the View has gone to the top -before removing it.
But when the animation occurs, the ScrollView translates OVER the Linear layout present above it.
How do I restrict it, so that the scrollview does not go over the linear layout, but disappears at the base of the Linearlayout. I want the linearlayout to always stay visible..
I've been trying to do this from quite some time, but I've not been able to get desired results..
Could someone kindly help me out here??
I don't quite understand your description of your layout, but the Android view system is drawn based on the ordering of the views in the hierarchy. Views added later to a parent are drawn after those added earlier. So if you always want the LinearLayout to be drawn on top of the ScrollView if/when they overlap, then declare or add the ScrollView object to its parent before the LinearLayout object.
In thinking more about this, I suppose the ordering here is important because you want the ScrollView to be placed below the LinearLayout in the parent of both of these views. Putting the ScrollView first (and thus having it painted first) would then put it above the other LinearLayout, which isn't what you want.
There are various ways to achieve what you want. For example, you could use a RelativeLayout as the parent of the views, then the ordering is not important.
Alternatively, you could place the ScrollView inside another LinearLayout (and that LinearLayout would be the second child of the overall parent layout). Then when you animate the ScrollView, it would be clipped by its immediate parent, which I believe would give you the effect you're looking for (make sure that setClipChildren() is set to true on this new intermediate LinearLayout, which it is by default, otherwise it won't clip the ScrollView as it animates out of it). Note that this approach would necessitate different animation values, since you are now animating the view outside of its parent (the new LinearLayout).