I'm trying to achieve the following programmatically (rather than declaratively via XML):
<RelativeLayout...>
<TextView ...
android:id="@+id/label1" />
<TextView ...
android:id="@+id/label2"
android:layout_below: "@id/label1" />
</RelativeLayout>
In other words, how do I make the second TextView
appear below the first one, but I want to do it in code:
RelativeLayout layout = new RelativeLayout(this);
TextView label1 = new TextView(this);
TextView label2 = new TextView(this);
...
layout.addView(label1);
layout.addView(label2);
setContentView(layout);
Update:
Thanks, TreeUK. I understand the general direction, but it still doesn't work - "B" overlaps "A". What am I doing wrong?
RelativeLayout layout = new RelativeLayout(this);
TextView tv1 = new TextView(this);
t开发者_开发百科v1.setText("A");
TextView tv2 = new TextView(this);
tv2.setText("B");
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.FILL_PARENT);
lp.addRule(RelativeLayout.RIGHT_OF, tv1.getId());
layout.addView(tv1);
layout.addView(tv2, lp);
From what I've been able to piece together, you have to add the view using LayoutParams.
LinearLayout linearLayout = new LinearLayout(this);
RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
parentView.addView(linearLayout, relativeParams);
All credit to sechastain, to relatively position your items programmatically you have to assign ids to them.
TextView tv1 = new TextView(this);
tv1.setId(1);
TextView tv2 = new TextView(this);
tv2.setId(2);
Then addRule(RelativeLayout.RIGHT_OF, tv1.getId());
Cut the long story short: With relative layout you position elements inside the layout.
create a new RelativeLayout.LayoutParams
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(...)
(whatever... fill parent or wrap content, absolute numbers if you must, or reference to an XML resource)
Add rules: Rules refer to the parent or to other "brothers" in the hierarchy.
lp.addRule(RelativeLayout.BELOW, someOtherView.getId()) lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
Just apply the layout params: The most 'healthy' way to do that is:
parentLayout.addView(myView, lp)
Watch out: Don't change layout from the layout callbacks. It is tempting to do so because this is when views get their actual sizes. However, in that case, unexpected results are expected.
Just spent 4 hours with this problem. Finally realized that you must not use zero as view id. You would think that it is allowed as NO_ID == -1, but things tend to go haywire if you give it to your view...
Android 22 minimal runnable example
Source:
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class Main extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final RelativeLayout relativeLayout = new RelativeLayout(this);
final TextView tv1;
tv1 = new TextView(this);
tv1.setText("tv1");
// Setting an ID is mandatory.
tv1.setId(View.generateViewId());
relativeLayout.addView(tv1);
// tv2.
final TextView tv2;
tv2 = new TextView(this);
tv2.setText("tv2");
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.FILL_PARENT);
lp.addRule(RelativeLayout.BELOW, tv1.getId());
relativeLayout.addView(tv2, lp);
// tv3.
final TextView tv3;
tv3 = new TextView(this);
tv3.setText("tv3");
RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
lp2.addRule(RelativeLayout.BELOW, tv2.getId());
relativeLayout.addView(tv3, lp2);
this.setContentView(relativeLayout);
}
}
Works with the default project generated by android create project ...
. GitHub repository with minimal build code.
call
tv1.setId(1)
after
tv1.setText("A");
Try:
EditText edt = (EditText) findViewById(R.id.YourEditText);
RelativeLayout.LayoutParams lp =
new RelativeLayout.LayoutParams
(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT
);
lp.setMargins(25, 0, 0, 0); // move 25 px to right (increase left margin)
edt.setLayoutParams(lp); // lp.setMargins(left, top, right, bottom);
This approach with ViewGroup.MarginLayoutParams worked for me:
RelativeLayout myLayout = (RelativeLayout) findViewById(R.id.my_layout);
TextView someTextView = ...
int leftMargin = Util.getXPos();
int topMargin = Util.getYPos();
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
new ViewGroup.MarginLayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT));
lp.setMargins(leftMargin, topMargin, 0, 0);
myLayout.addView(someTextView, lp);
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
final RelativeLayout relativeLayout = new RelativeLayout(this);
final TextView tv1 = new TextView(this);
tv1.setText("tv1 is here");
// Setting an ID is mandatory.
tv1.setId(View.generateViewId());
relativeLayout.addView(tv1);
final TextView tv2 = new TextView(this);
tv2.setText("tv2 is here");
// We are defining layout params for tv2 which will be added to its parent relativelayout.
// The type of the LayoutParams depends on the parent type.
RelativeLayout.LayoutParams tv2LayoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
//Also, we want tv2 to appear below tv1, so we are adding rule to tv2LayoutParams.
tv2LayoutParams.addRule(RelativeLayout.BELOW, tv1.getId());
//Now, adding the child view tv2 to relativelayout, and setting tv2LayoutParams to be set on view tv2.
relativeLayout.addView(tv2);
tv2.setLayoutParams(tv2LayoutParams);
//Or we can combined the above two steps in one line of code
//relativeLayout.addView(tv2, tv2LayoutParams);
this.setContentView(relativeLayout);
}
}
If you really want to layout manually, i'd suggest not to use a standard layout at all. Do it all on your own, here a kotlin example:
class ProgrammaticalLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ViewGroup(context, attrs, defStyleAttr) {
private val firstTextView = TextView(context).apply {
test = "First Text"
}
private val secondTextView = TextView(context).apply {
text = "Second Text"
}
init {
addView(firstTextView)
addView(secondTextView)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
// center the views verticaly and horizontaly
val firstTextLeft = (measuredWidth - firstTextView.measuredWidth) / 2
val firstTextTop = (measuredHeight - (firstTextView.measuredHeight + secondTextView.measuredHeight)) / 2
firstTextView.layout(firstTextLeft,firstTextTop, firstTextLeft + firstTextView.measuredWidth,firstTextTop + firstTextView.measuredHeight)
val secondTextLeft = (measuredWidth - secondTextView.measuredWidth) / 2
val secondTextTop = firstTextView.bottom
secondTextView.layout(secondTextLeft,secondTextTop, secondTextLeft + secondTextView.measuredWidth,secondTextTop + secondTextView.measuredHeight)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// just assume we`re getting measured exactly by the parent
val measuredWidth = MeasureSpec.getSize(widthMeasureSpec)
val measuredHeight = MeasureSpec.getSize(heightMeasureSpec)
firstTextView.measures(MeasureSpec.makeMeasureSpec(meeasuredWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
secondTextView.measures(MeasureSpec.makeMeasureSpec(meeasuredWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
setMeasuredDimension(measuredWidth, measuredHeight)
}
}
This might give you an idea how this could work
精彩评论