Why do layout params work in Android Programming? - java

First off my background: I'm new to Java coming over from Ruby. If that helps.
I'm confused about how layout params work. I'm following a basic Hello World introduction to creating an Android App. Step 1, extend the Activity class, and the onCreate() method to access the XML layout. Ok I get that.
Then I create a layout (say a RelativeLayout) in the Main.XML. So this is making use of the RelativeLayout class which extends the ViewGroup class, ok so far. Then lets say I create a button inside this. This is where my question starts. If I look at the example I am following I see attributes being assigned to the button that belong to the RelativeLayout class (i.e: android:layout_alignParentRight="true"). These seem to be the layout params. But why does this work? The button class seems to inherit from the View class. Why can a button object accept attributes for the RelativeLayout object? Maybe my Ruby programming is confusing me..
Thanks!
Update: For posterity sake: thank you to Slothsberry for pointing out the XML Layouts link, which seems to describe the answer clearly in 2 sections the section on "Attributes" and on "Layout Paramters". The attributes section reads:
Every View and ViewGroup object supports their own variety of XML
attributes. Some attributes are specific to a View object (for
example, TextView supports the textSize attribute), but these
attributes are also inherited by any View objects that may extend this
class. Some are common to all View objects, because they are inherited
from the root View class (like the id attribute). And, other
attributes are considered "layout parameters," which are attributes
that describe certain layout orientations of the View object, as
defined by that object's parent ViewGroup object.
The layout parameters section though is perhaps the section that really answers this question. Where it states:
Every ViewGroup class implements a nested class that extends
ViewGroup.LayoutParams. This subclass contains property types that
define the size and position for each child view, as appropriate for
the view group. As you can see in figure 1, the parent view group
defines layout parameters for each child view (including the child
view group).
They give a nice diagram as well. It seems that a beginning programmer needs to recognize that while Java classes are referenced, the XML acts more like a CSS sheet and that attributes are first computed in a nested fashion before being computed and carried over to their Java class counterparts. That's my current understanding anyways :)

Layout parameters do not strictly mirror object inheritence (as you have noticed). The reason is that there are two parts of layout: configuring a view, and parametrizing a view's parent using that view as an argument.
So parameters like android:layout_below will be ignored if the parent layout is not a RelativeLayout. It might make sense from an OOP perspective to put that parameter in the RelativeLayout object. But that is how you would do it in the java code.
In the XML code, it takes the approach that the information about the child is contained in the child. layout parameters that require a parent that is not present will be ignored when the layout is inflated. Its a nice system android uses to make the XML more readable and portable. And it is not strictly referring to the class package structure, but rather the intuitive way humans think about placing things in a layout.

All layout elements in android inherit from View, although many indirectly.
The generic View class has properties (attributes) appropriate for ANY visible layout element. for the root layout, some properties such as Layout Gravity, Layout dimensions, etc. are set by the system (in most cases I believe).
If my root layout is some linear layout, Android will allow me to have a relative layout be a child in the root. Android will let me set various layout properties on the nested element, in order to control how it renders. This works the same for Button, and any other Android layout.
If you don't care about a particular property, don't set it. They are present to allow you control over the screens of your app. Look into XML Layouts or Hello Views to get you started on the details.

you are a little bit confused, that layout param doesn't own a particular XML object . If you put it in one child XML XXXView or XXXLAyout , it will understand that its Right side must be in the same place than parent right.
Then if you don't create the layout params for that child , the child would try to inherit ones of its parent's.

