I'm using a GridView to display a bunch of views which are essentially LinearLayouts. I want the LinearLayouts to all be square, but I also want them to be dynamically sized--that is, there are two columns and I want the LinearLayouts to stretch depending on the size of the screen but remain square. Is there a way to do this through the xml layout or do I have to set the heights and widths programmatically?
A neat solution for square GridView items is to extend RelativeLayout or LinearLayout and override onMeasure like so:
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
With the new ConstraintLayout introduced in Android Studio 2.3, it is now quite easy to build responsive layouts.
In a parent ConstraintLayout, to make any of its children view/layout dynamically square, add this attribute
app:layout_constraintDimensionRatio="w,1:1"
w is to specify width-wise constraints and 1:1 ratio ensures square layout.
I've done this way:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int size;
if(widthMode == MeasureSpec.EXACTLY && widthSize > 0){
size = widthSize;
}
else if(heightMode == MeasureSpec.EXACTLY && heightSize > 0){
size = heightSize;
}
else{
size = widthSize < heightSize ? widthSize : heightSize;
}
int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(finalMeasureSpec, finalMeasureSpec);
}
With this implementation, your layout will be square, assuming the lower size between width and height. And it can even be set with dynamic values, like using weight inside a LinearLayout.
There's nothing in the xml that will let you link the width and height properties. Probably the easiest thing to do is to subclass LinearLayout and override onMeasure
#Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = width > height ? height : width;
setMeasuredDimension(size, size);
}
I've used this to create views that are always square before. It should still work for a LinearLayout.
More info that will help doing this:
http://developer.android.com/guide/topics/ui/custom-components.html
http://developer.android.com/reference/android/view/View.MeasureSpec.html
We can do it with a very simple way - just call super.onMeasure() twice.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int squareLen = Math.min(width, height);
super.onMeasure(
MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(squareLen, MeasureSpec.EXACTLY));
}
By calling super.onMeasure() twice, this is less efficient in terms of the drawing process, but it is a simple way to fix layout issues that the other answers can cause.
It is as simple as:
public class SquareRelativeLayout extends RelativeLayout {
public SquareRelativeLayout(Context context) {
super(context);
}
public SquareRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (widthMeasureSpec < heightMeasureSpec)
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
else
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}
Add the following line in XML:
app:layout_constraintDimensionRatio="1:1"
Here's a solution that works for all layout parameters that can be set to view or viewgroup:
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthDesc = MeasureSpec.getMode(widthMeasureSpec);
int heightDesc = MeasureSpec.getMode(heightMeasureSpec);
int size = 0;
if (widthDesc == MeasureSpec.UNSPECIFIED
&& heightDesc == MeasureSpec.UNSPECIFIED) {
size = DP(defaultSize); // Use your own default size, in our case
// it's 125dp
} else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED)
&& !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) {
//Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0)
size = width > height ? width : height;
} else {
//In all other cases both dimensions have been specified so we choose the smaller of the two
size = width > height ? height : width;
}
setMeasuredDimension(size, size);
Cheers
My suggestion is to create a custom layout class that inherits from FrameLayout. Override the OnMeasure() method and put whatever control you want to be square inside that SquareFrameLayout.
This is how it's done in Xamarin.Android:
public class SquareFrameLayout : FrameLayout
{
private const string _tag = "SquareFrameLayout";
public SquareFrameLayout(Android.Content.Context context):base(context) {}
public SquareFrameLayout(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer):base(javaReference, transfer) {}
public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs):base(context, attrs) {}
public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr):base(context, attrs, defStyleAttr) {}
public SquareFrameLayout(Android.Content.Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes):base(context, attrs, defStyleAttr, defStyleRes) {}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
int widthSize = MeasureSpec.GetSize(widthMeasureSpec);
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
int heightSize = MeasureSpec.GetSize(heightMeasureSpec);
int width, height;
switch (widthMode)
{
case MeasureSpecMode.Exactly:
width = widthSize;
break;
case MeasureSpecMode.AtMost:
width = Math.Min(widthSize, heightSize);
break;
default:
width = 100;
break;
}
switch (heightMode)
{
case MeasureSpecMode.Exactly:
height = heightSize;
break;
case MeasureSpecMode.AtMost:
height = Math.Min(widthSize, heightSize);
break;
default:
height = 100;
break;
}
Log.Debug(_tag, $"OnMeasure({widthMeasureSpec}, {heightMeasureSpec}) => Width mode: {widthMode}, Width: {widthSize}/{width}, Height mode: {heightMode}, Height: {heightSize}/{height}");
var size = Math.Min(width, height);
var newMeasureSpec = MeasureSpec.MakeMeasureSpec(size, MeasureSpecMode.Exactly);
base.OnMeasure(newMeasureSpec, newMeasureSpec);
}
}
If you want a View (or any other control) to be square (and centered) just add it to your layout the following way:
<your.namespace.SquareFrameLayout
android:id="#+id/squareContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<View
android:id="#+id/squareContent"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</your.namespace.SquareFrameLayout>
Check out SquareLayout, an Android Library which provides a wrapper class for different Layouts, rendering them Squared dimensioned without losing any core functionalities.
The dimensions are calculated just before the Layout is rendered, hence there is no re-rendering or anything as such to adjust once the View is obtained.
To use the Library, add this to your build.gradle:
repositories {
maven {
url "https://maven.google.com"
}
}
dependencies {
compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3'
}
The one you require is SquareLinearLayout.
For anyone wants solution With Kotlin, here's what I did with FrameLayout.
package your.package.name
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
class SquareLayout: FrameLayout {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
if (widthMeasureSpec < heightMeasureSpec)
super.onMeasure(widthMeasureSpec, widthMeasureSpec)
else
super.onMeasure(heightMeasureSpec, heightMeasureSpec)
}
}
Try this code:
public class SquareRelativeLayout extends RelativeLayout {
public SquareRelativeLayout(Context context) {
super(context);
}
public SquareRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int size;
if (widthMode == MeasureSpec.EXACTLY && widthSize > 0) {
size = widthSize;
} else if (heightMode == MeasureSpec.EXACTLY && heightSize > 0) {
size = heightSize;
} else {
size = widthSize < heightSize ? widthSize : heightSize;
}
int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(finalMeasureSpec, finalMeasureSpec);
}
}
<androidx.constraintlayout.widget.ConstraintLayout
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" >
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="#tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
Related
I've read many other SO answers but nothing seems to be what I want. What I want is a ViewPager inside a ScrollView with the heights of each page being appropriate for the content. Some of the more accepted answers on SO seem to have to take the max height of the children in the ViewPager but that leads for empty space.
WrapContentViewPager
public class WrapContentViewPager extends ViewPager {
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
View view = null;
for(int i = 0; i < getChildCount(); i++) {
view = getChildAt(i);
view.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = view.getMeasuredHeight();
if(h > height) height = h;
}
if (height != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), measureHeight(heightMeasureSpec, view));
}
/**
* Determines the height of this view
*
* #param measureSpec A measureSpec packed into an int
* #param view the base view with already measured height
*
* #return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec, View view) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
// set the height from the base view if available
if (view != null) {
result = view.getMeasuredHeight();
}
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
}
The reason why this doesn't work is because if I go to the third page, which has a large height with 32 items in the list, then go back to the second page with only 3 items, there is a lot of empty space in the second page since it took the height of the third page as max.
I've tried WCViewPager library on GitHub at https://github.com/rnevet/WCViewPager and it doesn't work for me as well.
Could someone guide me to a correct solution? I am using a ViewPager with just PagerAdapter, not FragmentPagerAdapter by the way.
UPDATE
I figured out a better way to solve it.
`public class WrapContentViewPager extends ViewPager {
public WrapContentViewPager(Context context) {
super(context);
}
public WrapContentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
// Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
// At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
// super has to be called in the beginning so the child views can be initialized.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int position = getCurrentItem();
View child = this.findViewWithTag("view"+position);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int height = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
// super has to be called again so the new specs are treated as exact measurements
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}`
and in my PagerAdapter I changed the method instantiateItem
#Override
public #NonNull Object instantiateItem(#NonNull ViewGroup view, int position) {
View layout = inflater.inflate(R.layout.layout, view, false);
layout.setTag("view" + position);
}
This solution remeasures the height at the current page every time it is moved. getChildAt(getCurrentItem()) gives different results so it's not reliable.
I tried to refer[https://medium.com/#nikhil4092/how-to-have-a-height-wrapping-viewpager-when-images-have-variable-heights-on-android-60b18e55e72e] this link for making the viewpager's height wrap_content but it didn't work.I've tried several questions of stackoverflow but none of them could address my problem.When I'm giving the height as wrap_content nothing is shown
Code:
public class HeightWrappingViewPager extends ViewPager {
public HeightWrappingViewPager(Context context) {
super(context);
}
public HeightWrappingViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
// Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
// At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
// super has to be called in the beginning so the child views can be initialized.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if (h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
// super has to be called again so the new specs are treated as exact measurements
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
XML:
<com.project.test.HeightWrappingViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
I'm not sure is it helps you.
I used the code below to images with different height.
Almost like your code, but i save height like a field.
public class MeasuredViewPager extends ViewPager {
private int mMaxHeight = 0;
public MeasuredViewPager(Context context) {
super(context);
}
public MeasuredViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if (h > mMaxHeight) mMaxHeight = h;
}
if (mMaxHeight != 0) heightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Problem
I want to use RecyclerView to implement GridView.In fact I need to display all local photos in RecyclerView which has 3 columns.I know about GridLayoutManager
My code :
mManager = new GridLayoutManager(this,3);
And the item XML:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
however how can I set the item view (ImageView)'s width and height?
Solution
I define a custom view:
public class RatioImageView extends ImageView {
private int originalWidth;
private int originalHeight;
public RatioImageView(Context context) {
super(context);
}
public RatioImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setOriginalSize(int originalWidth, int originalHeight) {
this.originalWidth = originalWidth;
this.originalHeight = originalHeight;
}
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (originalWidth > 0 && originalHeight > 0) {
float ratio = (float) originalWidth / (float) originalHeight;
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (width > 0) {
height = (int) ((float) width / ratio);
}
setMeasuredDimension(width, height);
}
else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Is there another solution?
Instead of creating Custom ImageView when you are creating your view inside onCreateViewHolder() method by inflating your recycler item view, you can set width and height to your ImageView using layoutParamas.
If you want 3 columns then you can get screen width and divide it into 3 and pass that to your RecyclerView Adapter and use it in onCreateViewHolder() to apply.
I have one HorizontalScrollView and one GridLayout inside with 3 images. It never scrolls as I thought it should, no matter what I do - with gestures, touch - nothing would make the horizontal scroll.
My XML:
<com.app4u.borala.atividades.layout.GridEventoPesqGeo
android:id="#+id/horizontalScroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:background="#color/azulActionBarTransparente"
android:fillViewport="true"
android:scrollbars="horizontal" >
<GridLayout
android:id="#+id/gridEventos"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rowCount="1" >
<com.app4u.borala.atividades.layout.ImageViewEventoPesqGeo
android:id="#+id/imageEvento1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/background_evento_temp" />
<com.app4u.borala.atividades.layout.ImageViewEventoPesqGeo
android:id="#+id/imageEvento2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/background_evento_temp" />
<com.app4u.borala.atividades.layout.ImageViewEventoPesqGeo
android:id="#+id/imageEvento3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/background_evento_temp" />
</GridLayout>
</com.app4u.borala.atividades.layout.GridEventoPesqGeo>
My class GridEventoPesqGeo:
public class GridEventoPesqGeo extends HorizontalScrollView {
public GridEventoPesqGeo(Context context) {
super(context);
}
public GridEventoPesqGeo(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GridEventoPesqGeo(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, (int) (parentHeight * 0.225));
}
}
My class ImageViewEventoPesqGeo:
public class ImageViewEventoPesqGeo extends ImageView{
public ImageViewEventoPesqGeo(Context context) {
super(context);
}
public ImageViewEventoPesqGeo(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ImageViewEventoPesqGeo(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
//int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
MarginLayoutParams lpimgFooter = (MarginLayoutParams) getLayoutParams();
lpimgFooter.bottomMargin = (int) (parentWidth * 0.015);
lpimgFooter.leftMargin = (int) (parentWidth * 0.015);
lpimgFooter.rightMargin = (int) (parentWidth * 0.015);
lpimgFooter.topMargin = (int) (parentWidth * 0.015);
setLayoutParams(lpimgFooter);
this.setMeasuredDimension((int) (parentWidth * 0.33), (int) (parentWidth * 0.33));
}
}
My Java classes just make the width and height proportional depending on the phone's screen size..
Here is the source of HorizontalScrollView onMeasure():
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!mFillViewport) {
return;
}
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED) {
return;
}
if (getChildCount() > 0) {
final View child = getChildAt(0);
int width = getMeasuredWidth();
if (child.getMeasuredWidth() < width) {
final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mPaddingTop
+ mPaddingBottom, lp.height);
width -= mPaddingLeft;
width -= mPaddingRight;
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
Notice that if mFillViewPort is true, it'll return without ever reaching your code. Since you set it to true in xml, no wonder your code is not executed.
I'm writing a custom View object, but I can't seem to get it to measure correctly. By looking at the View source code, I thought calling setMinimumHeight() and setMinimumWidth() would be enough (that's really all I need, a minimum size that the parent layout should respect). Here's my code:
public class MonthView extends View {
private final int minCellSize = 24;
public MonthView(Context context) {
super(context);
}
public MonthView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MonthView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final float scale = getContext().getResources().getDisplayMetrics().density;
setMinimumHeight((int) (minCellSize * scale * 6));
setMinimumWidth((int) (minCellSize * scale * 7));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.RED);
}
}
Pretty simple. I then embed it in a LinearLayout, something like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.foghina.adtp.MonthView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="I am below the monthview!"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
However, the MonthView takes up the entire screen and the TextView is not visible. How can I correctly write my View so that it has a minimum height / width when wrap_content is used?
I figured it out eventually. I had to write my own version of View.getDefaultSize() that is just slightly different. Here's how I used it:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final float scale = getContext().getResources().getDisplayMetrics().density;
setMeasuredDimension(getSize((int) (minCellSize * scale * 7), widthMeasureSpec),
getSize((int) (minCellSize * scale * 6), heightMeasureSpec));
}
private static int getSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
result = size < specSize ? size : specSize;
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Pretty nasty. I don't understand why the default View.getDefaultSize() doesn't work like that.
I think you must use setMeasureDimension()