Spreading gracefully buttons horizontally across a Layout - android

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.

Related

How to fill imageview's background color like a progressbar?

I have a png image something like below and I need to fill its background color according to its capacity.
For example if tank has oil %100 percent of its capacity, background should be yellow , if has %25 , background should be %25 percent yellow and %75 transparent.
Assume that this is a healthbar in a game.
this is what i have in my layout, just a simple imageview in a linearlayout.
Is there any way to achieve this using animation, clip or something ?
<ImageView
android:id="#+id/ivOilTank"
android:layout_width="0dp"
android:src="#mipmap/oilTank"
android:layout_height="match_parent"
android:layout_weight="1">
Change your layout xml to contain-
<FrameLayout
android:layout_width="150dp"
android:layout_height="wrap_content"
android:background="#android:color/black"
>
<View
android:id="#+id/percent_highlight"
android:layout_width="0dp"
android:layout_height="match_parent"
/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#mipmap/oilTank"
/>
</FrameLayout>
Now wherever you want to highlight the certain percentage of the image -
View highlight = findViewById(R.id.percent_highlight);
highlight.setBackgroundResource(<Color_id>);//Any color you want to set
ViewGroup.LayoutParams layoutParams = highlight.getLayoutParams();
layoutParams.width = (int) (dpToPixel(150) * 25 / 100.0f);//If you want to set 25%
highlight.setLayoutParams(layoutParams);
where dpToPixel() converts dp to pixels -
public int dpToPixel(float dp) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
Here is my solution,
Since I am not interested in using dp for width property, i looked for another solution. If you want to use dp directly check Shadab Ansari's answer which gave me a clue.
frameLayout
<FrameLayout
android:id="#+id/flOil"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
>
<View
android:id="#+id/progress_view"
android:background="#fc12d108"
android:layout_width="55dp"
android:layout_height="match_parent"
android:layout_margin="1dp"/>
<ImageView
android:id="#+id/ivOil"
android:layout_width="match_parent"
android:src="#mipmap/oilTank"
android:layout_height="match_parent"
>
</ImageView>
</FrameLayout>
Code
ViewTreeObserver vto = ivRate.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
public boolean onPreDraw() {
ivRate.getViewTreeObserver().removeOnPreDrawListener(this);
widthTank = ivRate.getMeasuredWidth();//get the width of the imageView which has oilTank image as dp.
ViewGroup.LayoutParams layoutParams = prgrs.getLayoutParams();
layoutParams.width = (int) (widthTank / (new Random().nextInt(4) + 2));//calculate a width for view which is going to fill background color, random was user for testwill be using desired value.
prgrs.setLayoutParams(layoutParams);
return true;
}
});
Before using widthTank variable , onPreDraw method has to be called so be sure that it is called otherwise it will not be assigned.
Please checkout this example
https://github.com/fanrunqi/WaveProgressView or https://github.com/rathodchintan/Percentage-wise-Image-Filling
this is the best way to fill image

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

How to scale 3 images to fit screen width

These is the result that I am after:
Basically I want to scale the 3 images so that they have the same height and all together fill the screen width. The original images will all have same height.
Can this be done using layout, without width calculations from code?
Just use Layout Weights.
In the main layout, or the layout which contains the ImageViews, put
android:weightSum="10"
and then in the individual ImageViews, put layout_weights as shown below, or upto your requirements.
This basically means the width of the images will be 25%, 55% and 20% respectively.
You can use a linear layout with weight attribute specified as shown below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:src="#drawable/bg_canvas"
android:layout_height="300dp"
android:layout_weight="0.33"/>
<ImageView
android:layout_width="0dp"
android:src="#drawable/bg_canvas"
android:layout_height="300dp"
android:layout_weight="0.33"/>
<ImageView
android:layout_width="0dp"
android:layout_height="300dp"
android:src="#drawable/bg_canvas"
android:layout_weight="0.33"/>
</LinearLayout>
Comment below if you need any further info
try this:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="3"
android:orientation="horizontal">
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
.../>
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
.../>
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
.../>
</LinearLayout>
the "magic" is in the weight component. you define a total weight of 3 in the layout and your image views should take a third of it, so the value is 1.
For my case the images needed to be updated at runtime, so none of the answers were exact fit.
I ended up extending LinearLayout and writing a small routine that unifies all images heights and make sure that all images together fill the LinearLayout width. In case someone is trying to achieve the same, my code looks like this:
public class MyImgLayout extends LinearLayout
{
public MyImgLayout(Context context)
{
super(context);
}
public void setup(ArrayList<String> images)
{
this.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
this.setLayoutParams(layoutParams); //set 0 height until we calculate it in onMeasure
for (String image : images) {
ImageView ivArticle = new ImageView(getContext());
setImageFromName(image, ivArticle); //this where you set the image
this.addView(ivArticle);
}
}
private void scaleImages()
{
if(getMeasuredHeight() == 0 && getMeasuredWidth() > 0) {
if (isHorizontal) {
double childRatioSum = 0;
int images = 0;
for (int i = 0; i < getChildCount(); i++) {
ImageView iv = (ImageView) getChildAt(i);
double width = iv.getDrawable().getIntrinsicWidth();
double height = iv.getDrawable().getIntrinsicHeight();
if (height > 0) {
childRatioSum += width / height;
images++;
}
}
if (childRatioSum > 0 && images == getChildCount()) {
//all images are downloaded, calculate the container height
//(add a few pixels to makes sure we fill the whole width)
double containerHeight = (int) (getMeasuredWidth() / childRatioSum) + images * 0.5;
for (int i = 0; i < getChildCount(); i++) {
ImageView iv = (ImageView) getChildAt(i);
double width = iv.getDrawable().getIntrinsicWidth();
double height = iv.getDrawable().getIntrinsicHeight();
iv.setLayoutParams(new LinearLayout.LayoutParams((int) (width * (containerHeight / height)), (int) containerHeight));
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) this.getLayoutParams();
params.width = LayoutParams.MATCH_PARENT;
params.height = (int) containerHeight;
this.setLayoutParams(params);
requestLayout();
}
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
scaleImages();
}
}

Android XML - ImageView scrolling

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

Move two side by side textviews to be one under another if text is too long

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;
}

Categories

Resources