The following two layout files produce different results:
<?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:gravity="center">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<View
android:id="#+id/box"
android:background="#ff0000"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="#+id/next_box" />
<View
android:id="#+id/next_box"
android:background="#0000ff"
android:layout_width="60dp"
android:layout_alignParentRight="true"
android:layout_height="30dp"
/>
</RelativeLayout>
</LinearLayout>
Result:
<?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:gravity="center">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<View
android:id="#+id/box"
android:background="#ff0000"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
/>
<View
android:id="#+id/next_box"
android:background="#0000ff"
android:layout_width="60dp"
android:layout_alignParentRight="true"
android:layout_height="30dp"
android:layout_toRightOf="#+id/box"
/>
</RelativeLayout>
</LinearLayout>
Result:
Both layouts are trying to describe the same constraints. Namely, the red rectangle should touch the left edge of the parent, the blue rectangle should touch the right edge of the parent, and they should appear next to each other horizontally. The only difference is whether you specify the "next to" constraint on the red rectangle or the blue rectangle. I figured out the reason which has to do with the measure resolution order generated by forming a dependency graph of the constraints, but I only figured it out through reading RelativeLayout's source code, and I couldn't find any documentation / notes regarding this behavior. Since RelativeLayout must be a commonly used layout component, is there a more intuitive explanation for this behavior, or is there some part of documentation that I am missing?
Although both seem to describe the same constraints, they actually don't. The difference is that one says, red must sit next to blue, while the other says blue must sit next to red. One means that where ever red goes blue must follow, the other says, where ever blue goes red must follow, and they both want to go to different places.
In the first instance, red box depends on the blue box, so the blue box gets constructed first. The blue box has a width of 60dp, so a 60dp blue box is constructed first and aligned right. Then comes the red box, which has a constraint to sit next to the blue box. Width 0 is ignore because it needs to sit next to 60dp blue and align left.
In the second instance, blue box depends on the red box, so the red box gets constructed first. The red box says it wants 0dp and align left, so it can't be seen. Then comes the blue box which needs to sit next to invisible red and align right, thus occupying the entire space, its width ignored.
Hope this makes sense :)
All this parameters defined in:
android.widget.RelativeLayout
private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
RelativeLayout.LayoutParams anchorParams;
// VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
// left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
// wants to the right
// left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
// wants to the left
// left=10, right=20 means the left and right ends are both fixed
childParams.mLeft = VALUE_NOT_SET;
childParams.mRight = VALUE_NOT_SET;
anchorParams = getRelatedViewParams(rules, LEFT_OF);
if (anchorParams != null) {
childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
childParams.rightMargin);
} else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
anchorParams = getRelatedViewParams(rules, RIGHT_OF);
if (anchorParams != null) {
childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
childParams.leftMargin);
} else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
if (anchorParams != null) {
childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
} else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
if (anchorParams != null) {
childParams.mRight = anchorParams.mRight - childParams.rightMargin;
} else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
if (0 != rules[ALIGN_PARENT_LEFT]) {
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
}
if (0 != rules[ALIGN_PARENT_RIGHT]) {
if (myWidth >= 0) {
childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
}
}
}
The view left and right edges ( childParams.mLeft, childParams.mRight) calculations based on anchor view parameters (anchorParams). From this code childParams.mRight edge of the view defined by LEFT_OF (android:layout_toLeftOf) can be recalculated by ALIGN_RIGHT (android:layout_alignRight) or ALIGN_PARENT_RIGHT (android:layout_alignParentRight). Here is explanation why 0-width red view become more than 0.
<View
android:id="#+id/box"
android:background="#ff0000"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="#+id/next_box"/>
Right edge of this view defined by LEFT_OF:
childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
childParams.rightMargin);
In this case anchor view is:
<View
android:id="#+id/next_box"
android:background="#0000ff"
android:layout_width="60dp"
android:layout_alignParentRight="true"
android:layout_height="30dp"
/>
the left edge of this view 60dp from the right side of the screen margings not defined => childParams.mRight = screen_width - 60dp
Left edge of this view defined by ALIGN_PARENT_LEFT:
childParams.mLeft = mPaddingLeft + childParams.leftMargin;
the left edge of this view left edge of anchor view is 0 because android:layout_alignParentLeft="true" and margins not defined => childParams.mLeft = 0
the same calculation can be done for the second example:
childParams.mRight = screen_width
childParams.mLeft = 0
Related
I have a layout that looks like this:
and I am trying to expand clickable area for the green view. The code that I use for this purpose is:
public static void setTouchDelegate(View view, float dimen) {
final View parent = (View) view.getParent();
parent.post( () -> {
final Rect delegateArea = new Rect();
view.getHitRect(delegateArea);
delegateArea.right += dimen;
delegateArea.left -= dimen;
delegateArea.bottom += dimen;
delegateArea.top -= dimen;
parent.setTouchDelegate( new TouchDelegate( delegateArea , view));
});
}
The red area is the parent of the green view. The problem is that regardless how large TouchDelegate I am trying to set it always applies only within the parent, the red area. I can cover the whole red area to be clickable but I can't expand the clickable area into the blue area.
My assumption is that this is not a problem in my code but normal behaviour of TouchDelegate.
The question is: how can I set clickable area for the green view that looks like the purple area on the following preview ?
// edit:
This is layout of the item - the red rectangle with text and the green ImageButton:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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:background="#android:color/holo_red_dark"
android:foreground="?attr/selectableItemBackgroundBorderless"
android:gravity="center_vertical"
android:paddingBottom="3dp"
android:paddingLeft="10dp"
android:paddingRight="4dp"
android:paddingTop="3dp">
<TextView
android:id="#+id/item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="18dp"
android:textColor="#android:color/black"
android:textSize="12sp"
android:textStyle="bold"
tools:text="Test" />
<ImageButton
android:id="#+id/item_green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:background="#android:color/transparent"
android:src="#android:color/holo_green_light" />
</FrameLayout>
Basically you need to set the delegateArea relative to the view' ancestor, and set the TouchDelegate on this ancestor.
In your image the target ancestor is the view with the blue background.
fun getRelativeLeft(view: View?, ancestor: View) : Int {
if (view == null)
return 0
return if (view.parent == ancestor)
view.left
else
view.left + getRelativeLeft(view.parent as? View, ancestor)
}
fun getRelativeTop(view: View?, ancestor: View) : Int {
if (view == null)
return 0
return if (view.parent == ancestor)
view.top
else
view.top + getRelativeTop(view.parent as? View, ancestor)
}
val relativeTop = Point(getRelativeLeft(view, ancestorView), getRelativeTop(view, ancestorView))
// View area in ancestor coordinates
val relativeRc = Rect(relativeTop.x, relativeTop.y, relativeTop.x + view.width, relativeTop.y + view.height)
// Increase touch area
relativeRc.inset(-offsetWidth, -offsetHeight)
// Set touch delegate on ancestorView with target view
ancestorView.touchDelegate = TouchDelegate(relativeRc, view)
Also it is better to use the modified TouchDelegate, due to a bug in the original version https://gist.github.com/patrickhammond/6d49e9ac08f96bd8d302#gistcomment-2385782
I'm trying to make an ImageView follow another ImageView, by changing it's coordinates. My movement method is that:
public void move(double zx, double zy) {
if (zx < a.getLeft()) {
if (a.getLeft() - zx > 0) {
a.setLeft(a.getLeft() - 1);
} else
a.setLeft(a.getLeft()+1);
}
if (zx > a.getLeft()) {
if (a.getLeft() - zx > 0) {
a.setLeft(a.getLeft() - 1);
} else
a.setLeft(a.getLeft() - 1);
}
if (zy > a.getTop()) {
if (a.getTop() - zy > 0) {
a.setTop(a.getTop() - 1);
} else
a.setTop(a.getTop() + 1);;
}
if (zy < a.getTop()) {
if (a.getTop() - zy > 0) {
a.setTop(a.getTop() - 1);
} else
a.setTop(a.getTop() + 1);
}
}
OnCreate:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fase);
a = (ImageView)findViewById(R.id.a);
b = (ImageView)findViewById(R.id.b);
move(b.getLeft(), b.getTop());
}
"a" and "b" are the ImageViews, and a should follow b. I declared both ImageViews correctly, and the method should work. Instead of it, the ImageView "a" keep in the same position since the application starts. What is wrong? Is the method move declared at the wrong place (onCreate) ? I also have tried to just set the vertical position of "a" in another part of the screen, for testing if it would work. But it didn't (and according to the logCat it's vertical position had changed). The tutorials I found about moving images are based on dragging them, but I haven't still found anything about another kind of movement, like this.
There is my layout (activity_fase.xml) :
<RelativeLayout 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:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context="thegame.app.Fase"
android:background="#drawable/seapaint">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/b"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:src="#drawable/b"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/a"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:src="#drawable/a"
android:focusable="true"
android:clickable="false"
android:layout_marginBottom="79dp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imageView2"
android:layout_above="#+id/b"
android:layout_alignLeft="#+id/a"
android:layout_alignStart="#+id/key"
android:layout_marginBottom="67dp"
android:src="#drawable/key"
android:layout_marginLeft="50dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Small Text"
android:id="#+id/points"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
I'm new on android developing, and I don't know the difference between relative layout and linear layout.
PROBLEM SOLVED
It's possible to change an ImageView's position for any region of the screen by setting a relativeLayout by code, and adding to it's parameters the leftMargin and the topMargin. Example:
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(80,80);
// 80 is just a number of my choice. There are many possibilities
params.leftMargin = 200;
params.topMargin = 400;
a.setLayoutParams(params);
Now the imageView's coordinates are (200,400). A successive change can be done. If you creat a timer that is called each 0.05 seconds and a task that adds 1 to the leftMargin and topMargin, the image's position will be successfully changed.
I have an edit text in my page and I need to adjust screen to show the values typing in it.
I have action bar in my page and I have given android:windowSoftInputMode="adjustPan|stateAlwaysHidden" .
When I run the app, I touch on my edit text and keyboard pops up. The edit text comes on top of the keyboard now which is right. But when I start typing this edit text goes down the keyboard.
This doesn't happen in all devices. In Samsung S3 its having this issue. How to solve this? Please help me out.
Edit:
My edit ext code is as below:
<EditText
android:id="#+id/name"
android:layout_width="300dp"
android:layout_height="50dp"
android:layout_below="#+id/empId"
android:layout_marginTop="10dp"
android:ellipsize="end"
android:hint="#string/name"
android:inputType="text"
android:maxLines="1"
android:padding="5dp"
android:textColor="#android:color/black" />
I had similar problem.what i did was , iadded a linear layout below the root layout like this
<LinearLayout
android:orientation="vertical"
android:id="#+id/footer_for_emoticons"
android:layout_width="match_parent"
android:layout_height="#dimen/keyboard_height"
android:visibility="gone" >
</LinearLayout>
Then in the activity
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView()
.getHeight() - (r.bottom - r.top);
if (lastDiff == heightDiff)
return;
lastDiff = heightDiff;
Log.i("aerfin","arefin "+lastDiff);
if (heightDiff > 100) { //Assume keyboard is present
emoticonsCover.setVisibility( View.VISIBLE );
flag2 = 0;
} else {
if (flag == false)
flag2 = 1;
emoticonsCover.setVisibility( View.GONE );
}
}
});
The linear layout will provide the gap and in manifest add android:windowSoftInputMode="adjustPan" to the activity.Check whether it helps
I have two textviews like this:
=======================
= TextView1 TextView2 =
=======================
And I would like to detect when the textviews are too long such that they are displayed like this:
=======================
= TextView1 =
= TextView2 =
=======================
currently for longer text, it is displayed like this:
=======================
= TextView1 Text =
= View2 =
=======================
how can I do this, such that when the text is short the textviews are side by side and when it is too long, the second textview is not splitted but moved to the second line?
I tought at a solution to create a single textview and build the text according to length (text 1 + padding + text 2 if short, and text 1 + "\n" + text 2 if long) but I do not like this solution.
Is there any way to detect if the second text will be split such that to change the orientation of the layout that contains the textviews from horizontal cu vertical?
UPDATE
This is my xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<TextView
android:id="#+id/my_text_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="10dp"
android:text="#string/text1"/>
<TextView
android:id="#+id/my_text_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="5dp"/>
</LinearLayout>
I have found a better solution. Changed my textviews into autoresizable textviews (more info here)
Also, each textview is in a separate layout, to make sure both textviews are resized to the same value.
My xml looks like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/value_linear_layout"
android:gravity="center">
<LinearLayout
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content">
<com.mihaela.view.AutoResizeTextView
android:id="#+id/my_text_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content">
<com.mihaela.view.AutoResizeTextView
android:id="#+id/my_text_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
and I have implemented the OnTextResizeListener from AutoResizeTextView to do this:
public class TextWidthResizeListener implements OnTextResizeListener {
#Override
public void onTextResize(TextView textView, float oldSize, float newSize) {
TextPaint paint = textView.getPaint();
if (paint.measureText(textView.getText().toString()) > (valueLinearLayout.getWidth() / 2)){
valueLinearLayout.setOrientation(LinearLayout.VERTICAL);
}
}
}
where valueLinearLayout is:
valueLinearLayout = (LinearLayout)findViewById(R.id.value_linear_layout);
This solution best fits for me, as the textviews are dimensioned when they are side by side until a minimum size. When the minimum size is reached, and the text still does not fit, the textviews will be aligned one under another.
Also, this idea with the listener can be applied to non-resizable textviews also.
I will set this answer as the correct one.
You should use a single, multi-line TextView and set the text as follows :
mTextView.setText(text1+" "+text2);
or
mTextView.setText(text1+"\n"+text2);
depending on your particular needs.
EDIT: you could specify your text in html, and then use Html.fromHtml(htmlString) and display this text in your TextView.
String text1 ="<font color=\"red\">This is some text!</font>"
String text2="<font color=\"blue\">This is some other text!</font>"
textView.setText(Html.fromHtml(text1+ "<br/>"+ text2);
I made a slightly different version of the accepted answer. I did not alter my layout xml in any way and did not use onTextResize() or AutoResizeTextView as that seemed an overkill for my situation. I needed my LinearLayout to switch from Horizontal orientation to Vertical orientation if the device's language setting caused a long string to be used.
Layout
<LinearLayout
android:id="#+id/customer_care_bottom_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:layout_marginBottom="#dimen/lmargin_bottom_10">
<TextView
android:id="#+id/customer_care_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/CUSTOMER_CARE_TITLE" />
<TextView
android:id="#+id/customer_care_number_information"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/CUSTOMER_CARE_INFORMATION"/>
</LinearLayout>
Java
private void setCustomerServiceLayoutOrientationBasedOnTextWidth() {
TextPaint paint = customer_care_number_text.getPaint();
TextView tvCustomerCareTitle = (TextView) findViewById(R.id.customer_care_title);
TextView tvCustomerCareInformation = (TextView) findViewById(R.id.customer_care_information);
int halfCustomerServiceLayoutWidth = getScreenWidth() / 2;
boolean isCustomerCareTitleTooLong = paint.measureText(tvCustomerCareTitle.getText().toString()) > customerServiceLayoutWidth;
boolean isCustomerCareInformationTooLong = paint.measureText(tvCustomerCareInformation.getText().toString) > customerServiceLayoutWidth;
if (isCustomerCareTitleTooLong || isCustomerCareInformationTooLong) {
LinearLayout llCustomerCareBottom = (LinearLayout) findViewById(R.id.customer_care_bottom_layout);
llCustomerCareBottom.setOrientation(LinearLayout.VERTICAL);
}
}
private int getScreenWidth() {
int screenWidth;Display display = getWindowManager().getDefaultDisplay();
if (Build.VERSION.SDK_INT < 13) {
screenWidth = display.getWidth();
} else {
Point point = new Point();
display.getSize(point);
screenWidth = point.x;
}
return screenWidth;
}
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.