I got this setup:
<ScrollView 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="#+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.35" />
...
</android.support.constraint.ConstraintLayout>
</ScrollView>
Now when the view is not full and has no need for a scroll bar - everything works as expected - the image is 35% in relation to the screen size. But as more content appears under the image, a need for scroll bar appears and the guideline's constraintGuide of 0.35 percent seems to be calculated off whole length of the screen (not physical), so the ImageView also becomes bigger as the view becomes "longer".
Is there a way to avoid this and always have x percent of physical screen size?
The guideline you have specified is placed at a percentage distance from the top of the ConstraintLayout. Unfortunately, for your application, the guideline is tied to the overall height of the view and not a percentage of the screen. So, if the ConstraintLayout is taller than the screen size allocated to it, you will see the shift. See documentation for Guideline.
Positioning a Guideline is possible in three different ways:
specifying a fixed distance from the left or the top of a layout (layout_constraintGuide_begin)
specifying a fixed distance from the right or the bottom of a layout (layout_constraintGuide_end)
specifying a percentage of the width or the height of a layout (layout_constraintGuide_percent)
You can specify a static offset from the top of the layout in terms of dp, but this will not accommodate different screen sizes. I don't believe there is a solution just using XML.
You can, however, calculate the number of pixels in code and set the distance on a run-time basis. You would need to change the Guideline to one that is a fixed distance from the top of the layout, calculate the distance from the top, and call setGuidelineBegin to place the guideline.
setGuidelineBegin
void setGuidelineBegin (int guidelineID,
int margin)
Set the guideline's distance form the top or left edge.
Related
In ConstraintLayout version < beta5, I had layouts like this example below:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/square_image"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintDimensionRatio="H, 1:1"/>
</android.support.constraint.ConstraintLayout>
However, versions starting beta5 have removed the MATCH_PARENT constraint for child views.
The documentation gives examples of using app:layout_constraintDimensionRatio:
You can also use ratio if both dimensions are set to MATCH_CONSTRAINT
(0dp). In this case the system sets the largest dimensions the
satisfies all constraints and maintains the aspect ratio specified. To
constrain one specific side based on the dimensions of another. You
can pre append W," or H, to constrain the width or height
respectively. For example, If one dimension is constrained by two
targets (e.g. width is 0dp and centered on parent) you can indicate
which side should be constrained, by adding the letter W (for
constraining the width) or H (for constraining the height) in front of
the ratio, separated by a comma:
<Button android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
https://developer.android.com/reference/android/support/constraint/ConstraintLayout.html#DimensionConstraints
This example works when the parent layout has a fixed height or match_parent, but not when the parent is set to wrap_content.
Using my code example above, if I set the ImageView width to 0dp, the parent view collapses as if it has no content.
This was an incredibly useful feature, I feel like I'm just missing something in this new version. Any help appreciated.
ConstraintLayout v1.0.2 fixes this issue.
I have a complex xml layout with part of it being:
...
<FrameLayout
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_weight="1" >
<ImageView
android:id="#+id/flexible_imageview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#drawable/gradient"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingTop="8dp" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>
...
The height of the FrameLayout #+id/parent must be determined at runtime because it is displayed above many other views, and they must be shown in the layout. This way the FrameLayout fills the remaining space perfectly (using height="0dp" and weight="1" properties).
The ImageView #+id/flexible_imageview receives an image from the network, and it always shows with the correct aspect ratio. This part is already ok as well. This View is the largest and should determine the size of the FrameLayout #+id/parent.
The problem is that when the image is drawn, the width of the FrameLayout #+id/parent is not adjusted to wrap and be the ImageView #+id/flexible_imageview as it should be.
Any help is appreciated.
-- update --
I've updated the layout to clarify some of the missing parts, and to add the reasoning behind all of it.
What I want is to have an Image (ImageView #+id/flexible_imageview) with unknown dimensions to have, on top of it, a gradient and some text on top of the gradient. I can't set the FrameLayout #+id/parent dimensions to both wrap_content because there is more Views after this Image that must be shown. If there's not enough space in the layout, the Image (ImageView #+id/flexible_imageview) is reduced until it all fits in the layout, but it should maintain its aspect ratio, and the gradient/texts on top of it.
The #drawable/gradient is just:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="270"
android:endColor="#aa000000"
android:startColor="#00000000" />
</shape>
-- update 2 --
I added two images below to demonstrate what's happening, and what should happen:
Bug:
Correct:
If would help if you explained more about what you are trying to accomplish in your layout (not the layout itself but what should the user see on the screen and what are the other elements in the layout).
A FrameLayout with multiple children is usually a "code smell". Usually, FrameLayouts should have only one child element. So this makes me wonder whether there is something wrong with your design.
-- Edit --
If I understand correctly, you are trying the framelayout to wrap the content of the image but at the same time match the space left from the other layout views before/after the frame layout.
What is the parent view/layout of the frame layout?
I see a couple of problems with this design or your explanation:
You have framelayout width set to match parent, but you want to wrap the content of the image.
You want the imageView to be reduced but you are not taking into account the text views in the linear layout. You have them set to wrap content. So when the fame layout is small, you will not see all the textviews. (Unless you are resizing them as well somehow).
Sorry if this isn't helpful enough but it's difficult to understand what you are trying to accomplish with this layout. A sample use-case would help in providing you a better recommendation.
When a dimension (width / height) is MeasureSpec.EXACTLY adjustViewBounds will not effect it.
In your case, having android:width="match_parent" ensures that the image view is the size of the parent, regardless of adjustViewBounds.
It works to begin with because the height is wrap_content - the height is adjusted when the image is scaled to fill the width.
When you override the height to fit everything on the screen (this may not be a great idea to begin with), the width is still matching the parent and doesn't get adjusted. However, because the scale type is ScaleType.FIT_CENTER the image is scaled and positioned so that the entirety of it fits in the bounds of the ImageView (and centred.. hence the name).
If you turn on the debug option for drawing layout bounds, or look at your app using hierarchyviewer, you'll see that the image view is still matching the width of its parent.
There are a couple of ways you could do what you want.
Since you're already manually calculating the height, setting the width shouldn't be that hard.
Drawable drawable = imageView.getDrawable();
if (drawable != null && drawable.getIntrinsicWidth() > 0 && drawable.getIntrinsicHeight() > 0) {
int height = // however you calculate it
int width = height / (getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth());
}
You might also get away with
<ImageView
android:id="#+id/flexible_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:minWidth="9999dp" />
Even if this works, you probably wouldn't be able to use it in a horizontal LinearLayout using weights any more (for example, if you wanted a landscape variant of the layout), or a number of other scenarios.
I recently tried to position an imageview's x and y coordinates with no luck, it seems there is no way to do it in Gingerbread. I then decided to try out paddings and margins, but when I set them, it shrinks my imageview. I set a left padding of 250dp and image view became tiny. The layout_height and width are set to wrap_content. I'm not sure what's going on. Does anyone know why setting a padding/margin would shrink an imageview?
You're confusing margin and padding. Margin is the area outside of your view, while padding affects the content inside your margin.
If you set padding, then it is going to affect your available content area, and assuming you have a ScaleType set, it's going to shrink your image down to fit the available space.
Now, you say you've tried margins, but margins will do exactly what you're asking.
For example, if you wanted an ImageView placed 10dp from the top-left corner, you can do something like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="#+id/my_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:src="#drawable/my_image_id"
/>
</RelativeLayout>
Keep in mind that this places it 10dp with respect to the parent boundaries. If your parent layout also has padding, then that will affect your content placement.
if by shrink you mean the picture's ratio is messed then you should use
android:scaleType="centerInside"
this will prevent the ratio from changing
I am creating a Camera application where I want to restrict the camera preview to be centered and around 60% of total size of the phone screen.
One way is to have the FrameLayout of Preview with padding so that the usable screen size is restricted , but I also want to show borders (around the corners) inside the restricted area which is not possible in this options.Also it not possible to show padding in percentage so that I can restrict user to 60% of screen size.
Other way is to have a image having a transparent center rectangular area and having border in inner rectangle corners . But I somehow need to restrict the FrameLayout of preview inside this rectangle which I am not able to do . Any suggestions on this option ?
Please also let me know if there are any other options .
Thanks.
Unless I don't fully understand your issues with using android:padding to provide space around your layout, you should be able to use android:layout_margin instead to accomplish the same goal but overcome the problems you mentioned. Adding padding creates space between the border of the View and its content. However, adding margin creates space between the border of the View and its parent, so the content still fills the View itself to the edges. However, you still can't define your view spacing in terms of percentage direct from XML...you would need to define the margin as static or apply the LayoutParams in Java code where you could calculate the required margin based on the current screen size.
Another option is to take advantage of the android:layout_weight property inside of a nested LinearLayout. The calculated weight sum could give you the 60% you're looking for directly in XML. Something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:weightSum="1.0" >
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.6"
android:gravity="center"
android:weightSum="1.0">
<SurfaceView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.6" />
</LinearLayout>
</LinearLayout>
Where the SurfaceView is the location of your Camera Preview, now centered with 20% of the screen on all sides. If you wanted to place things over or under this preview you would obviously want to place this entire block into a RelativeLayout or FrameLayout along with other components.
HTH!
I'm using a RelativeLayout, and want to center the orange bar below in the center and fill up 90% of the width. I know this is possible to do using a LinearLayout using percentage, but how do I do so with a RelativeLayout?
Do I have to programatically calculate how wide is the screen (in dp), and set this 90% of this as the width of the orange view?
In short, I want something like this, where the orange bar takes up 90% of the screen centered in the middle using a RelativeLayout, without hardcoding x and y coordinates, so that it works on all screen densities. (The dark bars in the edge are the edge of the phone)
Just a guess, but worth a try :)
Please redesign your layout structure like below:
<LinearLayout[with 90% width]>
<RelativeLayout
android:layout_width="fill_parent"
...
<!--this will fill up its parent i.e above LinearLayout which is 90% of the screen width -->
>
<!--child views goes here.. -->
</RelativeLayout>
</LinearLayout>
Use new percentage support library
compile 'com.android.support:percent:24.0.0'
See below example
<android.support.percent.PercentRelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
app:layout_marginTopPercent="25%"
app:layout_marginLeftPercent="25%"/>
</android.support.percent.PercentRelativeLayout>
*For More Info * Tutorial1 Tutorial2 Tutorial3