Пользовательский макет: вложенные представления отображаются на весь экран

#android #layout #android-framelayout

#Android #макет #android-framelayout

Вопрос:

Я создаю пользовательский класс layout, и представления в макете отображаются должным образом. Проблема, с которой я сталкиваюсь, заключается в том, что если я включу другой макет в этот, размер всех дочерних элементов вложенного представления будет в основном полноэкранным.

XML-код макета — содержимое в FrameLayout не имеет правильного размера.

 <CustomLayout>
<ImageView android:layout_height="wrap_content" android:layout_width="wrap_content"/>

<FrameLayout android:layout_height="wrap_content" android:layout_width="wrap_content">
  <!-- The views in here are sizing themselves as full screen. -->
  <include layout="@layout/panel_emptypicture" android:id="@ id/EmptyPictureRelativeLayout" android:visibility="visible" />
</FrameLayout>
<EditText android:layout_height="wrap_content" android:layout_width="wrap_content" />
<EditText android:layout_height="wrap_content" android:layout_width="wrap_content" />
</CustomLayout>
  

Java-код для пользовательского макета.

 @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    final int numChildren = getChildCount();

    Dimension d = new Dimension(0, 0, this.getWidth(), this.getHeight());
    final float scaleX = this.getWidth() / virtualWidth;
    final float scaleY = this.getHeight() / virtualHeight;

    for(int i = 0; i < numChildren; i  ) {
        final View child = getChildAt(i);
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        final Dimension lpd = lp.getDimension();

        Dimension dest = lpd.scale(scaleX, scaleY, d);
        child.layout(dest.x, dest.y, dest.width   dest.x, dest.height   dest.y);
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int width, height;
    float maxWidth = MeasureSpec.getSize(widthMeasureSpec);
    float maxHeight = MeasureSpec.getSize(heightMeasureSpec);

    final float widthVirtualMultiplier = virtualWidth / virtualHeight;
    final float heightVirtualMultiplier = virtualHeight / virtualWidth;

        if(maxWidth * heightVirtualMultiplier < maxHeight) {
            width = (int)maxWidth;
            height = (int) (width * heightVirtualMultiplier);
        } else {
            height = (int)maxHeight;
            width = (int)(height * widthVirtualMultiplier);
        }


    setMeasuredDimension(width, height);

    // Tried adding this to see if it would do anything. Nope, no effect.
    final int numChildren = getChildCount();
    for(int i = 0; i < numChildren; i  ) {
        final View child = getChildAt(i);
        measureChild(child, widthMeasureSpec, heightMeasureSpec);
    }

}
  

Комментарии:

1. можете ли вы опубликовать panel_emptypicture.xml ? Кроме того, какой класс расширяет CustomLayout?

2. panel_emptypicture.xml это просто относительный макет с несколькими кнопками в каждых 4 углах. CustomLayout расширяет ViewGroup. Он был смоделирован после FrameLayout, но имеет совершенно другие параметры Layout.

Ответ №1:

В итоге получился код, который был достаточно стабилен для моих нужд. Ошибка все еще была, но ладно.

Весь проект размещен здесь, если вам нужны другие классы, на которые есть ссылки. http://code.google.com/p/motivatormaker-android

 package com.futonredemption.makemotivator.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;

import com.futonredemption.makemotivator.util.RectUtils;

public class ScaledCompositeLayout extends ViewGroup {

    @ViewDebug.ExportedProperty(category = "virtual-measurement")
    public float virtualHeight;

    @ViewDebug.ExportedProperty(category = "virtual-measurement")
    public float virtualWidth;

    @ViewDebug.ExportedProperty(category = "clamp-width")
    protected boolean clampOnWidth;


    public ScaledCompositeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScaledCompositeLayout(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);

        final TypedArray styleValues = context.obtainStyledAttributes(
                        attrs,
                        com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout,
                        defStyle, 0);

        virtualWidth = styleValues.getDimension(
                        com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_VirtualWidth,
                        -1);
        virtualHeight = styleValues.getDimension(
                        com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_VirtualHeight,
                        -1);
        clampOnWidth = styleValues.getBoolean(
                com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_ClampOnWidth,
                false);

        styleValues.recycle();

