Height of TextView is greater than the screen - android

I have a simple layout with a TextView in it. I am trying to estimate its dimensions (height and width) at run-time. The code I use is as follows:
Log.v(this.toString(), "Screen width = " + this.getResources().getDisplayMetrics().widthPixels + " and height = " + this.getResources().getDisplayMetrics().heightPixels);
Log.v(this.toString(), "TV width = " + tv.getWidth() + " and height = " + tv.getHeight());
Log.v(this.toString(), "TV raw width = " + tv.getMeasuredWidth() + " and raw height = " + tv.getMeasuredHeight());
Log.v(this.toString(), "TV width from display manager = " + getWindowManager().getDefaultDisplay().getWidth() + " and height = " + getWindowManager().getDefaultDisplay().getHeight());
tv.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
Log.v(this.toString(), "TV width from measurespec = " + tv.getMeasuredWidth() + " and height = " + tv.getMeasuredHeight());
tv.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#SuppressWarnings("deprecation")
#Override
public void onGlobalLayout() {
//get view height and width here.
Log.v(this.toString(), "Inside layout width and height measurer.");
Log.v(this.toString(), "In layout listener, TV width = " + tv.getWidth() + " and height = " + tv.getHeight());
tv.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
Log.v(this.toString(), "In layout listener, TV raw width = " + tv.getMeasuredWidth() + " and raw height = " + tv.getMeasuredHeight());
Log.v(this.toString(), "TV width from display manager = " + getWindowManager().getDefaultDisplay().getWidth() + " and height = " + getWindowManager().getDefaultDisplay().getHeight());
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
tv.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
The phone I am testing it on is a Samsung Galaxy S Duos. In the LogCat, the height of the view is returned as 1058 pixels, which is greater than the phone's height of 800 pixels! The LogCat output is:
01-12 12:35:38.749: V/com.sriram.hellotts.HelloTTS#415f3ac8(18595): Screen width = 480 and height = 800
01-12 12:35:38.749: V/com.sriram.hellotts.HelloTTS#415f3ac8(18595): TV width = 0 and height = 0
01-12 12:35:38.749: V/com.sriram.hellotts.HelloTTS#415f3ac8(18595): TV raw width = 0 and raw height = 0
01-12 12:35:38.749: V/com.sriram.hellotts.HelloTTS#415f3ac8(18595): TV width from display manager = 480 and height = 800
01-12 12:35:38.759: V/com.sriram.hellotts.HelloTTS#415f3ac8(18595): TV width from measurespec = 48 and height = 32
01-12 12:35:38.929: V/com.sriram.hellotts.HelloTTS$1#41068c38(18595): In layout listener, TV width = 480 and height = 1112
01-12 12:35:38.959: V/com.sriram.hellotts.HelloTTS$1#41068c38(18595): In layout listener, TV raw width = 8518 and raw height = 59
01-12 12:35:38.959: V/com.sriram.hellotts.HelloTTS$1#41068c38(18595): TV width from display manager = 480 and height = 800
The layout is:
<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"
tools:context=".HelloTTS" >
<!-- Footer aligned with the bottom -->
<include
android:id="#+id/footerLayout"
layout="#layout/footer_ehtv_layout" />
<include
android:id="#+id/headerLayout"
layout="#layout/header_ehtv_layout" />
<SeekBar
android:id="#+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true" />
<Button
android:id="#+id/submitText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_above="#id/seekbar"
android:text="#string/submit" />
<Button
android:id="#+id/nextPage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/submitText"
android:layout_alignBaseline="#id/submitText"
android:text="Next Page" />
<Button
android:id="#+id/previousPage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#id/submitText"
android:layout_toLeftOf="#id/submitText"
android:text="Prev. Page" />
<ScrollView
android:id="#+id/scrl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/submitText"
android:layout_below="#id/headerLayout">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tv"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:inputType="none"
android:clickable="true"
android:longClickable="true"
android:textIsSelectable="true"
android:scrollbars="vertical"
android:textSize="15sp" />
</RelativeLayout>
</ScrollView>
</RelativeLayout>
My question(s):
1. How is this even possible?
2. What else can I do to ensure that the TextView height and width are returned correctly, that is, after the layout has been drawn.
Further, the TextView will only be displayed in portrait mode.

I have tried this sample.
Display display = this.getWindowManager().getDefaultDisplay();
int height=display.getHeight(); //you can get height
int weight=display.getWidth(); //you can get width
if(display.getHeight()==480&&display.getWidth()==320){
}
else if(display.getHeight()==800&&display.getWidth()==480){
}
else if(display.getHeight()==320&&display.getWidth()==240){
}

Related

ObjectAnimator, Changing "ScaleX" or "Left," but text doesn't stay properly centered

I need to animate three elements to show additional info when the user clicks on a button. I have a label (TextView tvAvailableToPair) that slides out from under a button (Button bSearchForDevices)--both of which start with a width of fill_parent--while it simultaneously slides up. I have it changing perfectly, but it isn't animated: I've achieved that via SetLayoutParams:
bSearchForDevices.setText(R.string.bSearchForDevicesDiscoveryStartedText);
FrameLayout.LayoutParams lp_bSFD =
(FrameLayout.LayoutParams) bSearchForDevices.getLayoutParams();
int mWidthOfBSearchForDevices = lp_bSFD.width =
(int) Math.round(fMeasureScreenWidth * 0.4);
int mLeftMarginOfBSearchForDevices = lp_bSFD.leftMargin =
Math.round(fMeasureScreenWidth - (fHorizontalMargin + lp_bSFD.width));
bSearchForDevices.setLayoutParams(lp_bSFD);
lp_bSFD = (FrameLayout.LayoutParams) tvAvailableToPair.getLayoutParams();
lp_bSFD.width = mLeftMarginOfBSearchForDevices;
lp_bSFD.leftMargin = 0;
tvAvailableToPair.setLayoutParams(lp_bSFD);
ViewGroup.LayoutParams lp = llAvailableToPair.getLayoutParams();
lp.height = Math.round(getResources().getDimensionPixelSize(
R.dimen.llAvailableToPair_height_expanded) * MainActivity.displayMetrics.density);
I learned a FrameLayout allows you to place elements on different Z-axis values (on top of or below another element; to cover or be covered over by another element). Here's my xml for this activity:
<RelativeLayout
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"
tools:context=".MainActivity"
android:id="#+id/rlDeviceListLayout"
android:background="#android:color/holo_blue_dark"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true"
android:animateLayoutChanges="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior" >
<android.support.design.widget.AppBarLayout
android:id="#+id/ablToolbarLayout"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_alignParentTop="true"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<LinearLayout>
<!-- Existing devices for user to select -->
</LinearLayout>
<!-- This is what needs to be animated up once user presses the button -->
<LinearLayout
android:id="#+id/llAvailableToPairLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="#dimen/llAvailableToPair_height_collapsed"
android:layout_alignParentBottom="true"
android:paddingTop="4dp">
<!-- This Layout is used to stack the button over the textview -->
<FrameLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginBottom="4dp">
<!-- We'll change the width of this programmatically so that it appears to
elegantly slide to the left -->
<TextView
android:id="#+id/tvAvailableToPair"
android:text="#string/tvAvailableToPair"
android:layout_width="fill_parent"
android:layout_height="#dimen/bSearchForDevices_height"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:inputType="none"
android:gravity="center"
android:foregroundGravity="left"
android:textSize="16sp"
android:textColor="#ffffff" />
<!-- We'll change the text, width and left of this so that it reveals the
textview under it. It needs to appear as if it were elegantly squeezed
toward the right -->
<Button
android:id="#+id/bSearchForDevices"
android:text="#string/bSearchForDevicesPreliminaryText"
android:layout_width="fill_parent"
android:layout_height="#dimen/bSearchForDevices_height"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:foregroundGravity="right"
android:visibility="visible"
android:theme="#style/Base.ThemeOverlay.AppCompat.Dark"
android:gravity="center" />
</FrameLayout>
<ListView
android:id="#+id/lvAvailableToPair"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:visibility="visible" />
</LinearLayout>
</RelativeLayout>
Its the last three elements I'm changing. Using SetLayoutParams keeps the text centered (and I shorten the text on the button to allow for smaller screens and the label -- I'll need to animate that as well next, possibly using TextSwitcher Animate Text Using TextSwitcher In Android) after the change. Moving on to getting those changes to animate, I had to use a different approach:
bSearchForDevices.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FrameLayout.LayoutParams lpTVATP = (FrameLayout.LayoutParams) tvAvailableToPair.getLayoutParams();
Paint paint = new Paint();
AnimatorSet RevealAvailableDevices = new AnimatorSet();
float fMeasuretvAvailableToPairTextWidth = paint.measureText(tvAvailableToPair.getText().toString()); // Function returns dp, not pixels
float fMeasurebSearchForDevicesTextWidth = paint.measureText(bSearchForDevices.getText().toString());
float fMeasureScreenWidth = MainActivity.displayMetrics.widthPixels; // + 0.5f; // Extra 0.5 to simplify/speed up rounding w/o a lot of excess code (LoungeKatt, via https://stackoverflow.com/questions/1016896/get-screen-dimensions-in-pixels) and used on Google's Android docs for just this purpose # https://developer.android.com/guide/practices/screens_support.html#dips-pels
float fHorizontalMargin;
// If the screen is cramped, we'll shorten the margins:
if (fMeasuretvAvailableToPairTextWidth > (fMeasureScreenWidth * 0.5)) {
fHorizontalMargin = (getResources().getDimension(R.dimen.activity_horizontal_margin_compressed) / MainActivity.displayMetrics.density);
} else {
fHorizontalMargin = (getResources().getDimension(R.dimen.activity_horizontal_margin) / MainActivity.displayMetrics.density);
}
/* This works PERFECTLY, but isn't animated:
bSearchForDevices.setText(R.string.bSearchForDevicesDiscoveryStartedText);
FrameLayout.LayoutParams lp_bSFD = (FrameLayout.LayoutParams) bSearchForDevices.getLayoutParams();
int mWidthOfBSearchForDevices = lp_bSFD.width = (int) Math.round(fMeasureScreenWidth * 0.4);
int mLeftMarginOfBSearchForDevices = lp_bSFD.leftMargin = Math.round(fMeasureScreenWidth - (fHorizontalMargin + lp_bSFD.width));
bSearchForDevices.setLayoutParams(lp_bSFD);
lp_bSFD = (FrameLayout.LayoutParams) tvAvailableToPair.getLayoutParams();
lp_bSFD.width = mLeftMarginOfBSearchForDevices;
lp_bSFD.leftMargin = 0;
tvAvailableToPair.setLayoutParams(lp_bSFD);
ViewGroup.LayoutParams lp = llAvailableToPair.getLayoutParams();
lp.height = Math.round(getResources().getDimensionPixelSize(R.dimen.llAvailableToPair_height_expanded) * MainActivity.displayMetrics.density);
*/
float fWidthOfbSearchForDevices = (float) ((fMeasureScreenWidth * 0.4));
float fWidthOftvATP = (fMeasureScreenWidth - ((fWidthOfbSearchForDevices * 1) + (fHorizontalMargin * 2)));
float fXOfbSearchForDevices = (fMeasureScreenWidth - (fWidthOfbSearchForDevices + (1 * fHorizontalMargin)));
Log.d(TAG, "...\nfMeasureScreenWidth = " + fMeasureScreenWidth + "\nfWidthOfbSearchForDevices = " + fWidthOfbSearchForDevices + "\nfHorizontalMargin = " + fHorizontalMargin +
"\nbSearchForDevices.getX() = " + bSearchForDevices.getX() + "\n (fXOfbSearchForDevices) X is going to be " + fXOfbSearchForDevices);
ObjectAnimator aniButtonMove = ObjectAnimator.ofInt(bSearchForDevices, "Left", (int) fXOfbSearchForDevices);
bSearchForDevices.setPivotX(fXOfbSearchForDevices + (fWidthOfbSearchForDevices / 2)); // No effect: To be in the center of where the button will end up at.
ObjectAnimator aniTextViewResize = ObjectAnimator.ofInt(tvAvailableToPair, "Right", (int) fXOfbSearchForDevices);
Log.d(TAG, "tvAvailableToPair's width = " + tvAvailableToPair.getWidth() + " tvAvailableToPair is supposed to be " + fWidthOftvATP + "\nWidth of bSearchForDevices = " + bSearchForDevices.getWidth());
RevealAvailableDevices.play(aniTextViewResize).with(aniButtonMove);
RevealAvailableDevices.start();
This produces an animation that correctly moves the bounds of the textview and button, but doesn't center the text to the new bounds (edges/left and right coordinates) of those elements: my button now shows a lot of leading empty space just before it shows S, E and half of A of "Search For Devices;" in other words, it's clipped. I need it kept centered (as is when using SetLayoutParams). The same applies to the textview: the last 5 letters of "Available Devices" get clipped off.
Is there a better way to create this animation that has three elements changing shape (and preferably changing the button's text as well)? I've tried ScaleX but am unable to get it to sync correctly as it pivots at the center without sliding to the right side, nor the textview to slide while it shrinks...maybe ScaleX but change the pivot to the left edge... Or maybe a PropertyValuesHolder?
3/15/2016 Update:
Here's what I've got: https://www.dropbox.com/s/n14hed6xcknfh9b/SS_20160315_135938.mp4?dl=0
And what I need it to look like post animation

Android ScrollView height not changing

I have a layout with the following XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:id="#+id/myScrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true" >
<LinearLayout
android:id="#+id/myEditLinearLayout"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<EditText
android:id="#+id/myEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dip"
android:paddingRight="8dip"
android:maxLines="300"
android:scrollHorizontally="false"
android:hint="Type notes here..."
android:inputType="textNoSuggestions|textMultiLine"
android:layout_gravity="center_horizontal|center_vertical"
android:gravity="left"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
</ScrollView>
<RelativeLayout
android:id="#+id/layoutKbd"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<android.inputmethodservice.KeyboardView
android:id="#+id/myKeyboardView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:keyPreviewLayout ="#layout/kbdpreview"
android:visibility="gone" />
</RelativeLayout>
</RelativeLayout>
The top view is RelativeLayout with a ScrollView for the main content (which contains an EditText) and custom keyboard view. The custom keyboard pops up when the user clicks on the EditText.
It works perfectly for most part, but if the text being entered in the EditText is large, it gets covered by the custom keyboard view. So at that time, I need to adjust the ScrollView's height. I did it in the following manner:
// Activity's onCreate implementation
public void onCreate(Bundle savedInstanceState)
{
...
EditText editText = (EditText) findViewById(R.id.myEditLinearLayout);
editText.addTextChangedListener(m_tw);
...
}
private TextWatcher m_tw = new TextWatcher()
{
#Override
public void afterTextChanged(Editable s)
{
// This function returns if the custom keyboard is visible
if (m_customKeyboard.isCustomKeyboardVisible())
{
int kbdHeight = m_customKeyboard.mKeyboardView.getHeight();
int editTextBottom = m_editText.getBottom();
ScrollView scrollView = (ScrollView) findViewById(R.id.myScrollview);
Rect scrollBounds = new Rect();
Log.d(TAG, "Setting scrollview height = " + (editTextBottom + kbdHeight + 20));
scrollView.getHitRect(scrollBounds);
Log.d(TAG, "Old scroll bounds: left = " + scrollBounds.left + " right = " + scrollBounds.right + " top = " + scrollBounds.top + " bottom = " + scrollBounds.bottom);
scrollView.getLayoutParams().height = editTextBottom + kbdHeight + 20;
Log.d(TAG, "Setting scrollview height = " + (editTextBottom + kbdHeight + 20));
scrollView.getHitRect(scrollBounds);
Log.d(TAG, "New scroll bounds: left = " + scrollBounds.left + " right = " + scrollBounds.right + " top = " + scrollBounds.top + " bottom = " + scrollBounds.bottom);
}
}
};
The above does not work. It shows the same value for scrollBounds.bottom, before and after setting scrollView height.
What am I missing?
Put the property
android:layout_above="#+id/yourKeyboardLayout"
in your scrollview.
You could try putting android:windowSoftInputMode="adjustPan" in your manifest. If you don't like the result, perhaps you can use some other pan.
Add this
android:windowSoftInputMode="adjustPan"
in your manifest where activity is declare and you can also add in scroll view
If you want to change scrollview's LayoutParams, you should use the following code in you example:
ViewGroup.LayoutParams lp = scrollView.getLayoutParams();
lp.height = editTextBottom + kbdHeight + 20;
scrollview.setLayoutParams(lp);

How to get the left margin of a centered view in android?

I have view that is centered in xml by android:layout_centerHorizontal="true". How do I get the leftMargin of it, so I can align other views with it?
I tried using getLayoutParams on the centered view but it didn't work. It seems to report a leftMargin of 0.
I also tried to compute the leftMargin with (screenWidth-view width)/2, but it gives a slightly higher value than expected. More specifically, this is my code:
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screenWidth = size.x;
int screenHeight = size.y;
TextView button1 = (TextView) findViewById(R.id.button1);
RelativeLayout.LayoutParams params=(RelativeLayout.LayoutParams)
button1.getLayoutParams();
params.height=102;
params.width=114;
params.topMargin =0;
button1.setLayoutParams(params);
params=(RelativeLayout.LayoutParams) button1.getLayoutParams();
int leftMargin = params.leftMargin;
TextView button2 = (TextView) findViewById(R.id.button2);
params=(RelativeLayout.LayoutParams) button2.getLayoutParams();
params.height=102;
params.width=114;
params.leftMargin =leftMargin;
params.topMargin =102;
button2.setLayoutParams(params);
TextView button3 = (TextView) findViewById(R.id.button3);
params=(RelativeLayout.LayoutParams) button3.getLayoutParams();
params.height=102;
params.width=114;
params.leftMargin =(screenWidth-114)/2;
params.topMargin =204;
button3.setLayoutParams(params);
Button1 is centered in xml.
Button2 is left-aligned, not centered.
Button3 is more or less centered, but slightly to the right.
So,
1) how do I get the left margin of a view centered in xml?
2) how do I center (exactly) a view in code? Does getDefaultDisplay reports an higher width than the real screen width?
You can directly ask the layout to have the views aligned. For example,
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/centered_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Random text"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#id/centered_view"
android:layout_alignParentTop="true"
android:text="Top Button"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#id/centered_view"
android:layout_alignParentBottom="true"
android:text="Bottom Button"/>
</RelativeLayout>
EDIT:
To get the relative offset of the centered view, this might suffice:
textView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Log.d(getClass().getSimpleName(), "top: " + textView.getTop() + " left: " + textView.getLeft());
textView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
See:
View.getTop() and View.getLeft()

