I need to change the size (specifically the height) of a button in an Android app where the button resides in a table and is created programmatically. I have tried about 20 different approaches and failed miserably. If I create a button outside the table, I can change the size without a problem, but as soon as it goes in a table, the height stays fixed even though I can change the width.
I have tried creating and using, LinearLayout params, ViewGroup layout params, TableLayout params, etc. and setting their heights either via that constructor (e.g WRAP_CONTENT) or using setHeight(). I have also tried simply calling button.setHeight(). I have changed settings in my xml manifest for the table as well. I have used pixel values and Dpi values. All of this has failed. Here is the basics of what I have so far (just showing a call to b.setHeight()):
TableRow row = new TableRow(getApplicationContext());
row.setId(counter);
TextView t = new TextView(getApplicationContext());
t.setText("BLH " + counter);
t.setTextColor(Color.BLACK);
//I have also tried table.getContext() in this constructor...
Button b = new Button(getApplicationContext());
b.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v)
{
//blah blah blah
});
//Convert from pixels to Dpi
final float scale = getResources().getDisplayMetrics().density;
int heightDp = (int) (33 * scale + 0.5f);
int widthDp = (int) (60 * scale + 0.5f);
b.setText(R.string.removeButtonText);
b.setTextSize(12);
b.setTag(counter);
b.setHeight(heightDp);
b.setWidth(widthDp);
b.setId(counter);
counter++;
row.addView(t);
row.addView(b);
// add the TableRow to the TableLayout
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
This correctly results in a button being placed in the table, but the height does not change no matter what values I use. In my xml file, here is what the table declaration looks like:
<TableLayout
android:id="#+id/myTable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:stretchColumns="0">
</TableLayout>
I have tried messing with stretchColumns and other settings here; again, to no avail. Does anyone have any idea why I can't change the height of a button added to my table programmatically, but have no problems doing so outside the table? I am sure it is some setting or adjustment that I just haven't found. Any help is appreciated as I am at my wits end. Thanks in advance.
I fixed my problem. Turns out it was an ordering issue. The problem is that I was setting the height before adding the button to the table. This caused the button's LayoutParams to not have any parent as explained here: ViewGroup.getLayoutParams(). Here is what I needed to do (basically, change height after adding the button to the table):
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
//now change the height so the buttons aren't so big...
final float scale = getResources().getDisplayMetrics().density;
int heightDp = (int) (33 * scale + 0.5f);
ViewGroup.LayoutParams params = b.getLayoutParams();
params.height = heightDp;
b.setLayoutParams(params);
b.requestLayout();
table.requestLayout();
The last two lines were suggested above by Lawrence. The table and button seemed to update without these. However, it seemed to me to be a good idea to update just in case.
Try this
int heightDp = (int) (33 * scale + 0.5f);
int widthDp = (int) (60 * scale + 0.5f);
ViewGroup.LayoutParams bLp = new ViewGroup.LayoutParams(widthDp,heightDp);
b.setLayoutParams(bLp);
instead of
b.setHeight(heightDp);
b.setWidth(widthDp);
I think you need to call View.requestLayout, this is not done automatically every time you add/remove child views.
Related
When I set padding on a button using XML it works without problems.
XML for the button:
<Button
android:id="#+id/comment_video"
android:layout_below="#+id/input_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/comment_video"
android:textColor="#android:color/white"
android:backgroundTint="#color/colorAccent"
android:layout_alignParentEnd="true"
android:textSize="14sp"
android:padding="10dp"
android:layout_marginTop="0dp"
android:layout_marginEnd="10dp"
android:visibility="gone"/>
Screenshot of button:
Then when I do it programmatically it's not working.
Code for the button:
// Add reply button
final Button replyButton = new Button(mContext);
replyButton.setId(170000 + i2);
replyButton.setText("REPLY");
replyButton.setTextColor(Color.parseColor("#FFFFFF"));
replyButton.setBackgroundColor(Color.parseColor("#C70000"));
replyButton.setPadding(dp10_in_px, dp10_in_px, dp10_in_px, dp10_in_px);
replyButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
replyButton.setVisibility(View.GONE);
RelativeLayout.LayoutParams lp8 = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
lp8.addRule(RelativeLayout.BELOW, textInputLayout.getId());
lp8.addRule(RelativeLayout.ALIGN_PARENT_END, relativeLayout3.getId());
lp8.setMargins(0, dp4_in_px, dp10_in_px, dp10_in_px);
replyButton.setLayoutParams(lp8);
Screenshot of button:
I set thousands of views dynamically based on YouTube data and must do it programmatically, at least as far as I know.
The formula used for variables in setPadding and setMargin are as follows:
int dp4 = 4; // 4 dps
final float scale = getResources().getDisplayMetrics().density;
int dp4_in_px = (int) (dp4 * scale + 0.5f);
int dp10 = 10; // 10 dps
int dp10_in_px = (int) (dp10 * scale + 0.5f);
Any help is appreciated.
Update 1 - Screenshot of layout bounds
Update 2 - Sharp corners
Another small detail that might be a clue. The corners on the programmatically made button is sharp. Normally they're a bit rounded.
OK, with help from war_Hero in comments I've found the solution.
setBackgroundColor() don't work. It sets the entire background, including the padding, to the same color.
This:
replyButton.setBackgroundColor(Color.parseColor("#C70000"));
Should be replaced with something like this:
replyButton.getBackground().setColorFilter(ContextCompat.getColor(mContext, R.color.colorAccent), PorterDuff.Mode.MULTIPLY);
Try this:
replyButton.setPadding(dp10_in_px, dp10_in_px, dp10_in_px, dp10_in_px);
you should replace line something like this
replyButton.setPadding(0,padding,0,0);
I have the following structure within a view hierarchy
ScrollView
LinearLayout (horizontal)
- RelativeLayout (X)
-LinearLayout
-CustomView
- RelativeLayout
- RelativeLayout
- RelativeLayout
- RelativeLayout
- RelativeLayout
Here is a sample of it in the xml
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:id="#+id/linLayoutWrapper"
android:layout_width="match_parent"
android:layout_height="1080dp"
android:background="#color/white">
<!--- note this include is a <RelativeLayout> -->
<include
android:id="#+id/dg_axis"
layout="#layout/day_grid_axis"></include>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#color/grayBorder"></View>
<--- start of repetitive substructure which his higlighted in screen shot -->
<RelativeLayout
android:id="#+id/sundayColumn"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="1080dp">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
</LinearLayout>
<com.mynursesstation.views.DayView
android:id="#+id/sundayDayView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#color/grayBorder"></View>
<!--- repeat above sub structure 6 more times --->
</LinearLayout>
</ScrollView>
where the inner structure (X) is repeated for all other RelativeLayout siblings. The Linear Layout, RelativeLayouts
NOTE: (X) is shown highlighted in its unusual behavior in Red, where as its parent is the entire screen width and is delimited with the grey vertical line above it.
The RelativeLayouts would all be flush to the top of the Linear Layout if all of them were the same size (That is my experimental opinion which has yet to be proved). If one is bigger than the rest, then they by default align to the bottom. My question is, how can These children of the RelativeLayout's exceed their parent when they are defined by the parent? Could it be that I am programmatically creating views whose intrinsic heights exceed the height of their parents? What is android's official handling of this case? Does the view overflow like an HTML DOM element?
UPDATE:
On inspecting each RelativeLayout, my hypothesis that one of them exceeded 3240pixels was wrong. None of them do. Only there seems to be a top margin applied to some (Which would theoretically increase the height of its parent, being wrap_content) but it does not.
The definition of the RelativeLayout called CustomView, is a RelativeLayout within which are programmatically positioned relativelayouts. They are positioned within their parent as follows:
int pixels = (int) (height * scale + 0.5f);
pixels = (pixels + 4) / 5 * 5;
relativeLayout.setLayoutParams(new RelativeLayout.LayoutParams(
columnWidth / maxConflicts,
pixels));
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
params.addRule(RelativeLayout.ALIGN_PARENT_START);
start = cal2.get(Calendar.HOUR_OF_DAY);
height = start > 0 ? start * 44.000000000f + (start - 1) : start * 44.00000000f;
height += cal2.get(Calendar.MINUTE) / 60.00000000000f;
pixels = (int) (height * scale + 0.5f);
if (offsetAmount != null){
params.setMargins( (columnWidth / maxConflicts) * offsetAmount, pixels, 0, 0);
} else {
params.setMargins(0, pixels, 0, 0);
}
relativeLayout.setLayoutParams(params);
However there is never a case where the top margin of the relativeLayout above plus the height of the of the relativeLayout exceeds 1080dp. I verify this dynamically by logging the height of the CustomView DayView with the following code:
columnHeight = MeasureSpec.getSize(heightMeasureSpec);
which always logs out as 3240 pixels which at a scale of 3 is exactly 1080dp.
So this eliminates the possibility that one of the DayView exceeds 1080 and that is why not all the children of the top level LinearLayout are not flush to its top. However, I still cannot comprehend what could be causing this behavior. All elements programmatically added to the DayView are alignTop to their parent and are positioned with a vertically margin which, with the height of the view being programmatically added` never exceeds 1080dp.
Notice that the red views have a mysterious mTop added even though their dynamic height is 1080dp. mTop + 1080dp > 1080dp yet there parent is 1080dp (it is wrap_content) !
Take a good look at the view which is flush. It also has the same height but has a zero mTop. I don't understand why this is or how can be possible.
enter image description here
UPDATE Saturday:
Good News! i have narrowed down the problem to a subview of a subview, which when omitted, does not cause this strange inconsistency in my layout logic / intention. The culprit is a <TextView> which is programmatically added to the two types of events which are programmatically added <RelativeLayout>s added to DayView. when both types of views have textviews added as subviews it breaks the view hierarchy. When only one is present, the views present as intended. Here is the code for programmatically laying out the calendar events the entirety of which consist of all the subviews of a DayView:
private void createEventLayout(CalendarEvent e, Integer offsetAmount, int maxConflicts) {
final float scale = getContext().getResources().getDisplayMetrics().density;
int idForMyView = e.assignmentId > 0 ? e.assignmentId : -e.conflictId;
RelativeLayout relativeLayout = new RelativeLayout(getContext());
relativeLayout.setId(idForMyView);
Calendar cal2 = Calendar.getInstance();
cal2.setTimeZone(TimeZone.getTimeZone("UTC"));
cal2.setTime(e.startDate);
Calendar cal3 = Calendar.getInstance();
cal3.setTimeZone(TimeZone.getTimeZone("UTC"));
cal3.setTime(e.endDate);
float start = cal2.get(Calendar.HOUR_OF_DAY);
start += cal2.get(Calendar.MINUTE) / 60.0000000f;
float end = cal3.get(Calendar.HOUR_OF_DAY);
end += cal3.get(Calendar.MINUTE) / 60.00000000f;
float height = (end - start) * 45.0000000f;
int pixels = (int) (height * scale + 0.5f);
pixels = (pixels + 4) / 5 * 5;
relativeLayout.setLayoutParams(new RelativeLayout.LayoutParams(
columnWidth / maxConflicts,
pixels));
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
params.addRule(RelativeLayout.ALIGN_PARENT_START);
start = cal2.get(Calendar.HOUR_OF_DAY);
height = start > 0 ? start * 44.000000000f + (start - 1) : start * 44.00000000f;
height += cal2.get(Calendar.MINUTE) / 60.00000000000f;
pixels = (int) (height * scale + 0.5f);
if (offsetAmount != null){
params.setMargins( (columnWidth / maxConflicts) * offsetAmount, pixels, 0, 0);
} else {
params.setMargins(0, pixels, 0, 0);
}
relativeLayout.setLayoutParams(params);
if (e.assignmentId > 0){
// company name
TextView tv = new TextView(getContext());
tv.setId(idForMyView);
tv.setText(e.companyName);
LayoutParams tvParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
tvParams.setMargins(0,0,0,0);
tv.setLayoutParams(tvParams);
RelativeLayout.LayoutParams companyNameParams = (RelativeLayout.LayoutParams) tv.getLayoutParams();
tv.setTextColor(Color.parseColor("#ffffff"));
tv.setTypeface(null, Typeface.BOLD);
companyNameParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
//time of assignment / conflict
TextView tvTime = new TextView(getContext());
tvTime.setTextColor(Color.parseColor("#ffffff"));
SimpleDateFormat sdf = new SimpleDateFormat("h:mm a");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String startTime = sdf.format(e.startDate);
String endTime = sdf.format(e.endDate);
tvTime.setText(startTime + " - " + endTime);
tvTime.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
RelativeLayout.LayoutParams timeParams = (RelativeLayout.LayoutParams) tvTime.getLayoutParams();
timeParams.addRule(RelativeLayout.BELOW, idForMyView);
relativeLayout.addView(tv); // ALSO this will break if present with the other kind
relativeLayout.addView(tvTime); // this too
relativeLayout.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.gradient_background_home));
relativeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final int assignmentId = view.getId();
if (assignmentId > 0) {
Intent intent = new Intent(getContext(), AssignmentDetailActivity.class);
intent.putExtra(getContext().getString(R.string.extra_assignment_id), assignmentId);
getContext().startActivity(intent);
}
}
});
} else {
final CalendarEvent event = e;
relativeLayout.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.conflict));
if (isConflictMode && e.isAllDay == 1){
TextView tv = new TextView(getContext());
tv.setId(idForMyView);
tv.setText(ALL_DAY_TEXT);
LayoutParams tvParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
tvParams.setMargins(0,0,0,0);
tv.setLayoutParams(tvParams);
RelativeLayout.LayoutParams companyNameParams = (RelativeLayout.LayoutParams) tv.getLayoutParams();
tv.setTextColor(Color.parseColor("#ffffff"));
tv.setTypeface(null, Typeface.BOLD);
companyNameParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimension(R.dimen.calendar_small_font));
relativeLayout.addView(tv); //THIS will break the layout logic
}
}
this.addView(relativeLayout);
return;
}
I tried making sure the <TextView> is clipped by its parent by setting
tv.setGravity(Gravity.CLIP_HORIZONTAL | Gravity.CLIP_VERTICAL)
when I inspect it in the view it appears that it is clipping exactly to the bounds of the tv, yet, something seems to be exceeding the height of its parent based on the fact that when the <TextViews> are gone everything addes up, but when they are present, there seems to be too much vertical content.
So my problem was that i was assigning the same id to the <RelativeLayout> which was the particular CalendarEvent as well as to the children of this view, the <TextViews>. This created circular logic which referred to the parent and child which caused the views to not properly by aligned, I think specifically with the BELOW requirement of one of the textViews, being essentially made to be below itself. By creating a unique id with View.generateId() each time I created a <TextView> then the intended layout was made possible.
I have an activity, containing a layout. I have a pool of 10 imagebuttons set up and hidden (invisible) in this layout. I am trying to load the image to these imagebuttons programmatically, and then set their location on the screen.
I have found this previous discussion: How to set margin of ImageView using code, not xml
This, however, does not work. I am getting tiny imagebuttons as a result, despite setting the height and width parameters. Here is my code:
RelativeLayout.LayoutParams lp1 = new RelativeLayout.LayoutParams(btnGP1.getLayoutParams());
lp1.leftMargin = P1leftValue;
lp1.topMargin = P1topValue;
lp1.height = 80;
lp1.width = 80;
btnGP1.setLayoutParams(lp1);
Something about this is not right, but I am not having any luck finding a more accurate resource. Any ideas?
Update:
I have edited my code to this:
RelativeLayout.LayoutParams lp1 = (RelativeLayout.LayoutParams)btnGP1.getLayoutParams();
lp1.leftMargin = P1leftValue;
lp1.topMargin = P1topValue;
lp1.height = 280;
lp1.width = 280;
btnGP1.setLayoutParams(lp1);
And it appears to be almost working. The only thing that remains now is that the functions above only appear to be allowing me to enter pixel sizes. I need to be able to enter pixel densities instead as not all screen sizes are the same.
My new question is this: how do I go about this? Is there a simple answer here? If I try 280dp or "280dp" the compiler will not allow it.
The answer to this was found in another thread. This was my final code:
final float SCALE = getResources().getDisplayMetrics().density;
RelativeLayout.LayoutParams LP = new RelativeLayout.LayoutParams(btnGP15.getLayoutParams());
LP.leftMargin = (int) (P1leftValue * SCALE + 0.5f);
LP.topMargin = (int) (P1topValue * SCALE + 0.5f);
LP.height = (int) (tileSize * SCALE + 0.5f);
LP.width = (int) (tileSize * SCALE + 0.5f);
Where btnGP15 is my image, P1leftValue and P1topValue store the dp values for my required location, so 100 and 150 for example. tileSize stores the dp value for the size of my images, 70 in my case.
Working perfectly ;)
I am trying to add many button into Relativelayout or Linearlayout,
Layout
<Relativelayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
>
</Relativelayout>
then in the class
_ll_layout = (RelativeLayout) findViewById(R.id.container);
I only know how add the button dynamically with code.
JSONArray jsonArray = new JSONArray(tmp.getString("productos"));
Button bt[] = new Button[jsonArray.length()]; // size of product
for (int i = 0; i < jsonArray.length(); i ++){
int padding_40dp = (int) (40 * scale + 0.5f);
int margin_10dp = (int) (10 * scale + 0.5f);
int padding_90dp = (int) (90 * scale + 0.5f);
LinearLayout.LayoutParams params = new Relativelayout.LayoutParams(padding_90dp, padding_40dp);
params.setMargins(margin_10dp, 0 , 0, 0);
bt[i] = new Button(DetalleServicioActivity.this);
bt[i].setText(jsonArray.getJSONObject(i).getString("nombre"));
bt[i].setTag(new TagInfo(jsonArray.getJSONObject(i).getString("id_producto")));
bt[i].setTextColor(Color.parseColor("#FFFFFF"));
bt[i].setBackgroundColor(Color.parseColor("#D8D8D8"));
bt[i].setEnabled(false);
bt[i].setId(Integer.parseInt(jsonArray.getJSONObject(i).getString("id_producto")));
bt[i].setLayoutParams(params);
_ll_layout.addView(bt[i]);
}
but the result is
One on another one, but I need something like this:
Edit
If I use LinearLayout with orientation horizontal and gravity center, this happend
Instead of using Relative Layout or Linear Layout I would rather suggest you to create custom flow layout.Custom flow layout will adjust child views accordingly in rows, and will jump the button in new row according to screen width.
Please have a look here : Flow layout example
Happy Coding :)
Instead of RelativeLayout, make use of LinearLayout with orientation as horizontal and add the button in them at run time
As per your design requirement, make sure you have two linear layouts here.
you can use griedlayout for solved your problem
here is the code that i am using to generate my view object:
// creates the seekBar
sBGauge = new SeekBar(this);
sBGauge.setMax(depthL - 1);
sBGauge.setMinimumWidth(150);
sBGauge.setId(2000 + i);
sBGauge.setOnSeekBarChangeListener(this);
reguardless of what i set the sBGauge.setMinimumWidth(); to it always appears to only be about 20 wide.
any thoughts?
thanks
Edit: to give some more info I am using this seekbar in between two textViews in a tablerow.
bump
#Amit Hooda your solution did not solve my problem but it did lead me down the right path to finding a solution.
What I had to do to fix this issue was to change from a dynamically created a TableRow to a LinearLayout within my TableLayout. I was then able to get my screen width and subtract out my textviews and then set my width of the seekbar with the resulting number.
// gets the width of the screen to set the seekbar width
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
width = width - 170;
LinearLayout lL = new LinearLayout(this);
// creates the seekBar
sBGauge = new SeekBar(this);
sBGauge.setMax(depthL - 1);
sBGauge.setId(2000 + i);
sBGauge.setLayoutParams(new LinearLayout.LayoutParams(width, 50, 1f));
sBGauge.setOnSeekBarChangeListener(this);
Try this
LayoutParams lp = new LayoutParams(150, 150);
sBGauge.setLayoutParams(lp);
you can change the value 150 and 150 according to your need.