From my vague understanding of layout managers I think that nesting layouts inside layouts can be inefficient (is this true for android too?)
I am trying to do this layout but have having to next layouts. Is there a better layout manager I could use so that things are not nested? All of the components are currently fragments so it is a case of laying out the fragments rather than the individual components.
Thanks
I don't believe there is a way to get that look without nested layouts. If so, it's because you're doing a lot of graphics that probably isn't necessary (not knowing your other requirements). And by "a lot of graphics" I mean from your image, if you want dynamic content in each box, then you must use nested layouts unless you have one layout and then re-draw the cent portion of the screen to give the effect of nested layouts.
Unless you have a performance problem, you don't need to worry about the efficiency with a low number of layouts (i.e. less than 100 or so). I have several apps with several layers of nested layouts (up to 10 layers of nesting) and no measurable difference in performance.
If you have a ScrollView or ListView with hundreds of children, then you might be concerned with nested layouts. I have worked with a few of these, and it can make a difference. For example, I had a ListView with 5,000 or so items, each had about 5 layouts. When I dropped it to 3, I noticed a difference on the backend. Neither were noticeable to the user. (And no, I could not recycle the view containers efficiently since each item essentially needed it's own layout - partial recycling was possible.)
Though it's a bit complex to set up, you can use GridLayout to achieve that. Assuming the top and bottom views have a fixed height, that would be something like:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:useDefaultMargins="true">
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="120dp"
android:layout_row="0"
android:layout_column="0"
android:layout_columnSpan="2"
android:background="#ffff070c"
android:layout_gravity="top"></FrameLayout>
<FrameLayout
android:layout_width="200dp"
android:layout_row="1"
android:layout_column="0"
android:background="#ff12ff1a"
android:layout_rowSpan="2"
android:layout_gravity="fill_vertical"></FrameLayout>
<FrameLayout
android:layout_height="64dp"
android:layout_row="1"
android:layout_column="1"
android:background="#ff0906ff"
android:layout_gravity="fill_horizontal"></FrameLayout>
<FrameLayout
android:layout_row="2"
android:layout_column="1"
android:background="#ff737373"
android:layout_gravity="fill"></FrameLayout>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_row="3"
android:layout_column="0"
android:layout_columnSpan="2"
android:background="#ffbebebe"></FrameLayout>
</GridLayout>
which produces this:
Related
This might be a very beginner question, but I'm yet unable to find myself around the android jungle.
I've already got a RecyclerView working to show a list of items (with data binding and Room database and DiffUtil.ItemCallback and all).
I'd like to put 2 links after the list: "missing something?" and "add new entry" that will lead to other fragments.
What I have:
When I put 2 buttons (I don't know yet how to put links, but this is not the point of this question) after the RecyclerView, all in a LinearLayout, they stay fixed near the screen bottom. I mean, the RecyclerView is scrollable by itself, scrolling "beneath" the two buttons, the entire LinearLayout expanding to fill the screen (match_parent).
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Missing something?"
android:onClick="#{...}" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Add new item"
android:onClick="#{...}" />
</LinearLayout>
What I want
I'd like the 2 buttons to scroll along with the list, so that they are always positioned after the last item (think as if they were items themselves, albeit an heterogeneous list with different types/RecyclerView.ViewHolder).
For a big enough list the buttons will be initially off screen; to be scrolled in if the user happen to scroll to the bottom of the list.
What I tried
I tried with ScrollView around the LinearLayout, and it works, but everywhere everybody say that one should never put a RecyclerView inside a ScrollView (maybe because it is scrollable itself).
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/routines_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<!-- buttons -->
</LinearLayout>
</ScrollView>
Being really a beginner in android programming, I'd like to know how usually this kind of layout should be done. Only main directions will be enough for me.
NB. I don't know if I really need a RecyclerView because I don't expect this list to be lengthy. Maybe usually something around 4 to 8 items, possibly 10. But I really don't expect it to be much bigger than that. For many users the two links will even be visible all the time (i.e. no scroll at all).
RecyclerView is always the most efficient to show a list especially if you are getting the data from a database or an API. Don't put your recyclerview in a scrollview. You can add two items to the bottom of the list as your links and program your recyclerview to exhibit different properties for last two items. That is the best way I can think of. Good Luck!
Also, Recyclerview is very difficult to work with when you are working with complex data. With small lists such as in your case, it can seem inconvenient to create a whole adapter class and do everything you are supposed to do. When you have grasped the concepts on xml android and have plenty experience with that. You can move to jetpack compose and lazy column will make your life easy.
I am having more of a conceptual question when it comes to ConstraintLayout. I want to re-use a layout inside my ConstraintLayout. But I ask myself a question that the ConstraintLayout aims at avoiding nesting of layouts and including a layout will decrease the ConstraintLayout performance. What is the best/ good practice to avoid nesting of layouts and at the same time re-use a layout so as to avoid code duplication?
You can use <merge/> tag to eliminate redundant view groups in your view hierarchy when including one layout within another.
Example:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<include layout="#layout/include_layout"/>
</androidx.constraintlayout.widget.ConstraintLayout>
include_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="33dp"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button" />
</merge>
Result:
In this case, without the <merge/> tag, you'll have two ConstraintLayout in the layout hierarchy.
Reference: https://developer.android.com/training/improving-layouts/reusing-layouts
Bottom Line
Don't be afraid of nesting ConstraintLayout as long as your View hierarchy doesn't grow too deep (<10). I'd consider it good practice to reuse and nest your layouts to avoid code duplication whenever it makes sense.
Background
I quote from the Android Developers page:
Additionally, if your app targets Android 7.0 (API level 24), it is likely that you can use a special layout editor to create a ConstraintLayout object instead of RelativeLayout. Doing so allows you to avoid many of the issues this section describes. The ConstraintLayout class offers similar layout control, but with much-improved performance. This class uses its own constraint-solving system to resolve relationships between views in a very different way from standard layouts.
Optimizing Layout Hierarchies from the developers page mentions that
Deep layouts - Layouts with too much nesting are bad for performance. Consider using flatter layouts such as RelativeLayout or GridLayout to improve performance. The default maximum depth is 10
However, ConstraintLayout is not mentioned on this page explicitly and I might add that a depth of 10 nested layouts would almost surely result in a rather cluttered UI.
To add, in my professional experience as a software developer, I've never had performance issues with ConstraintLayout despite the fact that my team does use nested ConstraintLayouts regularly (with limited depth, I might add), most of the time encapsulated in custom View Components. That being sad, these events certainly don't occur excessively, since ConstraintLayout enables one to build UIs without excessive use of nesting.
I have a layout as shown below. It is inflated by code and added to a HorizontalScrollView, sometimes a few hundred times, and causing getting memory issues.
I'm wondering if there's anything that can be done to make it more efficient? Originally I used LinearLayouts, and replacing that with RelativeLayout made a huge difference to the scrolling. Now I'm wondering if it can be further improved?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="156dp"
android:layout_height="254dp"
android:paddingLeft="7dip"
android:paddingRight="7dip">
<FrameLayout android:id="#+id/button_frame"
android:layout_width="156dp"
android:layout_height="218dp">
<ImageView android:id="#+id/button_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/image_bg"
/>
<ImageView android:id="#+id/button_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom|right"
/>
<TextView android:id="#+id/button_select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="#drawable/btn_selector_bg_selected"
/>
<TextView android:id="#+id/button_selected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="#drawable/title_bg_selected"/>
</FrameLayout>
<TextView
android:id="#+id/button_title"
android:layout_width="156dp"
android:layout_height="36dp"
android:textColor="#ffffff"
android:singleLine="true"
android:textSize="13sp"
android:textStyle="bold"
android:gravity="center_vertical|left"
android:ellipsize="end"
android:paddingLeft="30dip"
android:paddingRight="5dip"
android:layout_below="#id/button_frame"
android:background="#drawable/title_bg"/>
</RelativeLayout>
The ImageView button_image is populated using AQuery image caching, so I'm not sure if there's much more I can do to improve the way the image is handled. But any tips on improvements greatly appreciated.
In general, a good way to optimize layouts in Android is to minimize the amount of nesting of containers within each other. The deeper you nest layout containers, the more work the framework must do to measure, layout, and process events for the view hierarchy.
But I think you may be asking the wrong question.
The HorizontalScrollView and VerticalScollView are not intended for the use you're putting them to. They're meant to hold a mostly static layout, and allow it to scroll if necessary depending upon the size of the screen it happens to be running on.
You want a repetitive list of mostly identical Views, that the user can scroll. The correct Android view container to use is ListView, or one of the other descendants of AdapterView.
ListView is careful only to create/inflate the necessary child views to fill the space on screen, and reuse them as the user scrolls. This solves the memory problems you're experiencing. It does that by requiring you to pair it up with an Adapter - an object that wraps the actual data being displayed, and creates on-demand the correct view for a given data item.
Since you're trying to do horizontal scrolling, you might also look at Gallery (now deprecated in Android) or the newer ViewPager class, both of which support horizontal movement through a large list of data.
If you can use a ListView instead of a HorizontalScrollView, you could create an Array Adapter that uses the viewHolder pattern which essentially allows you to re-use views per item in your list view. Take a look at this for more details: http://www.jmanzano.es/blog/?p=166
I'm fairly new to Android development. I'm wondering what are the different ways that are used to design XML layouts. Ive been using the eclipse drag and drop interface and I've seen http://droiddraw.org/ while doing some searching. Just wondering if there are any other possibly better ways out there to design layouts that professions use because I'm having a hard time with the eclipse interface making complex designs?
First of all check out the android developer site user interface page
http://developer.android.com/guide/topics/ui/index.html
There are basically three different ways you can make an android layout:
XML
http://developer.android.com/guide/topics/ui/declaring-layout.html
You can define a static layout using XML. Perhaps a good way to think of it is a sort of shorthand. It is very easy to declare Views and attributes, and the hierarchical format of XML makes it a bit easier to visualize.
This is a typical layout
<?xml version="1.0" encoding="utf-8"?>
<ViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:attribute="value" >
<ViewGroup android:attribute="value" >
<View android:attribute="value" />
</ViewGroup>
<View android:attribute="value" />
</ViewGroup>
Then you use setContentView(R.layout.layout) in your activity and go about your business.
Java
You can do everything you would do in XML, plus add things like listeners, or do other dynamic things that you cannot in XML. Here is how you might declare a typical layout (ViewGroup is abstract so you would have to use a subclass. The same goes for XML)
ViewGroup parent = new ViewGroup(this);
ViewGroup vg1 = new ViewGroup(this);
View v1 = new View(this);
View v2 = new View(this);
parent.addView(vg1);
vg1.addView(v1);
parent.addView(v2);
v1.setOnAwesomeListener(new AwesomeListener() {
onAwesome(View v) {
doDynamicThings();
}
}
setContentView(parent);
Hybrid
This is the case used most often in my opinion. Declare a layout in XML with an id, like android:id="#+id/v1" then load Views from XML into Java
setContentView(R.layout.layout);
View v1 = findViewById(R.id.v1);
// dynamically change v1
How to design a layout using XML
So the lack of GUI designer tools has left you no choice but to dive into coding up your layout by hand. Good news is that once you get the hang of it you should be able to tackle any layout you wish. Let's look at the building blocks
ViewGroup
First off you need to choose a ViewGroup to define the structure of the layout, or section of the layout. Remember that these can be nested, so design top-down and try to classify sections of the layout based on the form you want them to have. There are two main options:
LinearLayout
As the name implies, useful for arranging items in a line. Choose an orientation, horizontal or vertical, and simply add items. They will be added in top to bottom or left to right ordering.
RelativeLayout
Useful for placing an item in a specific location on the screen. So if you want to put a button in the top-left, or a bar across the top, this is your ViewGroup.
Layout Parameters
Used for defining the width, height, weight, and other aspects of a view.
There are two options for width and height: fill_parent (replaced with match_parent in API level 8) and wrap_content. The view can choose to either fill the parent view's width, or take only the space it needs.
There is another useful layout parameter, unique to LinearLayout, called weight. It is useful for letting views share space in ratios, or letting one view take the space left over after other views in the LinearLayout take their share.
Example
Let's try to design the layout for Google Maps. Pretend it is a layout that I have in my head, and I want to implement it. Here is a screenshot
I will try to break this down:
Looking at it, there is a bar across the top and a map underneath it. I believe this could be implemented with either a LinearLayout or a RelativeLayout. However, the buttons in the bottom right and left scream RelativeLayout, so I will go with that.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TODO:BAR
android:id="#+id/bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true">
</TODO:BAR>
<MapView
android:id="#+id/map"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/bar" />
<ImageButton
android:id="#+id/latitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="20dp"
android:layout_marginLeft="20dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical" >
<ImageButton
android:id="#+id/zoom_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageButton
android:id="#+id/zoom_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
Now some explanation. In RelativeLayout you use alignParent[direction] to specify where the view goes. I also wanted some space on the sides, so I used margin[direction] to specify in dp or density-independent pixels. As you can see, wrap_content is used most of the time, so the buttons would acquire the size of the image used on them.
Now everything is defined but the bar at the top. I'm going to break it up into four different Views: The dropdown menu view, the search view, the layers button and the my location button. The way I would like it to work is put the menu at the far left, and the layers and my location buttons on the right, with the search box taking up the remaining space. This sounds like a job for LinearLayout and weight! Here is how I define the bar, which can be inserted into the placeholder above to get the final layout
<LinearLayout
android:id="#+id/bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true" >
<ImageButton
android:id="#+id/dropdown_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="#+id/search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageButton
android:id="#+id/layers"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageButton
android:id="#+id/my_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Setting the width of the search bar to 0dp means let the other views take what they need, then the weight says take the remaining space.
And there you have it. A recreation of the basic layout for the Google Maps app (minus button images and other niceties like custom views), showing how you might use various layouts and XML fairly painlessly. Hopefully this was useful.
The tool chain is a little weak in this area. I don't really care for DroidDraw, and the Eclipse GUI editor is not very good for anything more than simple layouts. It often renders RelativeLayouts incorrectly for example.
Personally I do almost everything directly in XML. You have to understand how all the different Layout classes work to do anything complex anyway. The only real downside to XML is that all of the extra cruft from tags, attributes, etc. makes for a lot of extra stuff to type, but the editor takes care of most of that for you.
I'm trying to layout views in a relative layout on a tablet, much like a bookshelf. There will be so many across, then a new row is created.
My initial state looks like this:
<ScrollView
android:layout_marginTop="150sp"
android:layout_marginLeft="50sp"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:id="#+id/user_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- Add User Avatar -->
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/user_frame" />
<ImageView
android:id="#+id/demo_image"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/add_user"
android:layout_marginLeft="10px" />
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/user_name_background"
android:layout_marginLeft="10px" />
<TextView
android:id="#+id/user_name"
android:layout_width="180px"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center_horizontal"
android:text="Add New User" />
</RelativeLayout>
</RelativeLayout>
</ScrollView>
I'm fetching records from a database, and for each record I need to place a version of the "add user avatar" section as seen above. I'd like to do 5, then wrap to a new row.
My initial idea was to use a layout inflater and add the views programmatically, using RelativeLayout.LayoutParams to set the layout_toRightOf values.
There must be an easier way though. I've seen lots of applications using the "bookshelf" metaphor.
There are a few options I can think of.
Instead of using a relative layout, why not use LinearLayouts with the horizontal attribute?
Using a TableLayout, much like the LinearLayout
Use a ListView with a custom adapter to fill five 'Add User Avatar's' per ListRow
Laying out views programmatically is fairly simple. I do this for all of my activities / views. I love Android's XML layout concept but find that as soon as the views become at all dynamic based on external data (such as your database) then it gets too complex.
I choose what my outermost layout will be and then only instantiate that layout in the XML. Then in my activity I find the outer layout using the normal find by id call and then use a sequence of add(View) method calls to add my dynamically created views or layouts as needed.
You will need to take into account different screen orientations, pixel densities and sizes. Device Independent Pixels will become your friend. For example, to create an image view, you would load the bitmap, figure out how much to resize it (if needed) and then set it as the drawable for a new ImageView instance, that you then add to the layout.
Sounds like your outer layout will be a scroll view with a vertical linear layout inside, that then has a horizontal layout for each "shelf" that then have up to five image views for each "book"