I am creating a list view for message of different heights. The views are xml layout which are setup to layout the same on all resolution screens. To increase performance I am converting these view into a bitmap image by calling View.getDrawingCache(). I am getting the Bitmap image but it seems to be scaling the text, especially in the high resolution screens. This is leaving the text a little blurry and in some case it is also being cut off at the edges. If I layout the View rather than the bitmap image, everything is scaled correctly but I am not getting the performance i desire. My item xml layout looks like this:
Xml Item View:
<?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"
android:padding="5px"
android:background="#FFFFFF">
<ImageView android:id="#+id/contact_photo"
android:layout_width="48dip"
android:layout_height="48dip"/>
<ImageView android:id="#+id/network_icon"
android:layout_alignParentRight="true"
android:layout_width="16dip"
android:layout_height="16dip"/>
<TextView android:id="#+id/created_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_toLeftOf="#+id/network_icon"
android:textColor="#000000"/>
<TextView android:id="#+id/sender_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_toRightOf="#+id/contact_photo"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#000000"/>
<TextView android:id="#+id/summary"
android:layout_below="#+id/sender_name"
android:layout_toRightOf="#+id/contact_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:maxLines="4"
android:textColor="#000000" />
<LinearLayout
android:id="#+id/AttachmentLayout"
android:layout_below="#+id/summary"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_width="wrap_content"/>
</RelativeLayout>
View Measure Snippet:
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
final int index = layoutMode == LAYOUT_MODE_ABOVE ? 0 : -1;
child.setDrawingCacheEnabled(true);
addViewInLayout(child, index, params, true);
final int itemWidth = (int)(getWidth() * ITEM_WIDTH);
child.measure(MeasureSpec.EXACTLY | itemWidth, MeasureSpec.UNSPECIFIED);
I have also added the snippet of code preforming the measurement of the view. Does anyone know how to prevent the text from scaling?
Ok... folks I figure it out. It was really simple and should have know this from the beginning. The solution is to make sure you add the to the AndroidManifest.xml. I set the supported resolution to any because my xml layout files are designed to automation re-size.
Related
I am trying to display a table that has three rows. The first two are images, the third is just data. I have tried to set them up with table row weights of 45%, 45%, 10%... However that never happens, it always come out somewhere about 47%, 47%, 6%.
But that's minor compared to the behavior that I can't figure out.
The images and data come in async, so they are populated using a "runnable".
The problem is the first image gets bigger every time it updates. And the second image keeps getting smaller.
However, here's the kicker, when I set breakpoints and pause it to debug it, it works exactly as I want it to. It doesn't change sizes, the row heights do not change.
So, that said, here is my layout XML:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:layout_weight="1"
android:id="#+id/mainscreen">
android:background="#00ff00"
<TableRow
android:layout_width="fill_parent"
android:layout_height="0dp"
android:id="#+id/img1box"
android:layout_weight="45"
android:gravity="center_vertical|center_horizontal">
<ImageView
android:contentDescription="image1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/img1"
android:src="#drawable/icon"/>
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="0dp"
android:id="#+id/img2box"
android:layout_weight="45"
android:gravity="center_vertical|center_horizontal">
<ImageView
android:contentDescription="image2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/img2"
android:src="#drawable/icon" />
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="10"
android:id="#+id/dataholder"
android:gravity="center_vertical|center_horizontal">
<TextView
android:id="#+id/statusbox"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
android:textColor="#000000"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_gravity="center"/>
</TableRow>
</TableLayout>
I have tried settings such as 0dp (or 0px), fill_parent, match_parent, wrap_content, and a dozen other configurations from web searches.
And the code snippets that happen when the data/images are posted (removed the parts that do stuff like findViewById(), that happens at onCreate() and works fine, plus rename some confidential variables):
final Runnable HandleImage1Ready = new Runnable() {
#Override
public void run() {
int height, width;
float xfactor, yfactor;
height = m_Image1Box.getHeight(); // This is the TableRow
width = m_Image1Box.getWidth();
// Compute the reduction factor, but it's
// always by height right now, so we just use that
Bitmap image = BitmapFactory.decodeByteArray(m_img1, 0, m_img1.length);
yfactor = height / (float) image.getHeight();
xfactor = width / (float) image.getWidth();
// create a new bitmap, I even try reducing the "height"
// to prevent the table row from growing... doesn't help
Bitmap scaled = Bitmap.createScaledBitmap(image, (int) (image.getWidth() * yfactor), height, true);
m_Image1.setImageBitmap(scaled);
CompleteProcessing();
}
};
final Runnable HandleImage2Ready = new Runnable() {
#Override
public void run() {
int height, width;
float xfactor, yfactor;
height = m_Image2Box.getHeight(); // This is the TableRow
width = m_Image2Box.getWidth();
// EVEN TRY USING THE SAME IMAGE ON ROW 2...
//Bitmap image = BitmapFactory.decodeByteArray(m_img2, 0, m_img2.length);
Bitmap image = BitmapFactory.decodeByteArray(m_img1, 0, m_img1.length);
yfactor = height / (float) image.getHeight();
xfactor = width / (float) image.getWidth();
Bitmap scaled = Bitmap.createScaledBitmap(image, (int) (image.getWidth() * yfactor), height, true);
m_Image2.setImageBitmap(scaled);
CompleteProcessing();
}
};
final Runnable HandleMicrReady = new Runnable() {
#Override
public void run() {
int height, width;
// Just to debug these, and see how this row changes
height = m_statusBox.getHeight();
width = m_statusBox.getWidth();
m_statusBox.setText(m_data);
CompleteProcessing();
}
};
Repeating... When I set a break point in one of the above functions, and resume the execution, both images are the same size, every time it posts new images. Which is what I want to happen.
But if I don't set the break point, every time a new image is posted, the top table row gets bigger and bigger. To the point that the top image is over 50% of the table (screen), and the bottom image and table row of data are getting smaller.
In essence, I believe Android Studio is laughing at me because, like a rattle in a car you take in, it doesn't do it when someone is watching for the problem...
(I included an "android-studio" tag, because pausing it in AS debugger causes the problem it disappear).
Any advice is appreciated.
-Scotty
Table layouts do not seem to take the weights placed upon them very strictly. E.g. when you add an ImageView to a TableRow with or without a src it already renders slightly differently. I also verified this with 2 images with different dimensions and the row with the bigger image completely overtakes the row of the smaller one.
The solution is to not use a TableLayout. If you don't want to use something like a GridView I suggest sticking with LinearLayout. You can use it in the exact same manner. You only need to specify its android:orientation="horizontal|vertical" attribute instead of making a distinction between TableRow (the horizontal kind) or TableLayout (the vertical kind)
Here's a visual comparison:
So for your provided layout XML this would become:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:weightSum="100"
android:background="#00ff00"
android:id="#+id/mainscreen">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:id="#+id/img1box"
android:layout_weight="45"
android:gravity="center_vertical|center_horizontal">
<ImageView
android:contentDescription="image1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/img1"
android:src="#drawable/icon"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:id="#+id/img2box"
android:layout_weight="45"
android:gravity="center_vertical|center_horizontal">
<ImageView
android:contentDescription="image2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/img2"
android:src="#drawable/icon" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="10"
android:id="#+id/dataholder"
android:gravity="center_vertical|center_horizontal">
<TextView
android:id="#+id/statusbox"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
android:textColor="#000000"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_gravity="center"/>
</LinearLayout>
</LinearLayout>
I'm trying to generate a bitmap to show in the notification. The layout is a bit complex, however, I need to set the width according to the screen size. In the XML, I have a LinearLayout in the root and I inflate the view with
LinearLayout v = (LinearLayout) i inflater.inflate(R.layout.notification_expanded_detail, null, false);
This layout has quite a bunch of elements which I need to fill. And their width depends on the width of the layout itself and the width of the layout varies with phone. I set the width for the inflated layout with
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) available_width, LinearLayout.LayoutParams.WRAP_CONTENT);
v.setLayoutParams(params);
However, when I try to generate a bitmap from this, it appears that the size set with the setLayoutParams is not affecting the child views of the inflated layout. There are many questions around i in SO, however, all of them deals with inflating a layout that already has a rootView and nothing worked for me. A couple of answers suggested that I call the measure() method on the view. However, after calling the measure method, the getMeasuredWidth returns a width lesser that what I had set with the setLayoutParams call.
Update
A little more context about the problem. I was trying to show a notification that had a placeholder ImageView which I would later fill in with a bitmap generated from a view inflated from an XML. The remote view layout is
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sliderViewBottom"
android:layout_width="match_parent"
android:layout_height="256dp"
android:background="#color/detail_page_blue"
android:clickable="true"
android:focusable="false"
android:orientation="vertical"
android:gravity="center"
android:padding="10dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#color/white"
>
<ImageView
android:id="#+id/progressImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#D05757"
android:gravity="center"
android:paddingBottom="8dp"
android:paddingTop="8dp"
>
<TextView
android:id="#+id/overAllDelay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delayed by "
android:textColor="#color/white"
/>
<TextView
android:id="#+id/overAllDelayVal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/overAllDelay"
android:text="-"
android:textColor="#color/white"
/>
</RelativeLayout>
</LinearLayout>
The layout that I was trying to inflate and generate a bitmap from to set in the progressImage was
<RelativeLayout
android:id="#+id/swipe_page_track_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingLeft="#dimen/swipe_layout_info_card_track_margin"
android:paddingRight="#dimen/swipe_layout_info_card_track_margin"
>
<ImageView
android:id="#+id/fromGreenCircle"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="1dp"
android:src="#drawable/from_green_circle"/>
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_alignParentRight="true"
android:layout_marginTop="1dp"
android:src="#drawable/station_end"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="3dp"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="4.5dp"
android:background="#drawable/swipe_page_empty_track"
/>
<ImageView
android:id="#+id/fromGreenTrack"
android:layout_width="105dp"
android:layout_height="3dp"
android:layout_alignParentLeft="true"
android:layout_marginTop="4.5dp"
android:src="#drawable/from_green_track"
/>
<ImageView
android:id="#+id/trackNavigationIcon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginLeft="100dp"
android:src="#drawable/ic_track_detail_card_current_station_pointer"
/>
<TextView
android:id="#+id/currentStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/trackNavigationIcon"
android:layout_marginTop="10dp"
android:textColor="#color/black"
/>
</RelativeLayout>
I was trying to inflate this second layout with
LinearLayout v = (LinearLayout) i inflater.inflate(R.layout.notification_expanded_detail, null, false);
And then was using
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) available_width, LinearLayout.LayoutParams.WRAP_CONTENT);
v.setLayoutParams(params);
This available_width was computed dynamically. However, when I later tried to generate a Bitmap from this inflated view by using v.layout(), it kep t generating an image which was not honouring the width that I set.
Later, I came across this SO thread where I noticed that while inflating, instead of specifying null as the parentView, new LinearLayoutParams(contex) was specified. I tried that and now, the dynamically set width is being honoured.
I came across this SO thread where I noticed that while inflating, instead of specifying null as the parentView, new LinearLayoutParams(contex) was specified. I tried that and now, the dynamically set width is being honoured
I have such layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/my_image"
android:ellipsize="end"
android:singleLine="true"
android:text="Some text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<ImageView
android:id="#+id/my_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/title"
android:layout_alignBottom="#+id/title"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:src="#drawable/my_bitmap_image" />
This layout does almost what I need: it makes image view height the same as text view. The image graphic contents stretched also keeping aspect ratio.
But, the width of the image view does not change! As a result, I have a wide gap between text and the image view! As a temporal solution, I override View#onLayout.
The question: how to change image width in xml layout?
UPDATE:
This is a final layout I need (text + a few images).
Look at the first image: its width should be exactly the same as scaled image in it with no paddings and margins:
For the imageView you can add the images to a linearlayout and give the weight property. For example if you have 3 images then give the linearlayout weight as 3 and then for each image you give the weight as 1. This way it will be uniformly aligned with equal width for all the images. Make linear orientation as horizontal hope so u got my point.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3"
>
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight ="1" />
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight ="1" />
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight ="1" />
</LinearLayout>
OK, as I see from the answers, there is no solution to force image views to change theirs width and height proportionally.
So, this can be solved only programmatically.
There is my solution below :
a) Create you custom layout class
(don't forget to override all the parent constructors with public access modifier, otherwise GUI editor will fail):
public class MyLayout extends RelativeLayout {...
b) Override method: Deprecated - changing layout params in this method might cause side effects. See recommended approach below
protected void onLayout(boolean changed, int l, int t, int r, int b) {
ImageView icon = (ImageView) findViewById(R.id.my_image);
icon.setMaxWidth(icon.getMeasuredHeight());
super.onLayout(changed, l, t, r, b);
}
b) Add creational method:
public static MyLayout createLayout(ViewGroup parent) {
Context context = parent.getContext();
MyLayout item = (MyLayout) LayoutInflater.from(context).inflate(
R.layout.my_layout, parent, false);
TextView tv = (TextView) findViewById(R.id.title);
ImageView icon = (ImageView) findViewById(R.id.my_image);
ViewGroup.LayoutParams p = icon.getLayoutParams();
p.width = (int) tv.getTextSize();;
icon.setLayoutParams(p);
return item;
}
c) Final layout:
<?xml version="1.0" encoding="utf-8"?>
<com.mypackage.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/my_image"
android:ellipsize="end"
android:singleLine="true"
android:text="Some text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<ImageView
android:id="#+id/my_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/title"
android:layout_alignBottom="#+id/title"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:src="#drawable/my_bitmap_image" />
</com.mypackage.MyLayout>
Instead of RelativeLayout, use LinearLayout. You may add "Weight" property to ensure desired spacing.
see the code below
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/logonFormButtons"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="true"
android:orientation="horizontal">
<TextView
android:id="#+id/logonFormBTLogon"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Some Text"
android:layout_weight="0.5" />
<ImageView
android:id="#+id/logonFormBTCancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:src="#drawable/apk"
android:layout_weight="0.5" />
</LinearLayout>
I guess this has to do with android:layout_alignParentRight="true" for your ImageView. Have you tried layout_toRightOf="#id/title" ?
Also you can try android:scaleType="fitStart" on the ImageView. This should align the image to the top left of the ImageView.
As for me I appreciate to change width or height dinamically in my code using getLayoutParams()... for example
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int wwidth = displaymetrics.widthPixels;
convertPixelsToDp(wwidth, getApplicationContext());
// Toast.makeText(getApplicationContext(), height+ "= "+convertPixelsToDp(height, getApplicationContext()) +
// " " + wwidth+ "= "+convertPixelsToDp(wwidth, getApplicationContext()), Toast.LENGTH_LONG).show();
//scroll view 1 screen height size
scr1 = (ScrollView) findViewById(R.id.ScrollView01);
scr1.getLayoutParams().height=height - 200;
put image view in Linear Layout . the give weight to each and every image view.. it will increase the size proportionately ..
try to add scaleType = fitXY OR centerInside property to imageView
I want to display three buttons in the middle of a screen, and have the three buttons all be the same width, though they will have text labels of different lengths.
Just adding three buttons with text labels of different lengths produces buttons of different widths.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center">
<Button
android:id="#+id/button_1"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:text="ABCDEF" />
<Button
android:id="#+id/button_2"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:text="GHI" />
<Button
android:id="#+id/button_3"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:text="JKLM" />
</LinearLayout>
default button width wraps contents:
--
Setting the layout_weight to 1 and the layout_width to 0dip on all the buttons causes them to stretch equally to fill the entire screen width. For what I want, such buttons are simply too big, especially on large screens.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center">
<Button
android:id="#+id/button_1"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="ABCDEF" />
<Button
android:id="#+id/button_2"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="GHI" />
<Button
android:id="#+id/button_3"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="JKLM" />
</LinearLayout>
layout weight 1 buttons fill screen width:
--
Setting different values for weightSum in the parent LinearLayout can be used to stop the buttons from filling the entire screen, but I don't think this is the path I want to take, because I don't want the buttons to occupy a large portion of the screen on large screen devices. To clarify, using weightSum, I could, for example, set the three buttons to collectively occupy half the screen width, which may look OK on small screens, but on a large screen, the buttons would still occupy half the screen width, and the buttons would simply be much larger than what I want. Perhaps the final solution will be to simply have different layout files for different screens, but I'd rather not go down this path.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:weightSum="5">
<Button
android:id="#+id/button_1"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="ABCDEF" />
<Button
android:id="#+id/button_2"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="GHI" />
<Button
android:id="#+id/button_3"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="JKLM" />
</LinearLayout>
weight sum 5 small screen:
weight sum 5 large screen:
--
I also tried many things with TableLayout, but didn't get anything better than just using LinearLayout.
GridView is extra-clumsy to use, and I haven't tried it, yet.
So, how does one create buttons with equal widths, preferrably where they are only as wide as necessary to fit the contents of the button with the longest label?
Any advice is appreciated.
(I did search and find this question asked and answered many times, but none of the answers I found resolved what I'm trying to achieve.)
Perhaps the final solution will be to simply have different layout files for different screens, but I'd rather not go down this path.
Many programmers will use res/layout/ and res/layout-large/ for handling situations like this. In the limited case of the three buttons, you might have alternatives, but usually user interfaces aren't quite that simplistic.
So, how does one create buttons with equal widths, preferrably where they are only as wide as necessary to fit the contents of the button with the longest label?
To accomplish your "preferrably" [sic] requirement, you would need to create a custom layout class for that. Here is one related to it, for the dashboard pattern, that you might use as a starting point.
If you know in advance what your widest button text will be you can use the android:ems attribute to set your buttons to that width.
<Button
android:id="#+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minEms="5"
android:text="#string/my_button" />
It's not perfect but it's the easiest way that I've found to achieve this look without endlessly fiddling around with layouts.
use Dashboard Fragment class
http://code.google.com/p/iosched/source/browse/android/src/com/google/android/apps/iosched/ui/DashboardFragment.java
Just one additional note from my side!
If you will need to set the width (or height) for the buttons (or any other views with a simple changes of code) that are added at runtime, you can use the code below (it does not depend on orientation):
public void AlignButtons(){
llModules = (LinearLayout) findViewById(R.id.llModules);
ViewTreeObserver viewTree = llModules.getViewTreeObserver();
viewTree.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int childcount = llModules.getChildCount();
int maxWidth = 0; //int maxHeight = 0;
String fontPath = "fonts/Isabella-Decor.ttf";
Typeface tf = Typeface.createFromAsset(getAssets(), fontPath);
for (int i = 0; i < childcount; i++) {
LinearLayout v = (LinearLayout)llModules.getChildAt(i);
View vv = v.getChildAt(0);
if (vv instanceof Button) {
int width = vv.getMeasuredWidth();
maxWidth = (maxWidth > width) ? maxWidth : width;
//int height = vv.getMeasuredHeight();
//maxHeight = (maxHeight > height) ? maxHeight : height;
}
}
for (int i = 0; i < childcount; i++) {
LinearLayout v = (LinearLayout)llModules.getChildAt(i);
View vv = v.getChildAt(0);
if (vv instanceof Button) {
LayoutParams params = ((Button) vv).getLayoutParams();
params.width = maxWidth;
//params.height = maxHeight;
((Button) vv).setLayoutParams(params);
// Applying font
((Button) vv).setTypeface(tf);
}
vv = v.getChildAt(1);
if (vv instanceof TextView) {
// Applying font
((TextView) vv).setTypeface(tf);
}
}
return true;
}
});
}
Here, in my case:
llModule - some layout containing another layout with what we need to align(v.getChildAt(0);) and other(v.getChildAt(1);).
LinearLayout v = (LinearLayout)llModules.getChildAt(i); - obtain layout of buttons to align.
Other parts are clear to understand.
Two cycles in this code are quite identical, but I still can't imagine how to combine them (to speed up the execution time).
Use this proper code this will help you.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="3">
<Button
android:id="#+id/button1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Get the Time"
android:onClick="showNewDate" />
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Get the Time"
android:onClick="showNewDate" />
<Button
android:id="#+id/button3"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Get the Time"
android:onClick="showNewDate" />
Keep the individual buttons under its own RelativeLayout.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:text="Button1"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="Button2"/>
</RelativeLayout>
</LinearLayout>
Reading this thread, then doing some trial-and-error and noticed that android:layout_width="180px" is an accepted parameter. Now as said, I just stumbled on this, didn't try to use this to solve your three button scenario.
It could well be that things changed since you originally posted. Although I tried this while building for version 1.5. That's old enough... :-)
Here is the complete main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/dateText"
android:text="\n\nClick for Date\n\n"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button"
android:layout_width="180px"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Get the Time"
android:onClick="showNewDate" />
</LinearLayout>
I want to display three buttons in the middle of a screen, and have the three buttons all be the same width, though they will have text labels of different lengths.
Just adding three buttons with text labels of different lengths produces buttons of different widths.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center">
<Button
android:id="#+id/button_1"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:text="ABCDEF" />
<Button
android:id="#+id/button_2"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:text="GHI" />
<Button
android:id="#+id/button_3"
android:layout_height="fill_parent"
android:layout_width="wrap_content"
android:text="JKLM" />
</LinearLayout>
default button width wraps contents:
--
Setting the layout_weight to 1 and the layout_width to 0dip on all the buttons causes them to stretch equally to fill the entire screen width. For what I want, such buttons are simply too big, especially on large screens.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center">
<Button
android:id="#+id/button_1"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="ABCDEF" />
<Button
android:id="#+id/button_2"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="GHI" />
<Button
android:id="#+id/button_3"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="JKLM" />
</LinearLayout>
layout weight 1 buttons fill screen width:
--
Setting different values for weightSum in the parent LinearLayout can be used to stop the buttons from filling the entire screen, but I don't think this is the path I want to take, because I don't want the buttons to occupy a large portion of the screen on large screen devices. To clarify, using weightSum, I could, for example, set the three buttons to collectively occupy half the screen width, which may look OK on small screens, but on a large screen, the buttons would still occupy half the screen width, and the buttons would simply be much larger than what I want. Perhaps the final solution will be to simply have different layout files for different screens, but I'd rather not go down this path.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center"
android:weightSum="5">
<Button
android:id="#+id/button_1"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="ABCDEF" />
<Button
android:id="#+id/button_2"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="GHI" />
<Button
android:id="#+id/button_3"
android:layout_height="fill_parent"
android:layout_width="0dip"
android:layout_weight="1"
android:text="JKLM" />
</LinearLayout>
weight sum 5 small screen:
weight sum 5 large screen:
--
I also tried many things with TableLayout, but didn't get anything better than just using LinearLayout.
GridView is extra-clumsy to use, and I haven't tried it, yet.
So, how does one create buttons with equal widths, preferrably where they are only as wide as necessary to fit the contents of the button with the longest label?
Any advice is appreciated.
(I did search and find this question asked and answered many times, but none of the answers I found resolved what I'm trying to achieve.)
Perhaps the final solution will be to simply have different layout files for different screens, but I'd rather not go down this path.
Many programmers will use res/layout/ and res/layout-large/ for handling situations like this. In the limited case of the three buttons, you might have alternatives, but usually user interfaces aren't quite that simplistic.
So, how does one create buttons with equal widths, preferrably where they are only as wide as necessary to fit the contents of the button with the longest label?
To accomplish your "preferrably" [sic] requirement, you would need to create a custom layout class for that. Here is one related to it, for the dashboard pattern, that you might use as a starting point.
If you know in advance what your widest button text will be you can use the android:ems attribute to set your buttons to that width.
<Button
android:id="#+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minEms="5"
android:text="#string/my_button" />
It's not perfect but it's the easiest way that I've found to achieve this look without endlessly fiddling around with layouts.
use Dashboard Fragment class
http://code.google.com/p/iosched/source/browse/android/src/com/google/android/apps/iosched/ui/DashboardFragment.java
Just one additional note from my side!
If you will need to set the width (or height) for the buttons (or any other views with a simple changes of code) that are added at runtime, you can use the code below (it does not depend on orientation):
public void AlignButtons(){
llModules = (LinearLayout) findViewById(R.id.llModules);
ViewTreeObserver viewTree = llModules.getViewTreeObserver();
viewTree.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int childcount = llModules.getChildCount();
int maxWidth = 0; //int maxHeight = 0;
String fontPath = "fonts/Isabella-Decor.ttf";
Typeface tf = Typeface.createFromAsset(getAssets(), fontPath);
for (int i = 0; i < childcount; i++) {
LinearLayout v = (LinearLayout)llModules.getChildAt(i);
View vv = v.getChildAt(0);
if (vv instanceof Button) {
int width = vv.getMeasuredWidth();
maxWidth = (maxWidth > width) ? maxWidth : width;
//int height = vv.getMeasuredHeight();
//maxHeight = (maxHeight > height) ? maxHeight : height;
}
}
for (int i = 0; i < childcount; i++) {
LinearLayout v = (LinearLayout)llModules.getChildAt(i);
View vv = v.getChildAt(0);
if (vv instanceof Button) {
LayoutParams params = ((Button) vv).getLayoutParams();
params.width = maxWidth;
//params.height = maxHeight;
((Button) vv).setLayoutParams(params);
// Applying font
((Button) vv).setTypeface(tf);
}
vv = v.getChildAt(1);
if (vv instanceof TextView) {
// Applying font
((TextView) vv).setTypeface(tf);
}
}
return true;
}
});
}
Here, in my case:
llModule - some layout containing another layout with what we need to align(v.getChildAt(0);) and other(v.getChildAt(1);).
LinearLayout v = (LinearLayout)llModules.getChildAt(i); - obtain layout of buttons to align.
Other parts are clear to understand.
Two cycles in this code are quite identical, but I still can't imagine how to combine them (to speed up the execution time).
Use this proper code this will help you.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:weightSum="3">
<Button
android:id="#+id/button1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Get the Time"
android:onClick="showNewDate" />
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Get the Time"
android:onClick="showNewDate" />
<Button
android:id="#+id/button3"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Get the Time"
android:onClick="showNewDate" />
Keep the individual buttons under its own RelativeLayout.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:text="Button1"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="Button2"/>
</RelativeLayout>
</LinearLayout>
Reading this thread, then doing some trial-and-error and noticed that android:layout_width="180px" is an accepted parameter. Now as said, I just stumbled on this, didn't try to use this to solve your three button scenario.
It could well be that things changed since you originally posted. Although I tried this while building for version 1.5. That's old enough... :-)
Here is the complete main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:id="#+id/dateText"
android:text="\n\nClick for Date\n\n"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button"
android:layout_width="180px"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Get the Time"
android:onClick="showNewDate" />
</LinearLayout>