Layout
Layout is a two pass process: a measure pass and a layout pass. The measuring pass is implemented in measure(int, int) and is a top-down traversal of the view tree. Each view pushes dimension specifications down the tree during the recursion. At the end of the measure pass, every view has stored its measurements. The second pass happens in layout(int, int, int, int) and is also top-down. During this pass each parent is responsible for positioning all of its children using the sizes computed in the measure pass.
When a view's measure() method returns, its getMeasuredWidth() and getMeasuredHeight() values must be set, along with those for all of that view's descendants. A view's measured width and measured height values must respect the constraints imposed by the view's parents. This guarantees that at the end of the measure pass, all parents accept all of their children's measurements. A parent view may call measure() more than once on its children. For example, the parent may measure each child once with unspecified dimensions to find out how big they want to be, then call measure() on them again with actual numbers if the sum of all the children's unconstrained sizes is too big or too small.
The measure pass uses two classes to communicate dimensions. The View.MeasureSpec class is used by views to tell their parents how they want to be measured and positioned. The base LayoutParams class just describes how big the view wants to be for both width and height. For each dimension, it can specify one of:
an exact number
MATCH_PARENT, which means the view wants to be as big as its parent
(minus padding)
WRAP_CONTENT, which means that the view wants to be just big enough
to enclose its content (plus padding).
There are subclasses of LayoutParams for different subclasses of ViewGroup. For example, AbsoluteLayout has its own subclass of LayoutParams which adds an X and Y value.
MeasureSpecs are used to push requirements down the tree from parent to child. A MeasureSpec can be in one of three modes:
UNSPECIFIED: This is used by a parent to determine the desired
dimension of a child view. For example, a LinearLayout may call
measure() on its child with the height set to UNSPECIFIED and a width
of EXACTLY 240 to find out how tall the child view wants to be given
a width of 240 pixels.
EXACTLY: This is used by the parent to impose an exact size on the
child. The child must use this size, and guarantee that all of its
descendants will fit within this size.
AT_MOST: This is used by the parent to impose a maximum size on the
child. The child must guarantee that it and all of its descendants
will fit within this size.
To initiate a layout, call requestLayout(). This method is typically called by a view on itself when it believes that is can no longer fit within its current bounds.

Related

Building views dynamically and make them reusable as much as possible

Let's assume we have a fragment and somewhere along its contents we have a part that is supposed to display X views.
We don't know the X number since it is not fixed.
Each of the X views can completely different. E.g.
Separator
TextView
Separator
Linear View with children
Separator
TextView
Separator etc
you get the idea.
So I was thinking
1) should this be build as some kind of custom component? How would I pass the data?
2) I originally thought this should be an inner fragment but then I thought that I don't care about the activity lifecycle about this.
3) Another approach is to just have a small function inside my fragment to add these programmatically but then how could I make this reusable?
What is the best strategy on these kind of designs?

How do I use different card layouts for varying content?

