Dimensions of usable space in window (action bar and margin excluded) - android

Disclaimer: I rewrote most of this question, including its title, because I partially figured it out.
I'd like to maximize the size of the button on all screen sizes, because it looks silly when it is to small. It should look similar to this:
(Sadly, I cannot include a picture, because my reputation is too low.)
But if I turn the orientation of the device, for example, the button matches it's parents width, becoming ugly proportioned.
(Sadly, I cannot include a picture, because my reputation is too low.)
I now have figured out how to get the dimensions of its parent (the LinearLayout) and how to set the button's size. I used the following code:
window is the ID of the LinearLayout containing (only) the button.
this code is located in the onCreate()-method of the MainActivity.
// Adapt button's size to smaller dimension:
final View window = findViewById(R.id.window);
window.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int width = window.getMeasuredWidth();
int height = window.getMeasuredHeight();
int smallerSize;
if (width < height) {
smallerSize = width;
} else {
smallerSize = height;
}
View button = findViewById(R.id.fartButton);
button.setLayoutParams(new LinearLayout.LayoutParams(smallerSize, smallerSize));
window.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
The problem with this approach is, that it doesn't seem to account for padding. The button get's cut off a little bit on the smaller side (in portrait mode its width, in landscape mode its height).
Interestingly, the image inside the button fits the window perfectly. If for example the height gets cut off a bit, the image still is visible in its full height (only some "extra" parts of the button get cut off, like a little border and shadow).
Is there a way to get the maximal size of the button, which would be the size of the window, but without action bar and minus padding, to prevent any part of the button to get cut off?

Your example above "should look similar to this:" does not seem to have loaded, illustration would help...
But you can manage screen proportions pretty well using android:layout_weight
I'm not sure I'm envisioning your exact needs, but you might try something like this:
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="X"
android:text=" "
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="button"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="X"
android:text=" "
/>
</LinearLayout>
where different values for X would control the horizontal aspect ratio for your button in a view.

I just figured it out. Was much easier than I thought. Thanks to everyone who answered, though. It helped me a lot on the way!
The padding that is applied to the window can easily be accesed through the getPadding...() methods. I just needed to adjust the part where the width and height get saved:
int width = window.getMeasuredWidth() - window.getPaddingLeft() - window.getPaddingRight();
int height = window.getMeasuredHeight() - window.getPaddingTop() - window.getPaddingBottom();
I thought, that even by manually excluding the padding, the highlight when pressing the button would be cut off, because it is a bit bigger than the button itself. But this is not the case and it works perfectly. The button now gets displayed in its whole glory. ;)

You can overload your onMeasure method to always return a square.
Create a class that extends to Button and include this
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(size, size);
}
Not sure how this will work if you give exact dimensions but it should work if you set width, height to match parent

Related

Why my ImageView would not change width in Android?

I'm developing an Android app with avatar (the image icon), the ImageView is inside an RelativeLayout like this
<ImageView
android:id="#+id/indicators"
android:layout_width="#dimen/conversation_avatar_size"
android:layout_height="#dimen/conversation_list_left_indicator_height"
android:layout_below="#id/contact_image"
android:layout_alignWithParentIfMissing="true"
android:layout_marginEnd="#dimen/smaller_def_margin"
android:layout_marginTop="#dimen/smaller_def_margin"
android:scaleType="fitCenter"
tools:src="#drawable/ic_reply"/>
However, I cannot change the width of this ImageView when I make the contact_image invisible because indicator takes less space. The code is like below
indicators.getLayoutParams().width = width;
indicatorsWidth = indicators.getWidth();
indicators.requestLayout();
After indicators.requestLayout(); the width of the indicator still keeps the old one not changed to width. I'm wondering why this can happen? I also tried indicators.setLayoutParams(layoutParams); but still doesn't work.
I did find some interesting, even though indicators.getWidth() is still the old value, indicators.getLayoutParams().width is the new width now, and ImageView size doesn't change at all.
Update : I found what view.getWidth() is, and then I used indicators.setLeft() and indicators.setRight(), this time indicators.getWidth() is also the new width, however, the size still doesn't change, I checked it and found that indicators.getMeasuredWidth() is still the old value, any idea?
Your issue may be android:scaleType="fitCenter" if the height of the ImageView is the constraining factor. The view will be resized to fit the height and maintain the aspect ratio by adjusting the width. See this.
Compute a scale that will maintain the original src aspect ratio, but will also ensure that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. The result is centered inside dst.
Here is a small app to play with this concept. You will see that even though the width is change, the height is not with android:scaleType="fitCenter". If you change it to android:scaleType="fitXY", you will see a change. This may not be your issue exactly, but you can use this code which is an MCVE to test out some ideas.
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView indicators = findViewById(R.id.indicators);
indicators.postDelayed(new Runnable() {
#Override
public void run() {
indicators.getLayoutParams().width = 500;
indicators.requestLayout();
}
}, 2000);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/indicators"
android:layout_width="100px"
android:layout_height="100px"
android:layout_alignWithParentIfMissing="true"
android:scaleType="fitCenter"
android:src="#mipmap/ic_launcher"/>
</RelativeLayout>
When modifying LayoutParams, you have to reassign the object back to the view or there will be no effect.
ViewGroup.LayoutParams params = myView.getLayoutParams();
params.width = newWidth;
myView.setLayoutParams(params);
This last line is what actually changes the width of the view.

