Android ViewGroup has the following methods to add (child) Views:
I know we can easily define horizontal/vertical orientation to LinearLayout in xml/programatically and add child views, eg.
Similarly with RelativeLayout, we can use ViewGroup's addView and RelativeLayout.LayoutParams's method:
I am also aware that we can use LinearLayout as a child inside ConstraintLayout and play with it.
Is there any solid and recommended way to add child views to ConstraintLayout dynamically?
UPDATE:
Let me give a not-so-simple example which I what I want to achieve.
Suppose you have a View1, View2 and View3. All V1, V2 and V3 are aligned vertically one below another and are complex views consisting of multiple TextViews and ImageViews. Based on user action and what server sends information, I need to add multiple V1 and V2 (can be 1 pair of V1-V2 and can be 3 pairs of V1-V2)between original V2 and V3. If I am using ConstraintLayout, would it be best if I add multiple constraints programatically when I can easily use LinearLayout with vertical orientation?
Now, in terms of efficiency, performance and less-and-beautiful-code, is ConstraintLayout best for this requirement as compared to Linear Layout?
Views can be added to ConstraintLayout using addView() in the same way as you would with LinearLayout. The difference is that with ConstraintLayout the added views must be constrained. To constrain a view programmatically, use ConstraintSet.
This class allows you to define programmatically a set of constraints to be used with ConstraintLayout. It lets you create and save constraints, and apply them to an existing ConstraintLayout.
Here is a brief example:
activity_main
Define two TextViews. Center them horizontally and positioned at the top.
<android.support.constraint.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/topView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Top View"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/bottomView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Bottom View"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/topView" />
</android.support.constraint.ConstraintLayout>
This is what you will see when a new TextView ("Middle View") is added to this layout without setting constraints. Notice that the new view defaults to position (0,0).
Let's say that we want the generated middle view to be placed between the top view and the bottom view centered horizontally in the window like this:
Here is the code that will produce this result:
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Define the new TextView and add it to the ConstraintLayout. Without constraints,
// this view will be positioned at (0,0).
TextView middleView = new TextView(this);
middleView.setId(View.generateViewId());
middleView.setText("Middle View");
middleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20.0f);
ConstraintLayout layout = findViewById(R.id.constraintLayout);
ConstraintLayout.LayoutParams lp =
new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
layout.addView(middleView, lp);
// Move the new view into place by applying constraints.
ConstraintSet set = new ConstraintSet();
// Get existing constraints. This will be the base for modification.
set.clone(layout);
int topMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
16, getResources().getDisplayMetrics());
// Set up the connections for the new view. Constrain its top to the bottom of the top view.
set.connect(middleView.getId(), ConstraintSet.TOP, R.id.topView, ConstraintSet.BOTTOM, topMargin);
// Constrain the top of the bottom view to the bottom of the new view. This will replace
// the constraint from the bottom view to the bottom of the top view.
set.connect(R.id.bottomView, ConstraintSet.TOP, middleView.getId(), ConstraintSet.BOTTOM, topMargin);
// Since views must be constrained vertically and horizontally, establish the horizontal
// constaints such that the new view is centered.
set.centerHorizontally(middleView.getId(),ConstraintSet.PARENT_ID);
// Finally, apply our good work to the layout.
set.applyTo(layout);
}
Related
This is the layout I want to design:
As you can see from the image, I want the linear layout to be constrained to both Item A and Item B and I want it aligned to left. I couldn't find any way to implement this inside a Constraint Layout. When I use this:
<LinearLayout
android:id="#+id/linear_layout"
android:layout_width="wrap_content"
android:layout_height="#dimen/ui_size_xs">
linear layout moves to the middle.
When I use this:
<LinearLayout
android:id="#+id/linear_layout"
android:layout_width="0dp"
android:layout_height="#dimen/ui_size_xs">
the linear layout occupies the whole area between Items A and B.
I want the linear layout to be wrap-content but should also align to the left like in the image. The only solution I could find is to create a new layout as parent of this linear layout and give 0dp to new parent linear layout and wrap-conent to this layout child layout. I don't want to do that. Is there any way to achieve this without creating any extra layout?
As mentioned in offical docs,
The default when encountering such opposite constraints is to center the widget; but you can tweak the positioning to favor one side over another using the bias attributes:
layout_constraintHorizontal_bias
layout_constraintVertical_bias
Try the following and see if it works
<LinearLayout
android:id="#+id/linear_layout"
android:layout_width="wrap_content"
android:layout_height="#dimen/ui_size_xs"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toRightOf="item1id"
app:layout_constraintRight_toLeft="item2id">
// try different horizontal bias values to meet your need
I am developing an Android app where I have two different views. Both views are actually canvas drawings (drawn programmatically) with a bunch of text views and buttons on top. The positions of these buttons and text views need to be changed depending on the view selection. I created a relative layout with my drawing and the buttons and text views. I use one of the text views as an anchor for the others and programmatically change its position.
Here is a part of the xml:
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:id="#+id/tvAnchor"
android:layout_above="#id/btnCtrl"
android:layout_marginBottom="130dp"
android:textSize="15sp"
android:textColor="#color/lightGrey"
android:gravity="center"
android:text="#string/Select1"
android:background="#drawable/controls_border_not_active"
android:clickable="true"/>
<TextView
android:layout_width="50dp"
android:layout_height="wrap_content"
android:id="#+id/tvAlign"
android:layout_alignBottom="#id/tvAnchor"
android:layout_toStartOf="#id/tvAnchor"
android:layout_marginEnd="2dp"
android:textSize="15sp"
android:textColor="#color/lightGrey"
android:gravity="center"
android:text="#string/Select2"
android:background="#drawable/controls_border_not_active"
android:clickable="true"/>
In my main I have the following code to change the position of the text views accordingly:
private void PositionControls(){
RelativeLayout.LayoutParams lopAnchor = (RelativeLayout.LayoutParams) tvAnchor.getLayoutParams();
if (isView1Selected){
lopAnchor.resolveLayoutDirection(relativeLayout.getLayoutDirection());
lopAnchor.removeRule(RelativeLayout.ALIGN_START);
lopAnchor.setMargins(0,0,0,370);
lopAnchor.addRule(RelativeLayout.CENTER_HORIZONTAL);
}else{
lopAnchor.setMargins(0,0,0,300);
lopAnchor.removeRule(RelativeLayout.CENTER_HORIZONTAL);
lopAnchor.addRule(RelativeLayout.ALIGN_START,swDoIt.getId());
}
tvAnchor.setLayoutParams(lopAnchor);
}
Basically, in View1, the tvAnchor should be centered horizontally and in the other view it should be positioned off center - in the middle of 'center-to-right screen edge'. In order to do that when the second view is selected I remove the CENTER_HORIZONTAL rule and add a rule 'ALIGN_START' with a switch that is already positioned in the desired X coordinate. It works just fine. The problem occurs when View 1 is selected again and I switch from View 2 to View 1. In this situation I remove the ALIGN_START rule and add the CENTER_HORIZONTAL one. The thing is that tvAnchor remains aligned to the switch as if the ALIGN_START rule was not removed. I tried to get the direction of the layout, used requestLayout and other things that could think of, but nothing seems to remove that rule.
Did you check that there isn't coming some mystery values enabled??
I had case where is moved clockwidget place in layout
I did add ALIGN_PARENT_END and removed CENTER_IN_PARENT but when i set layout back i could not get clock widget centered.
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) mTextClockWidget.getLayoutParams();
p.addRule(RelativeLayout.ALIGN_PARENT_END, 1);
p.removeRule(RelativeLayout.CENTER_IN_PARENT);
mTextClockWidget.setLayoutParams(p);
Then noticed that from somewhere my layout params got ALIGN_PARENT_RIGHT enabled and when tried to remove ALIGN_PARENT_END and enable CENTER_IN_PARENT it didn't work. So i had to also add remove for ALIGN_PARENT_RIGHT to get it work.
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) mTextClockWidget.getLayoutParams();
p.removeRule(RelativeLayout.ALIGN_PARENT_END);
p.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
p.addRule(RelativeLayout.CENTER_IN_PARENT, 1);
mTextClockWidget.setLayoutParams(p);
I add a view to the left of an existing view which is centred in parent. I would like the layout to update and centre both the view I added dynamically to the left of the existing view and the existing view.
I have previously looked at this question and tried to get the view to wrap_content after I have added the view which did not work using:
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.LEFT_OF, timePicker.getId());
params.addRule(RelativeLayout.CENTER_VERTICAL);
picker.setLayoutParams(params);
lytBottom.addView(picker);
RelativeLayout.LayoutParams existingParams = (RelativeLayout.LayoutParams) lytBottom.getLayoutParams();
existingParams.width = RelativeLayout.LayoutParams.WRAP_CONTENT;
and tried changing the visibility as per the top suggested comment. All the above did was wrap_content around the view originally there and completely ignores the dynamically added view.
Is there another way I could be approaching this to achieve the centering of both the new view and the old one while keeping the new view to the left of the old view?
The xml:
<android.support.percent.PercentRelativeLayout
android:id="#+id/lytBottom"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_below="#+id/lytTop"
android:layout_centerHorizontal="true"
app:layout_heightPercent="30%">
<TimePicker
android:id="#+id/timePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:timePickerMode="spinner"/>
</android.support.percent.PercentRelativeLayout>
I'm trying to create a list of clickable image buttons w/ text that fit inside a HorizontalScrollView. The images/content will be set programmatically. The best way to do this seemed to be a LinearLayout, that then contained a series of RelativeLayouts that contained the views to display the relevant content. However, I'm having trouble getting space between each RelativeLayout. Even though I've set margins in xml and programmatically they seem to be ignored and the RelativeLayout objects are squished together.
Some code:
<RelativeLayout
android:id="#+id/details_image_button"
android:layout_width="75dp"
android:layout_height="100dp"
android:layout_marginLeft="10dp"
android:background="#00ff78">
<ImageView
android:id="#+id/loadable_image_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<TextView
android:id="#+id/details_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#b083ef"
android:text="PH - Info about title"
android:layout_alignParentBottom="true"
/>
//Code below is looped through several times
RelativeLayout imageButtonLayout = (RelativeLayout) inflater.inflate(R.layout.details_image_button, null);
RelativeLayout.LayoutParams imageButtonLayoutParams = new RelativeLayout.LayoutParams(100, 100);
imageButtonLayoutParams.setMargins(10, 10, 10, 10);
imageButtonLayout.setLayoutParams(imageButtonLayoutParams);
The current result that I am getting is a solid green (background color of the RelativeLayout) rather than the expected result of a group of RelativeLayouts with a space between each. How can I best get a margin or buffer between each RelativeLayout?
If your RelativeLayout is inside a LinearLayout, the LayoutParams you need to use would be LinearLayout.LayoutParams:
RelativeLayout imageButtonLayout = (RelativeLayout)
inflater.inflate(R.layout.details_image_button, null);
LinearLayout.LayoutParams imageButtonLayoutParams = new
LinearLayout.LayoutParams(100, 100);
imageButtonLayoutParams.setMargins(10, 10, 10, 10);
imageButtonLayout.setLayoutParams(imageButtonLayoutParams);
LayoutParams come from the parent, not the child.
I need to implement the layout as in the picture. Parent and Sibling are in a vertical LinearLayout. So I need to make a child view to overlap it's parent. Can I do that in android?
If:
sibling is a sibling of parent
parent is a ViewGroup
and you really want child to be a child of parent
then maybe you could consider using android:clipChildren set to false on parent.
I was actually just looking at an example of a FrameLayout that had a TextView overlaid on top of an ImageView. So, there are obviously multiple ways to get it done. Your next question might be which one is best ... to that I have no idea, but here's a guy that might:
http://www.curious-creature.org/2009/03/01/android-layout-tricks-3-optimize-part-1/
Just contain them all within a RelativeLayout, and remember the draw order is top to bottom, so put the top most view on the bottom of the XML definition.
If you use a RelativeLayout you should have no problem achieving this effect. By default it will stack all of its children on top of each other in the top left corner if you don't supply them with android:layout parameters. So it will definitely support overlapping children. You'd just have to figure out what the best way to tell it where the child should go on the screen relative to something else.
There are at least two layouts that can do that. AbsoluteLayout and RelativeLayout. I suggest that you put your views in a RelativeLayout and add them with LayoutParams that specify their offset form the top and left of the parent:
RelativeLayout.LayoutParams rlp;
label = new TextView(ctx);
label.setBackgroundColor(0x00000000);
label.setTextColor(0xFF7ea6cf);
label.setTextSize(13);
label.setGravity(Gravity.LEFT);
label.setText("Examples:\n- Fentanyl\n- Dilaudid 2 mg PO q 4 hours prn moderate pain");
rlp = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,100);
rlp.topMargin=189;
rlp.leftMargin=30;
rlp.rightMargin=30;
rlParent.addView(label,rlp);
In my case, I have to set android:clipCildren to be false on the parent of parent.
i.e.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:id="#+id/parent1">
<FrameLayout
android:id="#+id/parent2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="64dp"
android:background="#android:color/holo_blue_bright">
<View
android:id="#+id/This_is_the_view_I_want_to_overlap_parent2"
android:layout_width="160dp"
android:layout_height="80dp"
android:layout_gravity="top|start"
android:layout_marginTop="-40dp"
android:background="#000000" />
</FrameLayout>
</FrameLayout>