        if(virtualWidth == -1) {
            throw new IllegalStateException("virtualWidth is required parameter.");
        }
        if(virtualHeight == -1) {
            throw new IllegalStateException("virtualHeight is required parameter.");
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int numChildren = getChildCount();

        Rect parentRect = new Rect(0, 0, this.getWidth(), this.getHeight());
        final float scaleX = this.getWidth() / virtualWidth;
        final float scaleY = this.getHeight() / virtualHeight;

        for(int i = 0; i < numChildren; i  ) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final RectF lpr = lp.getRectF();

            Rect dest = RectUtils.scale(lpr, scaleX, scaleY, new RectF(parentRect));
            child.layout(dest.left, dest.top, dest.right, dest.bottom);

            //Dimension out = dest;
            //throw new IllegalStateException(String.format("Position: %d x %d -- Size: %d x %d", out.x, out.y, out.width, out.height));
        }
    }

    /**
     * Uses the Virtual Width and Height to determine the ratio size of the actual layout.
     * The layout_width and layout_height parameters are used to determine the maximum bounds.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int width, height;
        //int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        //int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        float maxWidth = MeasureSpec.getSize(widthMeasureSpec);
        float maxHeight = MeasureSpec.getSize(heightMeasureSpec);

        if (maxHeight == 0.0F) {
            clampOnWidth = true;
        }

        final float widthVirtualMultiplier = virtualWidth / virtualHeight;
        final float heightVirtualMultiplier = virtualHeight / virtualWidth;

        //final float widthActualMultiplier = maxWidth / maxHeight;
        //final float heightActualMultiplier = maxHeight / maxWidth;
        /*
        if(widthMode == MeasureSpec.EXACTLY) {
            width = (int)maxWidth;
            height = (int) (maxWidth * heightVirtualMultiplier);
        } else if(heightMode == MeasureSpec.EXACTLY) {
            width = (int)(maxHeight * widthVirtualMultiplier);
            height = (int)maxHeight;
        } else { // Use scaling */
            if(clampOnWidth || maxWidth * heightVirtualMultiplier < maxHeight) {
                width = (int)maxWidth;
                height = (int) (width * heightVirtualMultiplier);
            } else {
                height = (int)maxHeight;
                width = (int)(height * widthVirtualMultiplier);
            }
        //}

        setMeasuredDimension(width, height);

        final int numChildren = getChildCount();
        final float scaleX = width / virtualWidth;
        final float scaleY = height / virtualHeight;
        final Rect parentRect = new Rect(0, 0, this.getWidth(), this.getHeight());

        for(int i = 0; i < numChildren; i  ) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final RectF lpr = lp.getRectF();

            Rect dest = RectUtils.scale(lpr, scaleX, scaleY, new RectF(parentRect));

            this.measureChild(child, MeasureSpec.makeMeasureSpec(dest.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dest.height(), MeasureSpec.EXACTLY));

            //Dimension out = dest;
            //throw new IllegalStateException(String.format("Position: %d x %d -- Size: %d x %d", out.x, out.y, out.width, out.height));
        }

        /*
        for(int i = 0; i < numChildren; i  ) {
            final View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
        */

        //throw new IllegalStateException(String.format("%d x %d -- Physical: %.0f x %.0f -- Virtual: %.0f x %.0f", width, height, maxWidth, maxHeight, virtualWidth, virtualHeight));
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new ScaledCompositeLayout.LayoutParams(getContext(), attrs);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(
            ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    public static class LayoutParams extends
            android.view.ViewGroup.LayoutParams {

        public RectF virtualRect = new RectF();

        public RectF getRectF() {
            return virtualRect;
        }

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            float virtualWidth;
            float virtualHeight;

            TypedArray styleValues = c
                    .obtainStyledAttributes(
                            attrs,
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout);
            virtualRect.top = styleValues
                    .getDimension(
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualTop,
                            0);
            virtualRect.left = styleValues
                    .getDimension(
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualLeft,
                            0);
            virtualRect.right = styleValues
                    .getDimension(
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualRight,
                            0);
            virtualRect.bottom = styleValues
                    .getDimension(
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualBottom,
                            0);

            virtualWidth = styleValues
                    .getDimension(
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualWidth,
                            -1);
            if (virtualWidth > 0) {
                virtualRect.right = virtualRect.left   virtualWidth;
            }

            virtualHeight = styleValues
                    .getDimension(
                            com.futonredemption.makemotivator.R.styleable.ScaledCompositeLayout_Layout_VirtualHeight,
                            -1);
            if (virtualHeight > 0) {
                virtualRect.bottom = virtualRect.top   virtualHeight;
            }

            styleValues.recycle();
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }
    }
}