Buttons Over Image Android

I am developing a feature for my app where the user can click on buttons that are placed within an image background. The problem: I need to place the buttons to a particular location within the image so it doesn't look displaced and works dynamically to different kind of devices' resolutions. Here's an image example:
I would like to place the buttons exactly where these rounded squares are. How can I be sure they will look exactly the same in different devices as well? I might need to place some text above each button. The buttons they have to be clickable and I have some animation over the buttons to allow the user to know when the button is being clicked. Any lead is much appreciated. Any feasible solution could be the accepted answer.
The game clash of clans have something similar:
This is exactly what I am trying to achieve!
Since there are a lot of Android devices, in practice you can't show buttons exactly in all devices. You can use a RelativeLayout or a LinearLayout to assure that the top title will be above buttons
There will be a parent view with two children : One is for the title, and another is for all the buttons
If you use a RelativeLayout, you could place the buttons at any point within the Image.
Just make sure that you use DP units of measurement and have size appropriate images for each density.
A short example:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button android:layout_marginLeft="10dp"
android:layout_marginTop="10dp" />
<Button android:layout_marginLeft="30dp"
android:layout_marginTop="30dp" />
<Button android:layout_marginLeft="10dp"
android:layout_marginTop="50dp" />
</RelativeLayout>
I found a hacked solution by using the percentages of the width and height of the device. It worked for tablets and 2 more different screen size devices.
int w = front_layout.getWidth();
int h = front_layout.getHeight();
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
int top = convert(h * 0.248f);
int bottom = 0;
int left = convert(w * 0.186f);
int right = 0;
params.setMargins(left, top, right, bottom);
btn1.setLayoutParams(params);
private int convert(float value) {
return (int) Math.ceil(value);
}
The only downside of it is that I have to find the exact percentages by try and error. A better solution will score my accepted answer!

Android: Design expand to width

I'm trying to figure out designing for multiple screen sizes/densities/etc. I thought "dp" was my answer but it's not working the way I thought it was going to.
Here's what I'm trying to do:
I have a header and footer section for menu items that remain static. Then I have a ScrollView between the 2. Within the ScrollView is several "blocks" that display various information. I want those blocks to always expand to the full width of the screen. I want the height of those blocks to simply scale appropriately to keep a proper 1:1 -> height:width scale. I also want the TextViews/Images/Buttons/etc to scale and keep their position within the block. If the Blocks height get too big for the screen, that's where the ScrollView comes in. If they fit just fine, that's OK too. I mainly want to scale to width and let the user Scroll if necessary.
Currently if the screen gets wider, the Blocks get really wide but don't scale the height to match them in.
Here's a basic (simplified for readabilities sake) rundown of what my XML layout code looks like. Also note that each block may be a different size. I'm not looking for a "take up 25%" solution. The blocks have fairly complex and completely different layouts within them.
<LinearLayout>
<RelativeLayout>Header stuff</RelativeLayout>
<ScrollView> //height and width = match_parent width lower priority than header/footer
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="???" //Had been using a specified "dp" value but that didn't work like I thought. What goes here?
>
Block#1 with lots of TextViews, ImageViews and Buttons...etc
</RelativeLAyout>
<RelativeLayout>
Block#2
</RelativeLAyout>
<RelativeLayout>
Block#3... etc
</RelativeLAyout>
</ScrollView>
<RelativeLayout>Footer stuff</RelativeLayout>
</LinearLayout>
One way of achieving 'perfect' 1:1 ratio (or other predefined ratios) of a view is to create your custom extension of it and override the onMeasure() method. in your case you might want a SquareRelativeLayout with an onMeasure() method looking something like this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, width);
}
Edit:
If you want a custom ratio, you can define it somewhere and measure your View like this:
float ratio = 0.5f;
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, ratio*width);
}
If you know how to create your own xml-attributes you can add the ratio attribute and get it from there, meaning you don't have to create a new View for each different ratio.

