I'm learning Android and i'm trying to avoid this behavior on my app.
This is the result using setMessage on the dialog.
This is the result using setTittle on the dialog.
Is there a way to avoid that the text or the radioButtons gets cut when i change orientation to horizontal ?
I'm using the a custom layout (LinearLayout) with this Alert Dialog for displaying the radioButtons.
I'm also using onCreateDialog to create the Alert Dialog .
#Override
protected Dialog onCreateDialog(int id) {
Dialog createdDialog;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
toDisplayInDialog = getLayoutInflater().inflate(R.layout.light_radiogroup, null);
builder.setTitle("Choose Startup Color:")
.setPositiveButton("Set Color",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Do things on Click
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
})
.setView(toDisplayInDialog);
createdDialog = builder.create();
return createdDialog;
}
The basic problem -- as you'll have guessed -- is that there are too many controls to fit on the screen when in landscape. You need to add a ScrollView so that the user can scroll the UI.
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Green"
android:checked="true" />
... etc.
</ScrollView>
Update
There are many ways to layout your widgets into two columns. Unfortunately there is no standard Layout that flows controls into multiple columns. However, below is a fixed way to display your five radio buttons.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Green" />
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Black" />
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Yellow" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Red" />
<RadioButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="White" />
</LinearLayout>
</LinearLayout>
Below is a preview:
Note: You'll probably not want to use this layout when displaying in portrait, only in landscape. You can have both by adding the "layout-land" resource directory where you can add this layout, and use your regular layout in the normal "layout" resource directory. This way the system will choose the layout resource accordingly to if the device is portrait or landscape.
It's twice the work, but your UI will look better for it.
As the screen height is too small for the dialog, the dialog needs to reduce its height to fit its margins and its own padding. As the elements don't fit normally anymore, it needs to decrease their height too. But on TextView's, that does not include scaling, so what you get here is cropping of the title.
That's a completely common issue in Android front-end development - you make yourself a perfect design but face technical issues on certain sizes/densities.
You can try these (I recommend you apply all 3 points if necessary):
RadioButton circle crops when its size is reduced. BUT it has some built-in padding included. You can try changing the height of each RadioButton. You'll just need to make sure it results compatible with screens of all densities and sizes.
Alternatively, you can apply these 3 steps:
1) set layout_height of every RadioButton, RadioGroup and the title to "match_parent"
2) set layout_weight of every RadioButton, RadioGroup and the title to "1". Radio buttons will be equally distributed within the container, and container and title will be of same height.
3) increase layout_weight of the title as much as necessary.
Method above will reduce title size and increase the RadioGroup size while distributing RadioButton's evenly inside.
Judging by cropping not happening immediately above RadioGroup, that probably means you have padding_bottom on the title. You should remove/decrease padding_bottom from the dialog title. If you need the padding in vertical mode, you can create 2 different layouts like in this answer, or take care of that programmatically through onConfigurationChanged like described here (removing padding_bottom in horizontal mode, and recovering it in vertical).
As you're using your own layout for the dialog, it seems to me like layout_height of your title might be set to "match_parent" or "fill_parent". You could try replacing it with "wrap_content", although it would have a different undesirable effect.
Related
I have an element that should remain centered in the layout and a button on the right size. The button width is variable.
The following design exemplify two scenarios.
Scenario 1: Long text button
Scenario 2: Small text button
The current solution is have an invisible duplicated button on the left. This is not ideal because the button look and feel may also vary for different locales. I have tried guidelines but that would require to define a percentage and I would prefer if it was dynamic. Barriers don't seem to work either because I would need them to be mirrored.
Any tips how to achieve this?
Try doing width with 0dp and give them weight and change this in runtime
may be they are in linear layout which is horizontal.
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
button.getLayoutParams();
params.weight = 1.0f; // afterwards you can do the same with changing the weight
button.setLayoutParams(params);
Perhaps you can take advantage of the ConstraintLayoutStates:
https://riggaroo.co.za/constraintlayout-constraintlayoutstates/
And have two layouts one for each scenario.
I ended up using a different solution. I used two guidelines, one at 20% and another at 80%, to define the areas where the button could expand to.
When no button is available, I used the property app:layout_constrainedWidth="false" that ignores the constrainst and allow the title to expand to the available space.
I used this solution because I may need multiple buttons with different call to actions according to the selected language. It would be difficult to manage multiple hidden anchor buttons.
If I understand your pictures correctly, you want to have two elements on your screen:
A View that's centered on the screen, it can be any width size.
A Button that's positioned to the right of the View that can also be any width size. You want this button to be centered in the empty space between the View and the edge of the right screen.
You can try this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/view"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="1"
android:textColor="#FFFFFF"
android:background="#000000"
/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/view"
app:layout_constraintEnd_toEndOf="parent"
android:text="2"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
I always create custom dialog without title to make it centered (both vertical and horizontal) using android:windowNoTitle in styles.xml or requestWindowFeature(Window.FEATURE_NO_TITLE) but some of my dialogs are not center horizontal, for example this dialog:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="20dp"
android:gravity="center"
android:background="#drawable/dialog_bg" >
<include
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="#layout/loading_s"/>
<TextView
android:id="#+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:gravity="center_vertical"
android:text="#string/loading"
android:textColor="#color/dialog_text"
android:textSize="#dimen/dialog_title_text_size" />
</LinearLayout>
This is how to create dialog:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
View v = LayoutInflater.from(getActivity()).inflate(R.layout.dlg_progress, null);
Dialog dlg = new Dialog(getActivity(), R.style.My_Dialog_Style); //My_Dialog_Style contains android:windowNoTitle = true
dlg.setContentView(v);
dlg.setCanceledOnTouchOutside(false);
dlg.setCancelable(true);
return dlg;
}
And here is how it appears on screen
If I remove android:windowNoTitle attribute this dialog show correctly so the problem only occurs when using dialog without title.
Does anyone know why this happen and how to make dialog always center on screen?
have you tried looking at this thread?
How to align custom dialog centre in android ?
android:layout_gravity="center"
It looks like its just a layout change, or try using relativeLayout or LinearLayout instead of FrameLayout
When you use Builder and set a custom view with setView, it should not be necessary to remove the Dialog's title and the dialog should be centered.
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
builder.setView(inflater.inflate(R.layout.dlg_progress, null));
return builder.create();
}
This is very similar to how it is done in the docs: Creating a Custom Layout
I believe you're running lower the dialog's minimum width attribute. It can be found as
<item type="dimen" name="dialog_min_width_major">65%</item>
in Android's framework. It varies depending on which values folder you're looking at, so it differs depending on density, orientation, etc.
You may be able to overwrite this value in your style. If you set it to something that is definitely smaller than your dialog(10%), it may work properly. If not, read on.
If you notice in your view tree panel, it shows your LinearLayout nested inside 3 FrameLayouts. My guess is that the deepest FrameLayout has its width set to wrap_content, so it's not filling the parent layout and is only as big as your LinearLayout. I can't be sure, though, because the dimensions are chopped off in your picture.
Why it changes when you remove the title? I don't know. You can hack it by adjusting the padding/layout params in onMeasure, but it seems like there should be a cleaner way to do it.
Still don't know why removing title make Dialog not centered horizontally but when I set min_width attr of LinearLayout = dialog minWidth this problem gone away.
I've got the following xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayout1"
android:background="#android:color/transparent"
android:layout_marginTop="0px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:weightSum="1"
android:orientation="horizontal">
<Button android:id="#+id/info" android:text="Info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom"></Button>
<Button android:id="#+id/town" android:text="Town" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom"></Button>
<Button android:id="#+id/unit" android:text="Unit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom"></Button>
<Button android:layout_height="wrap_content" android:text="EndTurn" android:id="#+id/endturn" android:layout_width="wrap_content" android:layout_gravity="bottom"></Button>
</LinearLayout>
which provides the following result: http://i42.tinypic.com/otdkb4.png
Now I've got some questions about this:
The top and bottom padding, how to get rid of it?
I tried RelativeLayout, multiple layouts within each other, padding, margin, changing height nothing seem to affect it in any way.
Is there a way to get the layout transparent? android:background seems to be the wrong one.
Between the third and the fifth button is a bit more space (where the fourth button should be). I catch it the in the program and set it to invisible.
unitButton.setVisibility(INVISIBLE);
unitButton.setWidth(0);
Now the space between the two buttons is more than double of the normal range (between 1 and 2) Any idea on this? - Altough this is a minor problem
Thanks in advance.
1: Is the layout presented in a Dialog? If so, that'll give you some headaches. To get more control you should either create your own custom Dialog extension (as some dialog layout values are hardcoded), or display your layout in another way (a new activity on top, or using a framelayout perhaps)?
2: To get a layout transparent, simply don't give it a background-attribute. (Though, if you really are using a dialog, the dialog box is not transparent, and it is that which you see. You can also set it to be transparent by setting background to "#00000000" (which is what you do).
3: A View with visibility as "invisible" is still measured, that means both its width/height as well as its margins and padding is displayed as empty space in your layout. Setting the visibility to "gone" instead will not measure it, and you won't need the setWidth(0) either. (You can still display it later by setting it back to "visible")
Edit: removing the unused "weightSum" attribute might also be a good idea, as the view is now expecting its children to have a total weight of something other than 0.
I've observed a behavior with layout_weight that I can't explain. The following is a trivial example:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="This is a very very very very very very very very very very very very very very very long string."
android:layout_weight="1"
/>
<View
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center_vertical"
android:background="#ffffffff"
/>
</LinearLayout>
In a QVGA display, the TextView wraps the text. The white square is displayed to the right of the text.
However, if I remove android:layout_weight="1" from the TextView, the TextView now takes up the entire display width. The white square is no longer displayed.
Why would layout_weight in the TextView affect whether or not the white square is displayed? Shouldn't the View with the white background always be assigned 32dpx32dp first? (It makes no difference if the view were any other types - ImageView or TextView).
The problem I was working on is that I want the white square to always be displayed to the right of the TextView (whether or not the text is wrapped), but I don't want any empty space between the TextView and the white square. (If I add android:layout_weight="1" to the TextView, then there is a gap if the text is not wrapped.)
Any help would be appreciated!
To answer my question #1: One thing I learned by looking at the source for LinearLayout: Not only does layout_weight assign unused space to a child, it also shrinks a child with layout_weight if the child extends beyond the bounds of the LinearLayout. That explains why a TextView with wrapped text is shrunk in my layout.
As for the answer to my question #2, I think you meant android:toRigthOf instead of android:layout_alignRight. Using a RelativeLayout instead of a LinearLayout doesn't change the layout behavior. The tricky part is placing a view immediately to the right of a TextView, without gaps, whether or not the text is wrapped. Setting a maxWidth would limit the TextView's width, but that solution doesn't scale across portrait/landscape and different display dimensions.
Solution - Looks like Dyarish's solution is the best available. My layout problem exists regardless of the layout you use. The key is to set a maxWidth for the TextView so that it doesn't take up the all of the horizontal space in the layout. Because hardcoding a android:maxWidth value in the TextView doesn't scale across different displays, setting the maxWidth at runtime, as Dyarish suggested, is a good solution.
Hopefully this is what you are looking for.
First off, here is a great resource I found for Creating UI's.
layout_weight - Specifies how much of the extra space in the layout to be allocated to the View.
If you want to ensure that the white square is always to the right of the textview, you can use a Relative View, and add the parameter to the view. android:layout_alignRight="+id#yourTextViewID". This should always make the box appear right beside the textView area. You should probably also add something like android:maxWidth="250px" This will ensure that you don't push the white box completely out of the screen.
Here is a code sample:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:maxWidth="250px"
android:id="#+id/TextForWhiteBox"
android:layout_height="wrap_content"
android:layout_gravity="center|left"
android:text="This is a very very very very very very very very very very very very very very very long string."
/>
<View android:background="#ffffffff" android:layout_width="32dp" android:layout_height="32dp" android:id="#+id/view1" android:layout_toRightOf="#+id/TextForWhiteBox"></View>
</RelativeLayout>
You could also add to the View:
android:layout_alignTop="#+id/TextForWhiteBox" android:layout_alignBottom="#+id/TextForWhiteBox"
to make the white box the same size as the TextView.
Firstly I've tested the code from my other answer and it does exactly what you've described you've wanted. (unless I'm misunderstanding what you are asking for). You definitely do not want to use the android:layout_alignRight which is not what is in the code sample. That would simply keep the box on the right hand of the screen and not be affected by the textview at all. This sample uses android:layout_toRightOf="#+id/TextForWhiteBox" which is possible due to it being a relative layout. Since the Relative Layout allows you to place objects in relation to others. That line will always place the box just to the right of the textview with no gaps.
As for the screen orientation changes:
When the orientation changes it creates a new instance of the view.
Here is a simple solution.
//Add to oncreate in your Activity
private TextView textStatus;
textStatus = (TextView) findViewById(R.id.TextForWhiteBox);
// This get's the width of your display.
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
// Now you know the screen orientation, and it's width. So just set the maxwidth of the text view to match the display width - the pixels of your white box.
textStatus.setMaxWidth(width - 32); // 32 is here because you already know the size of the white box. More logic is needed to dynamically get this value, because you would need to wait for the activity to be fully created.
}
Here is the main.xml I used:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:id="#+id/TextForWhiteBox"
android:layout_height="wrap_content"
android:layout_gravity="center|left"
android:text="This is a very very very very very very very very very very very very very very very very very very very long string."
/>
<View android:background="#ffffffff" android:layout_width="32px" android:layout_height="32px" android:id="#+id/view1" android:layout_toRightOf="#+id/TextForWhiteBox"></View>
</RelativeLayout>
You might need some additional logic to keep screen values.
This code has been tested, you should be able to literally copy and paste this to work as you asked.
Also depending on your logic you could use something like this to return the screen orientation.
int orient = getResources().getConfiguration().orientation;
Hope this helps!
If this helped you, please click the accepted button. =) Cheers!
I have an App that has a toolbar at the bottom of the screen and the rest is filled with a custom View (see xml below). Now when I make the App full screen (I tried all possibilities, programmatically and via Manifest.xml), when it's started the whole layout seems to be shifted down by about the height of the notification bar. The buttons in the toolbar are only visible half-way. Sometimes, all of it moves up after a few seconds, or when I click a button in the toolbar.
I'm pretty sure, that it's a problem with my custom view, because I do not get this effect if I replace it with a Button or the like. I guess it must have something to do with the onMeasure method. I don't really know how to implement it, my version is shown below. The custom view is used for drawing inside, so basically it wants to be as large as possible.
Any help would be much appreciated. I searched for several hours already, but no clue yet.
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<com.example.MyCanvasView
android:id="#+id/canvas"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<!-- Buttonbar -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:orientation="horizontal"
android:background="#android:drawable/bottom_bar"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3"
/>
</LinearLayout>
</LinearLayout>
And this is my onMeasure method:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
You're not taking the mode into account when you're setting your measurement.
The mode of a MeasureSpec can be one of MeasureSpec.EXACTLY, MeasureSpec.AT_MOST, or MeasureSpec.UNSPECIFIED. Simply accepting the size component and setting your measured size to that is appropriate for EXACTLY, but it isn't often the right thing for the others.
Because you're trying to use layout_weight in addition to a height of wrap_content on this custom view, the following is happening:
Your custom view is the first child of the LinearLayout with a height of wrap_content so LinearLayout measures it. Since LinearLayout has been told by the LayoutParams that it should wrap_content, it measures your custom view with a MeasureSpec mode of AT_MOST and a size of the entire available space.
But your custom view is greedy. It decides to take all of the space available. In essence, you have implemented your measurement to treat wrap_content as match_parent.
Now there's no more space left. The lower button bar gets measured accordingly but it's not done yet, there's a child with weight. In a LinearLayout any space left over after all normal measurement is complete is divided among the weighted children according to their weight values. This isn't the behavior you want.
When you use weight to fill available vertical space like you're doing in this layout, you normally want to set the layout_height to 0dip. This will make LinearLayout skip the first measure pass on that child and only use the weighted measurement pass to measure your view, giving it the remaining available space.
I found the reason for the described behaviour. I had set the view to be focusable in touchmode via setFocusableInTouchMode(true) in the onCreate() method. As soon as I removed this, it works fine. Thanks to adamp though -- your description of what goes on during layout and measuring was very interesting.
But that leaves me with the problem that I do not receive any key/button clicks any more :-(