I am trying to create own custom view ,I observed onMeasure shows into 2 of whatever value set.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.customview.PercentageCircle
android:layout_width="match_parent"
android:layout_height="100dp"
custom:currentValue="0"
custom:maxValue="100"
custom:thickNess="80"
custom:fillBackgroundColor="#color/light_gray"
custom:fillColor="#color/red"
/>
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);//shows 200
this.setMeasuredDimension(parentWidth, parentHeight);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
When I log the parentHeight or parentWidth ,I get double the specified value.I am not able to understand cause of it.
All the inputs to Canvas functions are in pixels.
Function parameters of onMeasure are also in pixels.
To work UI across the devices , If we use pixels, things become too small on high resolution screens.
To convert from dp to pixels,
float heightInPixel = getHeight()/ getResources().getDisplayMetrics().density;
Related
I believe my problem is related to this: widthMeasureSpec is 0 when in HorizontalScrollView
Long story short, I'm trying to create a custom horizontal scroll view. At some point, I'm overriding
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
And while this works fine in Android >= 7, I'm getting a value of 0 for widthMeasureSpec in Android <= 6.
The code for measureChild changed between API 23 and 24:
In API 23:
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
In API 24:
final int childWidthMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
Math.max(0, MeasureSpec.getSize(parentWidthMeasureSpec) - horizontalPadding),
MeasureSpec.UNSPECIFIED);
I have tried to get the width from the parent view using a getter, but it is not working as expected as only some calls get the correct width. I also tried to override measureChild in the parent and putting the newest code there, with no luck:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChild(getTouchInterceptTextView(), widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
final int horizontalPadding = getPaddingLeft() + getPaddingRight();
final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
Math.max(0, MeasureSpec.getSize(parentWidthMeasureSpec) - horizontalPadding),
MeasureSpec.UNSPECIFIED);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
getPaddingTop() + getPaddingBottom(), lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
My wholes files are here: https://github.com/AdrienPoupa/VinylMusicPlayer/commit/ba0dfae7dd03771f3625b8393927986aad552e2a
The important part being:
<com.poupa.vinylmusicplayer.views.TouchInterceptHorizontalScrollView
android:id="#+id/title_scrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.poupa.vinylmusicplayer.views.AutoTruncateTextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:textAppearance="#style/TextAppearance.AppCompat.Subhead" />
</com.poupa.vinylmusicplayer.views.TouchInterceptHorizontalScrollView>
The onMeasure function is in AutoTruncateTextView. I can get the measures in TouchInterceptHorizontalScrollView but that does not seem to help. I tried to add android:fillViewport="true" in the XML for TouchInterceptHorizontalScrollView, AutoTruncateTextView and both but it did not change the calculation.
What can I do to get the proper value of widthMeasureSpec in Android 6 and below?
Thanks for your help!
After many tries, it seems to be working by adding the following snippet to AutoTruncateTextView's onMeasure:
if (textBoundsWidth == 0) {
textBoundsWidth = getTouchInterceptFrameLayout().getMeasuredWidth();
}
Basically, getting the width of the TouchInterceptFrameLayout, that is the 2nd level of the XML file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="72dp"
android:foreground="?attr/rectSelector"
tools:ignore="UnusedAttribute">
<!-- for getSwipeableContainerView() -->
<com.poupa.vinylmusicplayer.views.TouchInterceptFrameLayout
android:id="#+id/dummy_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
and keeping the code posted above otherwise the truncate mechanism does not work anymore.
I need a view (custom view called DayView) that stretches twice the height of the scrollView it's in. In this view, there are two nested views, one of which is another custom view. The custom views all extend from RelativeLayout.
The layout is like this:
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Is twice the size as scrollView, see code below -->
<com.example.DayView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Should be same size as parent DayView -->
<include layout="#layout/layout_dayplan_background"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- Should be same size as parent DayView -->
<com.example.BlockView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.example.DayView>
</ScrollView>
The two nested views (include and BlockView) should have the same height as the parent DayView.
In the DayView, I override the onMeasure to make it twice as large. This works: (see the '* 2' in the second to last row)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, parentHeight);
this.setLayoutParams(new ScrollView.LayoutParams(parentWidth, parentHeight * 2 ));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
The DayView has the correct size, however, the two nested views don't strech along with the parent, but seem to use a wrap_content height layout rule.
Question
How can I make these two nested views take over the same height as the parent DayView?
You can try following methods
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec) * 2;
this.setMeasuredDimension(parentWidth, parentHeight);
measureChildren(MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(parentHeight, MeasureSpec.EXACTLY));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Here it is measuring children too. Maybe this can work
I want to customize a layout and make it into a square. In xml, I set the its layout_width and layout_height into 'match_parent'. And then in its onMeasure() method, I get the values of its height and width and set the shortest of them as the value of each side of square. The code is kind of like this:
<SquaredRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
/>
public class SquaredRelativeLayout extends RelativeLayout {
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(width > height) {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
} else {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
}
Now here is my problem: I need to set this layout center horizontally of its parent. But every time when width > height, the layout will be aligned to left of its parent. Does any one how to solve this?
Use android:gravity="center" in your SquaredRelativeLayout's parent
or
use android:layout_centerInParent="true" in your SquaredRelativeLayout (instead of android:layout_centerHorizontal="true")
Use android:gravity="center" in your parent layout.
I have created a custom layout which extends RelativeLayout. At the moment, this layout does nothing except overriding the onMeasure method (that I will need later). Here is my code:
public class CustomLayout extends RelativeLayout {
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
}
And I have the following layout file:
<LinearLayout
android:id="#+id/containerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal" >
<...CustomLayout
android:id="#+id/item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="50"
android:background="#fff" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello"
android:textSize="20sp" />
</...CustomLayout>
<RelativeLayout
android:id="#+id/controls"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="50"
android:background="#0f0" >
</RelativeLayout>
</LinearLayout>
The problem is that the height is null. During my debugging, I have noticed that if I change the CustomLayout to a RelativeLayout it works.
So, it is quite obvious that the problem comes from my CustomLayout, but what do I need to add to make the wrap_content work ?
I have finally found the problem. I change this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
To this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
And it works fine.
Explanation
First, the following line was useless:
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
But why does it makes the View's height null?
In fact, I misunderstood the paramaters of the onMeasure method. They are MeasureSpec values. Here is what Google says about this object:
A MeasureSpec encapsulates the layout requirements passed from parent
to child. Each MeasureSpec represents a requirement for either the
width or the height. A MeasureSpec is comprised of a size and a mode.
There are three possible modes:
UNSPECIFIED The parent has not imposed any constraint on the child. It
can be whatever size it wants. EXACTLY The parent has determined an
exact size for the child. The child is going to be given those bounds
regardless of how big it wants to be. AT_MOST The child can be as
large as it wants up to the specified size. MeasureSpecs are
implemented as ints to reduce object allocation. This class is
provided to pack and unpack the tuple into the int
The widthMeasureSpec and heightMeasureSpec values are not only the width and the height of the View. In this case, passing these values to setMeasureDimension are completly wrong because this method actually requires real sizes as parameters.
I created a custom View, and I put this on a LinearLayout.
Problem is that I cannot fill completely the height of the layout, the size of the custom View is always squared, width = height.
Here is my layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:layout_weight="0">
<it.inav.graphics.MapView
android:id="#+id/map"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"/>
</LinearLayout>
The only way it seems to work is using a RelativeLayout and "strecth" the view using both
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
but still, if I try to get the size height, it returns the same length of before.
The problem was in the definition of the View, and not in the XML.
I have done copy-paste of this piece of code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredWidth = measure(widthMeasureSpec);
int measuredHeight = measure(heightMeasureSpec);
int d = Math.min(measuredWidth, measuredHeight);
setMeasuredDimension(d, d);
}
And, naturally, it sets the size of the View as a square with the length of the minor.
This is the correct code:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredWidth = measure(widthMeasureSpec);
int measuredHeight = measure(heightMeasureSpec);
setMeasuredDimension(measuredWidth, measuredHeight);
}