Device screen dimension has weird effect on scaled ImageViews

Update:
The multiple device screen dimensions is a red herring - the problem is just that the image does not scale up properly to fill the screen - see comments on Ivan's answer.
I have a layout file with one image:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/image"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
Then I assign a drawable, which is small and must be scaled up:
setContentView(R.layout.image_story);
ImageView image = (ImageView)findViewById(R.id.image);
image.setImageDrawable(s.image);
Here is how it is rendered on two AVDs with different screen dimensions. These should be the same (detailed question at the bottom). Sorry for the large images.
with scaleType=centerCrop:
with centerInside
AVDs:
Edit:
With layout_height="fill_parent" and scaleType="centerInside"
I have a 2.1 AVD with the default values, so the screen is smaller, and this works exactly as expected- the image is scaled up to fill the width, and the view height is wrapped to the scaled height.
On my Droid Bionic with a longer screen, and on any AVD with the same screen dimensions, this doesn't work- the image is scaled to fill the width, but the view is wrapped to the original pre-scale image height, so the top and bottom are cropped off.
I have no idea why the device screen aspect ratio would have an effect on this. I've tried countless combination of layout parameters and scale types trying to get this to work on the Bionic. Everything works exactly as expected on the smaller screen, and not on the larger. If I set the image height explicitly in dp, it works as expected, but I never know what the dimensions of the image (or screen) will be. Any suggestions?
That is a very good question indeed.
Here's the reason it's behaving like that (from ImageView.onMeasure(int, int) [line 661]):
// Try adjusting height to be proportional to width
if (!done && resizeHeight) {
int newHeight = (int)((widthSize - pleft - pright)
/ desiredAspect) + ptop + pbottom;
if (newHeight <= heightSize) { // line 661
heightSize = newHeight;
} // line 663
}
What it does is it adjusts the height of the view only if the new height that is based on aspect ratio of the drawable and the adjusted width (in our case, that's exact width of the parent view) is smaller than the adjusted height (which at this poin is just drawable's native height plus padding in our case. Let me know if you want me to brake down this point further.)
What I don't get is why is there the restriction that new height must be smaller. It makes sense only if our heightSize is either EXACTLY or AT_MOST and has been set to the upper bound. In other cases it's not necessary for it to be so.
So actually instead of the whole piece 661 through 663 there should have been another call to
heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightSpec);
to make sure we only use the height restriction when it should be restricted (ie. we got AT_MOST restriction in heightSpec and the height value in heightSpec is smaller than the new height. EXACTLY can't happen here unless we use variable width.)
Maybe, I missed something there. Guys, whoever is reading this, please comment if you see any flaws in that, especially if you're part of Android team at Google :)
PS As a workaround, I can suggest you implement a custom ImageView and override onMeasure(int, int) to set the bounds to your exact aspect ratio. Let me know if you need help on the actual code.
UPD I'm going to write out names to catch attention of smart android guys at Google (I'm hoping the guys have Google Alerts set up): Romain Guy, Roman Nurik, Reto Meier, please, take a look at this discussion.
As I understand, you need to scale the image maintaining its aspect ratio. scaleType="fitCenter" should give you the desired behavior:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/image"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter" />
If it doesn't help, try adding this to the manifest file:
<supports-screens
android:resizeable="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true" />

How to create a view that is bigger than the screen?