Displaying a certain layout for a card, when using CardView, is simple enough.
I've created an XML layout for my card, and instantiate that XML layout to the View using LayoutInflater.
More specifically:
View largeCardView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_view, viewGroup, false);
Which is called in the onCreate() method of my RecyclerAdapter. The onCreate method then returns this largeCardVew. This works perfectly and the cards are displayed as they are laid out in the XML file.
But what if I want to display different cards for different content? Is it possible to show different cards for different things, but have them all in the same RecyclerView?
For example, if I know before hand that there will be a string passed into a TextView in the card, and this string can only have two values "Large" and "Small", how can I use one type of XML file to use with the "Large" text, and another XML file to use with the "Small" text?
Here is an example (taken from Google Play):
As you can see in the picture, there are two different card types, with different layouts (ignore the orange button). Is there a way to achieve a similar thing, and have the cards in the same RecyclerView?
It's similar to how you would create a single card layout, but instead of having a single .xml and a single ViewHolder, each layout needs to have its own .xml file defining it, and a ViewHolder class.
The method which was found to be very important, is the getItemViewType(int position) method in the RecyclerView.Adapter class. By default, this method returns 0, assuming a single layout for the viewType being used (in this case, a cardView). Since more than a single type is needed, I had to define them.
I then created an array of integers (called cardViewTypes), defining which layouts get used for which type of data. Since you should already have some sort of dataset for the content of the cards (whether it's a List or a HashMap), it's important to make sure that the data matches up with the view types.
In my case, I wanted to have 4 different card layouts, so I defined each in their own .xml file and gave them unique names. Then, in my RecyclerView.Adapter class, I created a separate class which extended ViewHolder for each layout. I made sure that each integer in my viewType array matched up with my data and called getItemViewType.
I then check the view type in the onCreateViewHolder method of the RecyclerView.Adapter, and inflate the corresponding view, which is then bound in onBindViewHolder(ViewHolder, int).
And the result is differing card layouts for different content.

Why don't we use R.id instead of R.layout while inflating layout?

View v=inflater.inflate(R.layout.fragment_b,container,false);
View v = findViewById(R.id.fragB);
i want to know when to use
R.id.fragB
and when to use
R.layout.fragment_b.
Can't we use
R.id.fragB
in place of
R.layout.fragment_b
in first statement.
All references under R.layout refer to your layout files themselves. For example, if you define the layout for your fragment in fragment_b.xml, then R.layout.fragment_b is a way for the framework to reference that file.
References under R.id are identifiers. They could be identifiers for views in a layout, or they could be just generic identifiers that you create for other purposes.
Your layout XML might start with a root View that has an ID, but it also might not. Your layout XML can also contain views with many different IDs. Thus the two are not interchangeable.

GUI building trouble

I have a GUI setup that looks like this:
It displays a document and enables user to edit it by adding/removing JTextAreas and text in them.
I have a problem when trying to open the document. Document itself is made up of layers of JPanels on top of each other. There are never more than 6 layers of JPanels at a single point in the GUI.
Since I don't know what the height of the JTextArea will be, I have to make it relative to the parent (in other words, not specify it).
Because the layout manager (MigLayout) doesn't know the exact size of the component (it's relative to the parent), it first asks its parent for the parent's size. That size is again unknown and the parent's parent is asked and so on (until a level 1 or 2 JPanel). In the end it has to ask a lot of components for their size (a method checkParent(Container) within MigLayout was called over 100 000 times just for the example above).
What I need to do is set the height of the JTextArea (or the JPanel that it resides in) only when the document is being opened, so the layout manager doesn't have to ask JTextAreas parent, causing a recursive hell.
After the document is opened I have to reset the height to default (so the height adjusts when the text is added/removed).
I have absolutley no idea how to do this, or if this is the way to go, I'm only sure that the thing I described above is the problem.
Several notes:
this is not a MigLayout bug, I have been to the forums(link below)
check this out (the last post)
I can't put a JScrollPane into the JTextArea as its task isn't only to hold the information, but to display exactly how much room it takes
I can easily get the heights of the JTextAreas when saving the document, thus having them at my disposal when opening it again
EDIT:
The document referenced in this question is not "the document" as thingy used in JTextAreas and similar, but "a document" as in custom class in my program (which I didn't mention because it is irrelevant, unless it's understood as "the document").
Because your Document models a hierarchical structure, you need a tree. Because your interface requires both a control and a view for each node, you need a table. Outline, seen here, might be a suitable choice. Your DocumentTreeModel would hold a reference to the document's root Element, just as the example's FileTreeModel holds a reference to the root File. Your RowModel would include a JLabel, a JCheckBox and a JTextArea.

How to escape the duplicity in Android?

My experience and good books had taught me that repeating of the same thing in code is bad. But when I use Android views, I often repeat the same thing even 4 times:
twice in XML:
<LinearLayout -1
android:id="#+id/lila"
...
>
...
</LineraLayout -2
and twice in code:
3 4
LinearLayout ll=(LinearLayout) findViewById(R.id.lila);
Of course, when I have to change the type of the view, I have to do it in four places, what causes errors. In the XML I could use refactoring to change simultaneously the class of layout or even of a view or widget. But that doesn't help much, because the most dangerous disrepancy, not catched by the compiler, can appear between code and XML. And later I have to waste time and look for the source of a runtime error.
Are there other possibilities to address views from layouts? Not so dangerous?
If not, do you know some trick to change or at least check all these places simultaneously?
Thank you.
Something that could help you (depending on case, of what you want to do for example with your LinearLayout) is declaring your Views as generic as possible. Here, in your example you could keep your LinearLayout as, ViewGroup if you do not need any LinearLayout specific methods. So, if later you decided to change that layout in xml, in RelativeLayout or FrameLayout, etc, you will not get any cast problems.
You could take the same approach for Buttons, or other Views, depending on what methods you use on them. For example if you just set an onClickListener on your Button, you can keep a refrence to it just to an View, (ex. View button=findViewById(R.id.button);), and later you could change it in a ImageView maybe, and still don't have any cast problems, since they both are extended from View.
So the solution in big lines : use a class at the highest level as you can.
for xml layout reusing, you can use include and merge layouts.
http://developer.android.com/resources/articles/layout-tricks-merge.html
As for Activity, you must use super class references as far as possible, because it provides you a flexibility to change specific object class.
Like instead of using we can
ViewGroup ll=(ViewGroup ) findViewById(R.id.lila);
now this can address relative layout, linear layout, table layout, etc.

Categories

Resources