Hi I'm developing an app on android and I have some layouts where I would like to have an image that fills the screen-width in landscape mode. On top of that, i want some buttons on specific places.
I used a scrollview (because the image doesn't fit in height if it fills the width of the screen), with a relative layout within.
In that relative layout, I put the imageview and the buttons. The image fills the width and wraps the content for the height. The buttons are placed through setting the left and top margins within the relative layout.
Everything is looking good on the phone i tested it with, but when i test on a different device (other screen size and density) all buttons are on the wrong place and aren't big enough anymore.
How can i change this, so that all proportions stay the same? The buttons have to be in the exact same position and have to be the same size, in comparison with the image.
My code:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/svCentralAmerica"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true" >
<RelativeLayout
android:id="#+id/rlCentralAmerica"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/ivCentralAmerica"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:src="#drawable/mapcentralamerica" />
<Button
android:id="#+id/mexu"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="16dp"
android:layout_marginTop="33dp"
android:background="#drawable/usa_y"
android:onClick="onCountryClick"
android:paddingTop="3dp"
android:scaleType="fitCenter"
android:tag="1;U"
android:text="2"
android:textColor="#color/Black"
android:textSize="15sp"
android:textStyle="bold" />
<Button
android:id="#+id/mexr"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_alignTop="#id/mexu"
android:layout_marginLeft="4dp"
android:layout_toRightOf="#id/mexu"
android:background="#drawable/russia_n"
android:onClick="onCountryClick"
android:paddingTop="18dp"
android:scaleType="fitCenter"
android:tag="1;R"
android:text="3"
android:textColor="#color/Red"
android:textSize="15sp"
android:textStyle="bold" />
</RelativeLayout>
</ScrollView>
I have actually done what you're thinking of. Unfortunately, if you want exact control of the positioning on any screen size, and an exact aspect ratio, then you're going to have to position resources programmatically. If you use xml, you can come pretty close, but won't have precise positioning control. For a beginner, positioning programmatically can be tedious. Since you're only positioning a couple of buttons here and there, it shouldn't be too much of a hassle.
How I did it: get the screenwidth and screenheight programmatically. Then calculate the aspect ratio of your button. Next, calculate the position of the button in relation to the screenwidth and screenheight (this would be in %). With all this info, you should be able to position buttons exactly where you want it on any screen size with a particular orientation. I would recommend positioning everything else on xml and do only the buttons programmatically. On orientation change, you will need to recalculate everything again, which can be annoying. I would recommend that you stick with 1 orientation and maybe support multiple orientations later.
I use these helper functions to move my buttons around based on the percentage of the screen height and width. First I layout the screen for a 320 x 480 screen in graphical layout to create the xml, then move the buttons in my onCreate method.
Here are the imports I used:
import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import android.widget.AbsoluteLayout;
import android.widget.Button;
import android.content.Context;
import android.util.Log;
Here are the calls to move the buttons in onCreate:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
moveBtn(0.1843,0.1187,0.128,0.7375,R.id.credits_button);
moveBtn(0.1843,0.1187,0.3125,0.7375,R.id.website_button);
moveBtn(0.1843,0.1187,0.5,0.7375,R.id.facebook_button);
moveBtn(0.1843,0.1187,0.6906,0.7375,R.id.support_button);
}
Here are the helper functions:
//----------------------------------------------------------------------------------------------
// public void moveBtn(double percent_width, double percent_height, double percent_x, double percent_y, int myObject)
//----------------------------------------------------------------------------------------------
public void moveBtn(double percent_width, double percent_height, double percent_x, double percent_y, int myObject) {
int width = (int)Math.round((ScWth(this) * percent_width));
int height = (int)Math.round((ScHgt(this) * percent_height));
int x = (int)Math.round((ScWth(this) * percent_x));
int y = (int)Math.round((ScHgt(this) * percent_y));
Log.d(TAG, "Object: " + myObject + "/Width: " + width + "/Height: "+ height + "/x: "+ x + "/y: "+ y);
AbsoluteLayout.LayoutParams myParam = new AbsoluteLayout.LayoutParams(width, height, x, y);
Button thisButton = (Button)findViewById(myObject);
thisButton.setLayoutParams(myParam);
}
//----------------------------------------------------------------------------------------------
// public static double ScWth(Context context)
//----------------------------------------------------------------------------------------------
//return the screen width of the device
public static double ScWth(Context context){
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
return display.getWidth();
}
//----------------------------------------------------------------------------------------------
// public static double ScHgt(Context context)
//----------------------------------------------------------------------------------------------
//return the screen height of the device
public static double ScHgt(Context context){
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
return display.getHeight();
}
I hope it helps someone.
Related
This might look like a very common problem but I can't seem to move a view to the center of the screen.
I have 3 views:
a background image (center crop)
a black/white grid to visualize the center of the screen, for debugging purpose. Also center crop
a square that I want to move to the center, top left corner of the square at the center of the screen.
I first get the screen size in pixels, divide width and height by 2 to get the coordinates of the center. Then move my square view by center.x and center.y
I expect the square to be moves precisely to the center of the screen.
I also scale the square view so its size is always the same relative to the background image.
I want to solve this programmatically (not just centering with layout properties) because it is only a first step, I intend to be able to place the square anywhere I want on the screen. I try to place in the center here because it seems like the easiest thing to do.
The code
public class FullScreenActivity extends AppCompatActivity {
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
}
}
The class whit the logic
public class AnimalPopupActivity extends FullScreenActivity {
private final String TAG = "AnimalPopup";
ImageView backgroundView;
ImageView square;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animal_popup);
backgroundView = (ImageView)findViewById(R.id.background);
square = (ImageView) findViewById(R.id.square);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidthInPixels = metrics.widthPixels;
int screenHeightInPixels = metrics.heightPixels;
Log.i(TAG, "screen width pixels: "+screenWidthInPixels);
Log.i(TAG, "screen height pixels: "+screenHeightInPixels);
Point center = new Point(screenWidthInPixels/2, screenHeightInPixels/2);
Log.i(TAG, "center: "+center);
double ratioH = (double)screenWidthInPixels/100d;
Log.i(TAG, "ratioH: "+ratioH);
double ratioV = (double)screenHeightInPixels/100d;
Log.i(TAG, "ratioV: "+ratioV);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams((int)(10*ratioH), (int)(10*ratioV));
layoutParams.leftMargin = center.x;
layoutParams.topMargin = center.y;
square.setLayoutParams(layoutParams);
}
}
Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.bentaye.app.games.AnimalPopupActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:srcCompat="#drawable/animal_popup_background" />
<ImageView
android:id="#+id/grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:srcCompat="#drawable/animal_popup_grid" />
<ImageView
android:id="#+id/square"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#drawable/square" />
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
Here is what I get on a Nexus 5 (1080*1920:420dpi)
With the following logs:
screen width pixels: 1080
screen height pixels: 1794
center: Point(540, 897)
ratioH: 10.8
ratioV: 17.94
Here is what I get on a Nexus 10 (2560*1600:xhdpi)
With the following logs:
screen width pixels: 1600
screen height pixels: 2464
center: Point(800, 1232)
ratioH: 16.0
ratioV: 24.64
The 2 are off by a few pixels, I am not an Android specialist and clearly I am doing something wrong but can't understand what.
Any help welcome. Cheers!
EDIT: I think that it is due the calculation taking the status bar into account. It also explains why the discrepancy is bigger on the Nexus5, as the status bar is bigger relatively to the screen height. And also why the discrepancy only affects the vertical alignment.
I'll come back and post the code once I sort it out.
Assuming the square you want centered is inside the RelativeLayout, add layout_gravity=center
For example:
<ImageView
android:id="#+id/square"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity=center"
app:srcCompat="#drawable/square"/>
Add this Line
android:layout_centerInParent="true"
this line is for RelativeLayout
A sprite on my android game is set to fall by 5 pixels every 100 milliseconds, this works fine the only problem is that the ImageView itself is only 53dp high, if I make it any bigger the image inside scales with it. Since the ImageView is only 53dp high the image disappears after 1100 milliseconds as it scrolls outside the boundaries of the imageview.
I need the layout height of the ImageView to fill_parent but I need the image to stay the same size instead of scaling with it, here's my current code:
<ImageView
android:id="#+id/blueman"
android:layout_width="0dip"
android:layout_height="53dp"
android:paddingRight="300dp"
android:layout_weight="0.03"
android:src="#drawable/fall" />
Thanks in advance :)
since you didn't give the full code of the layout, I'll make some assumptions...
you're talking about setting your sprite's height to the screen's height without scaling?
There should be a difference between your screen size (that is the root layout item) and the sprites in it. I guess you declared your layout as...:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="?gameBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/btTap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="14dp"
android:layout_marginTop="350dp"
android:background="#drawable/tap"
android:visibility="visible" />
<Button
android:id="#+id/btCellR1C1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="490dp"
android:layout_marginTop="155dp"
android:background="#drawable/cell_red" />
The only thing I had to cope with, was the scaling of my sprites depending on the device's resolution with such a method:
public static void scaleView(View view, int top, int left, int width,
int height) {
top = Math.round(top * scaleY);
left = Math.round(left * scaleX);
width = Math.round(width * scaleX);
height = Math.round(height * scaleY);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
params.height = height;
params.width = width;
params.topMargin = top;
params.leftMargin = left;
view.setLayoutParams(params);
view.setSoundEffectsEnabled(false);
}
Please give us more details to help you
Best regards
Serge
I want a Button like this:
+-----------------------+
| |
| +-----+ |
| |Image| |
| +-----+ |
| Text |
| |
| |
+-----------------------+
EDIT: Explanation to the picture: I want the COMBINATION of Image and text centered (text ALWAYS below the image)
I want the Button to stretch to a parent object (to make the whole area the button click area) and still align imgage AND text at center.
I achieve only top center alignment with folowing code, but I don't get the desired behaviour...
<Button
android:id="#+id/btInfo"
android:paddingTop="20dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="top|center_horizontal"
android:layout_weight="1"
android:background="#drawable/border_button_main_menu"
android:drawableTop="#drawable/bt_info"
android:onClick="onClick"
android:text="#string/info"
android:textColor="#drawable/bt_white_red_text"
android:textSize="15dp" />
changing android:gravity="top|center_horizontal" to android:gravity="center_vertical|center_horizontal" only leads to image centered at top and text centered at bottom...
---- EDIT2 -----
Wanted behaviour:
1) look as described (Image and text is a optical group and the group is centered in the button)
2) text should be part of the button (I want the onclick behaviour to work with selectors)
---- EDIT3 -----
added my own solution... but thanks to all that tried to help
Use the Following Code, your problem will be solve.
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="#+id/foreground"
android:layout_alignLeft="#+id/foreground"
android:layout_alignRight="#+id/foreground"
android:layout_alignTop="#+id/foreground"
android:background="#android:drawable/dialog_frame"
android:onClick="clickedMe" />
<RelativeLayout
android:id="#+id/foreground"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true" >
<TextView
android:id="#+id/button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="112dp"
android:text="#string/hello_world" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/button_text"
android:layout_centerHorizontal="true"
android:paddingBottom="10dip"
android:paddingTop="10dip"
android:src="#drawable/ic_launcher" />
</RelativeLayout>
</RelativeLayout>
------------- EDIT --------------------
oNclick Method:
final TextView text = (TextView) findViewById(R.id.button_text);
RelativeLayout foreground = (RelativeLayout) findViewById(R.id.foreground);
foreground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "clicked");
Toast.makeText(getApplicationContext(), "Clicked...!!!",
Toast.LENGTH_LONG).show();
text.setTextColor(Color.RED);
}
});
Try this:
<LinearLayout
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="#+id/btInfo"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#drawable/border_button_main_menu"
android:drawableTop="#drawable/bt_info"
android:onClick="onClick"
android:text="#string/info"
android:textColor="#drawable/bt_white_red_text"
android:textSize="15dp" />
</LinearLayout>
Call the setBackgroundDrawable() the text you will then add to the button will appear below the drawable!
I think the best way to achieve this kind of UI is using ImageButton instead of Button. But you can still achieve this by some hackish ways. One of them is here:
How to have Image and Text Center within a Button
You'll just have to mention inner RelativeLayout's orientation as "vertical" in the solution given in above link.
I hope it helps.
May be this can help you too:
How to center icon and text in a android button with width set to "fill parent"
The output you are trying to achieve can not be done in that way using drawableTop.
reason why? - View can set background or drawable and while setting drawable Android gives only 4 options to set bounds of the drawable either top,left,right or bottom and nothing for center.
Now in your XML you are having view's height and width as MATCH_PARENT so every time the drawable set using drawableTOP or LEFT etc. it will go to that edge of the view. which is happening right now with you.
From Comments :
Android OS is ultimately a Software.. and a software has always been developed within it's scope. The thing you are asking is out of scope so it's directly not supported by android using any default Form widget..
I demonstrated that how the android have written the Drawable class and how you can use it so please try to understand the answer and not only the Solution to your problem.. by the way to get click of whole area you can write click on that LinearLayout instead of the button.
Solution will be :
<LinearLayout height = MATCH_PARENT and width = 0dp>
<Button height and width = WRAP_CONTENT with drawableTop=image and gravity = center>
</LinearLayout>
My solution is now deriving from a button... that fullfills all my requirements... I don't know, if the measuring of the text is really exact that way, but it looks to be so... if not, everyone get's the idea behind and can adjust that...
Thanks for the help, though
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.widget.Button;
public class CenterImageTextButton extends Button {
private Paint mPaint = new Paint();
private String mText = null;
private float mTextSize = 0;
public CenterImageTextButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CenterImageTextButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CenterImageTextButton(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
mText = getText().toString();
mTextSize = getTextSize();
mPaint.setStyle(Style.FILL);
mPaint.setColor(getCurrentTextColor());
// get image top
Drawable drawable = getCompoundDrawables()[1];
Drawable curDrawable = null;
if (drawable instanceof StateListDrawable)
curDrawable = ((StateListDrawable)drawable).getCurrent();
else
curDrawable = ((BitmapDrawable)drawable).getCurrent();
Bitmap image = ((BitmapDrawable)curDrawable).getBitmap();
// call default drawing method without image/text
setText("");
setCompoundDrawables(null, null, null, null);
super.onDraw(canvas);
setText(mText);
setCompoundDrawables(null, drawable, null, null);
// get measurements of button and Image
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int imgWidth = image.getWidth();
int imgHeight = image.getHeight();
// get measurements of text
//float densityMultiplier = getContext().getResources().getDisplayMetrics().density;
//float scaledPx = textSize * densityMultiplier;
//paint.setTextSize(scaledPx);
mPaint.setTextSize(mTextSize);
float textWidth = mPaint.measureText(mText);
// draw Image and text
float groupHeight = imgHeight + mTextSize;
canvas.drawBitmap(image, (width - imgWidth) / 2, (height - groupHeight) / 2, null);
canvas.drawText(mText, (width - textWidth) / 2, mTextSize + (height - groupHeight) / 2 + imgHeight, mPaint);
}
}
I'm trying to create a 2D game engine for an Android app. I've followed this tutorial, which works fine for creating a full screen display, but I don't want that. I want to make my view take the top 2/3 (or whatever) of the screen, and fill the bottom third with standard Android widgets (buttons, text entry, etc.). I cannot get this to work. The best I can get is a blank white screen. I've tried many permutations, including using an outer LinerLayout, then embedding the custom SurfaceView inside a nested RelativeLayout, and putting the Android widgets in a nested LinearLayout, and it doesn't work.
For instance, this produces a white screen, when I feel like it should be 50% SurfaceView, 50% for a TextView:
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<RelativeLayout
android:layout_width="0dip"
android:layout_height="0dp"
android:layout_weight="1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapContainer"
>
</RelativeLayout>
<LinearLayout
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="0dp"
xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#color/white"
android:text="#string/test_str" />
</LinearLayout>
</LinearLayout>
MainActivity.java:
package com.removed.for.privacy;
import com.removed.for.privacy;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout mapContainer = (RelativeLayout) findViewById(R.id.mapContainer);
JSMapView mapView = new JSMapView(this);
mapContainer.addView(mapView);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
Any ideas?
Figured it out through lots of googling and semi-related questions. Here's an example of how I got it to fill 2/3 the height in portrait mode (100% width), and 2/3 width (100% height) in landscape.
In the custom surface view, override the onLayout method:
#Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (changed) {
(this).layout(0, 0, viewWidth, viewHeight);
}
}
In the class constructor, add this code:
public YourCustomSurfaceView(Context context) {
super(context);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
int rotation = display.getRotation();
// If vertical, we fill 2/3 the height and all the width. If horizontal,
// fill the entire height and 2/3 the width
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
screenWidth = display.getWidth();
screenHeight = display.getHeight();
viewHeight = 2 * (screenHeight / 3);
viewWidth = screenWidth;
} else {
screenWidth = display.getWidth();
screenHeight = display.getHeight();
viewWidth = 2 * (screenWidth / 3);
viewHeight = screenHeight;
}
// Enter rest of code here
}
To have Custom SurfaceView size
surfaceView!!.layoutParams = ConstraintLayout.LayoutParams(width, height)
ConstraintLayout Depend upon your Parent Layout
Now, that the constraintLayout is out. One can do this by setting "layout_constraintHeight_percent" to "0.5". The surfaceView would look like below:
<SurfaceView
android:id="#+id/surface_camera"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHeight_percent="0.5"
/>
To change SurfaceView's custom height and width use "android.view.ViewGroup.LayoutParams"
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. By this you can change X and Y of SurfaceView
Code of change surfaceview height width is here:
http://agalaxycode.blogspot.in/2014/12/change-surfaceview-height-width.html
This is my very first post at Stackoverflow. Before I make my question, I just want to say that this is a great resource of information and I find the community extremely helpful.
I hope to be able to share my Android development knowledge with everyone else as well.
I have been developing for Android for 6 months now and, although I have learned very much, I still greatly struggle when it comes to layout/design.
I have one layout.xml file that contains a Relative layout. Inside this layout, I have three buttons. I want those three buttons to have a certain width size that would grow or shrink depending on the device's screen size/density.
This is the xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayout1123123213"
android:layout_width="fill_parent"
android:layout_height="#dimen/actionbar_height"
android:orientation="horizontal">
<Button
android:id="#+id/btnLogin"
android:layout_width="100dp"
android:layout_height="50dp"
android:background="#drawable/button"
android:drawableLeft="#drawable/balloon_overlay_close"
android:gravity="center"
android:text="Earned" />
<Button
android:id="#+id/btnLogin2"
android:layout_width="113dp"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/btnLogin"
android:background="#drawable/button"
android:drawableLeft="#drawable/balloon_overlay_close"
android:gravity="center"
android:text="In Progress" />
<Button
android:id="#+id/btnLogin3"
android:layout_width="107dp"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/btnLogin2"
android:background="#drawable/button"
android:drawableLeft="#drawable/balloon_overlay_close"
android:gravity="center"
android:text="Done" />
</RelativeLayout>
I hard coded the width values, considering that I have different text sizes in each button... so that should obviously affect the width as well...
My question is... is there any intelligent way to accomplish this? Maybe programatically, when I know the current device's screen size?
Thanks everyone!
Felipe
====================================================
UPDATED SECTION
Hey guys,
Thanks for your help so far.
I have added the LinearLayout and the weight as per recommendation, but I am still not exactly getting what I want. It's almost there, but not quite.
My new xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="#dimen/actionbar_height"
android:orientation="horizontal"
android:gravity="center" android:weightSum="1">
<Button
android:id="#+id/btnLogin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#drawable/button"
android:drawableLeft="#drawable/balloon_overlay_close"
android:gravity="center"
android:text="#string/bottonbar_earned"
android:layout_weight=".10"/>
<Button
android:id="#+id/btnLogin2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".10"
android:background="#drawable/button"
android:drawableLeft="#drawable/balloon_overlay_close"
android:gravity="center"
android:text="#string/bottonbar_inprogress" />
<Button
android:id="#+id/btnLogin3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#drawable/button"
android:drawableLeft="#drawable/balloon_overlay_close"
android:gravity="center" android:text="#string/bottonbar_redeemed"
android:layout_weight=".90"/>
</LinearLayout>
This is the result link
Can you see the end of the right side? I don't think the weight is distributed like it should, although I set a .90 weight to the third button.
What do you guys think?
What you can do is use android:layout_weight attribute on buttons, but you need to place them into a LinearLayout. If you need to have RelativeLayout, then you can place the LinearLayout inside it.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="1.0" >
<Button
android:text="left"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight=".30" />
<Button
android:text="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight=".40" />
<Button
android:text="right"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight=".30" />
</LinearLayout>
If you could do it in xml itself, I wouldn't recommend doing it programmatically. You can add something like :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center" style="android:style/ButtonBar" >
<Button android:text="Ok" android:id="#+id/bookOkBtn"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_marginRight="10dp" />
<Button android:text="Return" android:id="#+id/bookReturnBtn"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
Making android:layout_width="0" & android:layout_weight=1 uses all the available space width wise.
If you want height-wise also, then specify dip in android:layout_height="100dip" and check your desired height.
If this also doesn't fit your needs and dynamic setting is only the solution, then have added Answer for that too.
UPDATED :
Have a look at this code :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:orientation="horizontal" android:gravity="center"
android:layout_marginTop="15dp" style="android:style/ButtonBar">
<Button android:id="#+id/browseAddBtn" android:text="Add" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginRight="10dp" />
<Button android:id="#+id/browseViewBtn" android:text="Edit" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginRight="10dp" />
<Button android:id="#+id/browseDelBtn" android:text="Delete" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginRight="10dp" />
<Button android:id="#+id/browseReturnBtn" android:text="Return" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" />
</LinearLayout>
RESULTS
Image shows results of above code and also on changing the layout_weight as mentioned in file respectively. My parent layout is also LinearLayout & has no padding or margins set.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
This sounds more like a case for a LinearLayout than a RelativeLayout. If you specify a LinearLayout as the parent with a total layout weight of 3 and have each of your Buttons specify a layout weight of 1 they should end up precisely as you desire on any screen.
I had same situation where I had to set sizes of buttons based on screen sizes and density. I calculate the size of buttons based on the actual space for the application I get.
I would suggest you to use LinearLAyout instead of RelativeLayout, but as you have some experience, you must be aware of the differences and ease of usage with Linear rather than Relative layouts.
In my XML I have root LinearLayout & 2 inner LinearLayout (row1 & row2). Each row has 3 butttons.
In my onCreate I get above 3 of them using findViewById.
Using the LayoutParams and the screen's size and densty, I calcualte the size of buttons and text sizes.
Algorithm :
Get Screen height & Width
Deduct the padding & margins you use
Deduct height of Title bar
Now, you have got your full available space. Divide it horizontally & vertically as you need
This will give you size of 1 button
Set the same size to all buttons
Based on buttons width & height & density figure out the text size
You can start up with this, if you need help I will be there. Feel free to ask.
UPDATED : CODE ADDED :-
These methods are added in a class named "Utility" and made static to access the methods in whole project easily :
public static ScreenInfo scrnInfo = new ScreenInfo();
public static void calculateChildSize(LinearLayout root, LinearLayout.LayoutParams row1Params, LinearLayout.LayoutParams row2Params, DisplayMetrics metrics) {
int height, width;
int childWidth, childHeight;
//gets screen dimensions
height = metrics.heightPixels; //480
width = metrics.widthPixels; //320
scrnInfo.setScreenHeight(height);
scrnInfo.setScreenWidth(width);
//Log.i("MY", "Actual Screen Height = " + height + " Width = " + width);
// Get height/width after deducting Padding of all 4 sides
height = height - (root.getPaddingBottom() + root.getPaddingTop()); // 480-70
width = width - (root.getPaddingLeft() + root.getPaddingRight()); // 320-70
//Log.i(TAG, "Actual Area after Padding W = " + width + " H = " + height);
int bottomMargin = row1Params.bottomMargin; //15
bottomMargin = (row1Params.bottomMargin %2) == 0 ? row1Params.bottomMargin : row1Params.bottomMargin+1;
// Actual Height/Width of child
childWidth = (int)(width);
childHeight = (int)(height /2) - (bottomMargin/2);
childHeight -= scrnInfo.getTitleBarHeight();
scrnInfo.setRowHeight(childHeight);
row1Params.height = childHeight;
row1Params.width = childWidth;
row1Params.bottomMargin = (bottomMargin/2);
row2Params.height = childHeight;
row2Params.width = childWidth;
row2Params.topMargin = (bottomMargin/2);
scrnInfo.setChild1LayoutParams(row1Params);
scrnInfo.setChild2LayoutParams(row2Params);
calcTileWidthHeight(childWidth);
calcTileTextSize();
//android.util.Log.i(TAG, "Child W = " + childWidth + " H = " + childHeight + " Tile W = " + scrnInfo.getTileWidth() + " Tile Text Size = " + getTileTextSize());
return;
}
public static void calcTileWidthHeight(int childWidth) {
int margin = 8;
scrnInfo.setTileWidth(((childWidth/3)-margin));
}
public static void findTitleBarHeight(Window win) {
Rect rect = new Rect();
win.getDecorView().getWindowVisibleDisplayFrame(rect);
int statusHeight = rect.top;
int contentViewTop = win.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int titleHeight = contentViewTop - statusHeight;
scrnInfo.setTitleBarHeight(titleHeight); // SET TitleBarHeight
//Log.i(Utility.TAG, "titleHeight = " + titleHeight + " statusHeight = " + statusHeight + " contentViewTop = " + contentViewTop);
return;
}
public static void calcTileTextSize() {
// current is 11 on W = 75 => its small
int w = scrnInfo.getTileWidth();
float ts = 11f;
if (w >= 51 && w <= 70) // Port LDPI W - 54 H -98
ts = 15f;
// Screen 320 * 480 Medium dense
else if (w >= 71 && w <= 80) // Port MDPI
ts = 13f;
else if (w >= 81 && w <= 110) // Land LDPI W - 81 H - 58
ts = 15f;
else if (w >= 111 && w <= 220) // Landscape - Perfect
ts = 18f;
else if (w >= 221 && w <= 250)
ts = 20f;
setTileTextSize(ts);
}
ScreenInfo class contains setters/getters for following members :
public class ScreenInfo {
private int titleBarHeight, screenHeight, screenWidth;
private int rowHeight;
private LinearLayout.LayoutParams child1LayoutParams, child2LayoutParams;
private int _6tiles_Width; // Width of a Tile where 3 tiles on 2rows r shown
NOW comes actual implementation in Activity :
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.datapage);
root = (LinearLayout) findViewById(R.id.dataroot);
row1 = (LinearLayout) findViewById(R.layout.datarow1);
row2 = (LinearLayout) findViewById(R.layout.datarow2);
btn1 = (Button) findViewById(R.id.relationBtn);
btn2 = (Button) findViewById(R.id.productBtn);
btn3 = (Button) findViewById(R.id.bankBtn);
btn4 = (Button) findViewById(R.id.locationBtn);
btn5 = (Button) findViewById(R.id.curencyBtn);
btn6 = (Button) findViewById(R.id.dataReturnBtn);
root.post(new Runnable() {
public void run() {
Utility.findTitleBarHeight(getWindow());
// CALCULATE THE SIZE OF INNER LAYOUTS
//calculateChildSize();
LinearLayout.LayoutParams row1Params = (android.widget.LinearLayout.LayoutParams) row1.getLayoutParams();
LinearLayout.LayoutParams row2Params = (android.widget.LinearLayout.LayoutParams) row2.getLayoutParams();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Utility.calculateChildSize(root, row1Params, row2Params, metrics);
row1.setLayoutParams(Utility.scrnInfo.getChild1LayoutParams());
row2.setLayoutParams(Utility.scrnInfo.getChild2LayoutParams());
RefreshComponents();
}
});
}
protected void RefreshComponents() {
// Set background of the root
root.setBackgroundColor(Utility.getBackgroundColor());
// set Gradient Colors & text color of all buttons
RefreshGradientButtons();
}
protected void RefreshGradientButtons() {
GradientDrawable btnGradient = Utility.getButtonDrawable();
int i = -1;
for(i=0; i < row1.getChildCount(); i++) {
Button btn = (Button)row1.getChildAt(i);
btn.setBackgroundDrawable(btnGradient);
btn.setTextColor(Utility.getTextColor());
btn.setTextSize(Utility.getTileTextSize());
}
for(i=0; i < row2.getChildCount(); i++) {
Button btn = (Button)row2.getChildAt(i);
btn.setBackgroundDrawable(btnGradient);
btn.setTextColor(Utility.getTextColor());
btn.setTextSize(Utility.getTileTextSize());
}
}
Whenever screen is changed from Portrait to Landscape or vice-versa OR if at all density is changed at runtime, onCeate is called every such time. Hence this code is added in onCreate(), so the calculations and settings can be appropriately on spot.
In my app, this works like a charm in versions from 1.5 to 3.2 and all density's.
You will have to make changes for your requirement accordingly. As your design is just 3 buttons in a row, while my design for the above code is like Tiles on screen. 2 rows n 3 buttons in each row. I have kept the logs I had added as it will help you to debug and figure out your solution.
This will work 100%.
*I would still recommend to give a try to : just create a new xml and in LinearLayout as parent, add your LinearLayout of buttons and see. Does that work ? While executing try it in both the modes. In your LinearLayout try removing weightSum. According to me, this requirement should be achieved in xml itself. *
BEST LUCK.