Programmatic Margin for LinearLayout is ignored

I have an Android layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dice_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="top" >
<FrameLayout
android:id="#+id/die_frame_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|top" >
<ImageView
android:id="#+id/die1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/dice"
android:src="#drawable/d6" />
<ImageView
android:id="#+id/die_overlay_1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.4"
android:visibility="invisible"
android:src="#drawable/gray_shape"
android:contentDescription="#string/gray_overlay" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/die_frame_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/die2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/dice"
android:src="#drawable/d2" />
<ImageView
android:id="#+id/die_overlay_2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.4"
android:visibility="invisible"
android:src="#drawable/gray_shape"
android:contentDescription="#string/gray_overlay" />
</FrameLayout>
<FrameLayout
android:id="#+id/die_frame_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/die3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/dice"
android:src="#drawable/d1" />
<ImageView
android:id="#+id/die_overlay_3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:alpha="0.4"
android:visibility="invisible"
android:src="#drawable/gray_shape"
android:contentDescription="#string/gray_overlay" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
And I am trying to programmatically take care of the positions of the images. The problem is that the result is not what I want it to be, which would be three images centered in the form of a pyramid. Die number 1 is the top, and 2 and 3 bottom left and right respectively.
FrameLayout[] frames = new FrameLayout[] {(FrameLayout) findViewById(R.id.die_frame_1),
(FrameLayout) findViewById(R.id.die_frame_2),
(FrameLayout) findViewById(R.id.die_frame_3)};
Point size = getSize();
int width = size.x;
int height = size.y;
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) frames[0].getLayoutParams();
// Width will be restricted either by height or width. Dice should take max 50% of screen
int frameWidth = (int) Math.min(width * 0.44, height * 0.5 * 0.44); // Each dice 40%
params.width = frameWidth;
params.height = frameWidth; // Square, so width equals height
// Ensure that the space between each dice is equal
int frameHeightMargin = (int) (height * 0.5 - frameWidth * 2) / 3; // Divide the remainder evenly
int frameWidthMarginOuter = (int) (width - 2 * frameWidth - frameHeightMargin) / 2; // Calculate outer
int frameWidthMarginCenter = (int) frameHeightMargin / 2; // Same spacing between dice
int frameWidthMarginUpper = (int) (width - frameWidth) / 2; // Upper only
for (int i = 0; i < frames.length; i++) {
if (i == 0) {
params.setMargins(frameWidthMarginUpper, frameHeightMargin, frameWidthMarginUpper, frameWidthMarginCenter); // Gravity is centered
} else if (i == 1) { // Left dice
params.setMargins(frameWidthMarginOuter, frameWidthMarginCenter, frameWidthMarginCenter, frameHeightMargin);
} else if (i == 2) {// Right dice
params.setMargins(frameWidthMarginCenter, frameWidthMarginCenter, frameWidthMarginOuter, frameHeightMargin);
}
frames[i].setLayoutParams(params);
}
Current result:
The code is in the onCreate method by the way. I have both tried with and without requestLayout() for the FrameLayouts and the parents.
Unless you know exactly what you are doing do not use the same LayoutParams for more than one ViewGroup. The solution is very simple. Create a new LayoutParams for each die.
FrameLayout[] frames = new FrameLayout[] {(FrameLayout) findViewById(R.id.die_frame_1),
(FrameLayout) findViewById(R.id.die_frame_2),
(FrameLayout) findViewById(R.id.die_frame_3)};
Point size = getSize();
int width = size.x;
int height = size.y;
// Width will be restricted either by height or width. Dice should take max 50% of screen
int frameWidth = (int) Math.min(width * 0.44, height * 0.5 * 0.44); // Each dice 40%
// Ensure that the space between each dice is equal
int frameHeightMargin = (int) (height * 0.5 - frameWidth * 2) / 3; // Divide the remainder evenly
int frameWidthMarginOuter = (int) (width - 2 * frameWidth - frameHeightMargin) / 2; // Calculate outer
int frameWidthMarginCenter = (int) frameHeightMargin / 2; // Same spacing between dice
int frameWidthMarginUpper = (int) (width - frameWidth) / 2; // Upper only
for (int i = 0; i < frames.length; i++) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.width = frameWidth;
params.height = frameWidth; // Square, so width equals height
if (i == 0) {
params.setMargins(frameWidthMarginUpper, frameHeightMargin, frameWidthMarginUpper, frameWidthMarginCenter); // Gravity is centered
} else if (i == 1) { // Left dice
params.setMargins(frameWidthMarginOuter, frameWidthMarginCenter, frameWidthMarginCenter, frameHeightMargin);
} else if (i == 2) {// Right dice
params.setMargins(frameWidthMarginCenter, frameWidthMarginCenter, frameWidthMarginOuter, frameHeightMargin);
}
frames[i].setLayoutParams(params);
}

Spreading gracefully buttons horizontally across a Layout

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.

Categories

Resources