Is it possible to create a view that is bigger than the screen?
I need a view that has a bigger width then the screen of the device. I use this view in a rotation animation. During the rotation the parts that were not on the screen before animating the view will become visible.
Is there a way to achieve this effect with the android framework?
Update
I tried to set my parent layout much bigger then the screen and it is working. This will make somethings a little bit uncomfortable but it could work. The next problem now is that my layout still starts at the left side of the screen. I can't think of a method to make the layout to expand itself to the left and the right of the screen.
Ok I got an answer. It is not very nice because it uses a deprecated View class but it works at least on my current testing screen resolution other resolutions are tested tomorrow.
I wrapped the view that I wanted to expand beyond the screen in an absolute layout like this:
<AbsoluteLayout
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true">
<ImageView
android:id="#+id/content"
android:layout_width="600dip"
android:layout_height="420dip"
android:scaleType="centerCrop"
android:layout_x="-200dip"
android:layout_y="60dip"
android:src="#color/testcolor" />
</AbsoluteLayout>
The -200 x coordinate makes the view stick 200dip out of the left side of the screen. If I'm animating the view those parts that are outside the screen will gradually become visible.
E.g. setting negative bottom margin together with setting extra large layout_height (large enough for you) solved the similar issue as for me.
Works fine at least using API 11+ animations/rotations.
Could look like:
android:layout_marginBottom="-1000dp"
android:layout_height="1000dp"
In case anyone still comes up on this page. The key is your root layout, it will only work with a FrameLayout (or the deprecated absolutelayout). Then you have two options to make your child view bigger.
through xml, this is quick and easy but you don't know the actual screen width & height in advance so your off with setting a ridiculously high value for layout_width & layout_height to cover all screens.
Calculate the screen size programatically and make the view's width/height proportional bigger to this..
Also be aware that your bigger view still starts in the top left corner of the screen so to account this you will have to give a negative top & left margin that's half of what you are adding to the view's width/height
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) viewToMakeBigger.getLayoutParams();
int marginLeft = (int) (viewToMakeBigger.getWidth()*0.1);
int marginTop = (int) (viewToMakeBigger.getHeight()*0.1);
params.width = (int) (viewToMakeBigger.getWidth()*1.2);
params.height = (int) (viewToMakeBigger.getHeight()*1.2);
params.leftMargin = -marginLeft;
params.topMargin = -marginTop;
viewToMakeBigger.setLayoutParams(params);
HorizontalScrollView:
http://developer.android.com/reference/android/widget/HorizontalScrollView.html
Layout container for a view hierarchy that can be scrolled by the user, allowing it to be larger than the physical display.
The simple axml below creates an ImageView that is 400dp wider than the screen (even though the layout_width is set to equal the parent's width) using a negative left and right margin of 200dp.
The ImageView is situated 250dp above the top of the screen using a negative top margin, with 450dp of 700dp vertical pixels visible on the screen.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:background="#FFFF0000"
android:layout_height="700dp"
android:layout_marginLeft="-200dp"
android:layout_marginRight="-200dp"
android:layout_marginTop="-250dp"
android:layout_width="match_parent" />
</RelativeLayout>
You can override the views in the onMeasure method. This will set your View dimensions to 1000x1000 px.
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(1000, 1000);
}
Is it possible to create a view that is bigger than the screen?
Why not, you can define the layout_width and layout_height in px(or dip) as you want:
android:layout_width="10000px"
android:layout_height="20000px"
You need to change the size of the window, by getWindow().setLayout. This will increase the size for your window. Since the root layout can be as big as its parent you can then increase the size of the view you want to be bigger than the screen size. It works for me let me know
You can use ViewSwitcher to handle that. Used with Animation and a OnGestureListener looks pretty good.
You can do it programmatically:
FrameLayout.LayoutParams rootViewParams = (FrameLayout.LayoutParams) rootView.getLayoutParams();
rootViewParams.height=displayMetrics.heightPixels+(int)dpToPixels(60);
rootViewParams.width=displayMetrics.widthPixels+(int)dpToPixels(60);
rootView.setLayoutParams(rootViewParams);
rootView.setX(rootView.getX() - dpToPixels(30));
rootView.setY(rootView.getY() - dpToPixels(30));
MUST BE ONLY IN
"public void onWindowFocusChanged(boolean hasFocus)" method.
and
rootView = (RelativeLayout) findViewById(R.id.rootLayout);
Inside "protected void onCreate(Bundle savedInstanceState)" method.
Where yout .xml file is like this:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/rootLayout"
tools:context="com.example.Activity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_centerHorizontal="true"
android:layout_margin="30dp"
android:layout_centerVertical="true"
android:layout_height="match_parent">
// Bla bla bla
</RelativeLayout>
and:
public float dpToPixels(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}

Categories

Resources