동적이지만 정사각형 레이아웃을 수행하는 간단한 방법


84

나는 GridView본질적으로 많은 뷰를 표시 하기 위해 사용하고 있습니다 LinearLayouts. 나는 LinearLayouts모두 정사각형이 되길 원하지만 동적으로 크기가 조정되기를 원합니다 LinearLayouts. xml레이아웃을 통해이 작업을 수행하는 방법이 있습니까? 아니면 프로그래밍 방식으로 높이와 너비를 설정해야합니까?

답변:


113

정사각형 GridView항목에 대한 깔끔한 솔루션은 다음 과 같이 확장 RelativeLayout하거나 LinearLayout재정의하는 onMeasure것입니다.

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

2
이것은 setMeasuredDimension ()을 사용하는 대신 측정 사양을 사용하므로 좋습니다. 그러나 항상 너비를 취하기 때문에 유연하지 않습니다.
아담

3
@DavidMerriman이 위에서 말했듯이, 뷰가 높이보다 넓 으면 뷰가 뷰 영역 아래로 확장되어 뷰의 일부가 잘립니다.
vcapra1 2015-06-18

5
이 super.onMeasure ()의 두 인수로 Math.min (widthMeasureSpec, heightMeasureSpec)의 값을 전달 경우에 더 유연 할 것
폴 Wintz

이것은 활동에서는 쉽게 작동하지만 앱 위젯에서는 작동하지 않습니다.
Apollo-Roboto

73

답변이 늦어 질 수 있지만 그래도 새로운 방문객에게 도움이 될 것입니다.

Android Studio 2.3에 도입 된 새로운 ConstraintLayout을 사용하면 이제 반응 형 레이아웃을 매우 쉽게 빌드 할 수 있습니다.

부모 ConstraintLayout에서 자식 뷰 / 레이아웃을 동적으로 정사각형으로 만들려면이 속성을 추가합니다.

app:layout_constraintDimensionRatio="w,1:1"

w는 너비 방향 제약을 지정하는 것이며 1 : 1 비율은 정사각형 레이아웃을 보장합니다.


9
이 왕의 대답
itzhar

2
기존 레이아웃을 정사각형으로 표시하려는 경우 최고의 답변!
Quentin Klein

9
자식 레이아웃의 레이아웃 너비와 높이를 "0dp"로 설정해야합니다. 그렇지 않으면 작동하지 않습니다.
thilina Kj

또한 뷰 자체에서도 작동합니다. contrantLayout의 자식이 정사각형이되도록하려면 layout_constraintDimensionRatio 속성을 자식 뷰에서 직접 정의 할 수 있습니다
Plinio.Santos

1
해결책에 대단히 감사합니다. 솔루션을 사용하여 내 코드의 전체 요지를 해결하는 데 도움이되는지 확인했습니다. gist.github.com/nadar71/006699e31ef7451813e6c97dacfbc5e2
android_dev71

44

xml에는 width 및 height 속성을 연결할 수있는 것이 없습니다. 아마도 가장 쉬운 방법은 하위 클래스를 LinearLayout만들고 재정의하는 것입니다.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);
}

나는 이것을 사용하여 항상 이전에 정사각형 인 뷰를 만들었습니다. 여전히 LinearLayout.

이를 수행하는 데 도움이되는 추가 정보 : http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html


네, 결국 비슷한 일을하게되었습니다. 감사!
Catherine

1
이것이 효과가 있다면 정답으로 표시 할 수 있습니까?
David Merriman

1
@Catherine 안녕하세요 Catherine, 스 니펫을 게시 할 수 있습니까? 아마 :-) 다른 사람을 위해 도움이 될 것입니다
마렉 Sebera

3
추가하는 것을 잊지 마세요 : super.onMeasure(widthMeasureSpec, widthMeasureSpec);!
deKajoo

1
@deKajoo Using super.onMeasure(widthMeasureSpec, widthMeasureSpec);은 정사각형을 제공하지만 항상 너비 x 너비입니다. 주어진 치수가 키보다 넓 으면 문제가 발생합니다. 내 솔루션은 두 측정 중 더 작은 것을 사용하므로 항상 주어진 직사각형에 맞는 가장 큰 정사각형을 얻습니다.
David Merriman 2014 년

43

나는 이렇게했다 :

@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);
}

이 구현에서는 너비와 높이 사이의 크기가 더 낮다고 가정하여 레이아웃이 정사각형이됩니다. 또한 LinearLayout 내부에서 가중치를 사용하는 것과 같이 동적 값으로 설정할 수도 있습니다.


"onMeasure"메서드는 GridView를 확장하는 클래스에서 재정의됩니다.
An-droid

정사각형이되기를 원하는 클래스에서 Tt가 재정의되었습니다. 질문의 경우 GridView 내부의 LinearLayouts입니다.
Fernando Camargo

이것은 StaggeredGridLayout에서 정사각형보기를 사용할 때 몇 가지 이상한 문제를 해결하는 데 도움이되었습니다. 내 순진한 구현은 충분하지 않았습니다. 감사!
Peterdk

10

아주 간단한 방법으로 할 수 있습니다 super.onMeasure(). 두 번만 전화하면 됩니다.

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));
}

super.onMeasure()두 번 호출 하면 그리기 프로세스 측면에서 효율성이 떨어지지 만 다른 답변으로 인해 발생할 수있는 레이아웃 문제를 해결하는 간단한 방법입니다.


2
super.onMeasure를 두 번 호출하는 대신 두 번째로 setMeasuredDimension (squareLen, squareLen);
user3802077

super.onMeasure()두 번 호출 하면 뷰의 새로운 크기가 주어지면 레이아웃이 올바르게 수행됩니다. 이것이 없으면 다른 방식으로 레이아웃을 트리거하거나 잘못된 레이아웃을 처리해야합니다.
Richard Le Mesurier

4

다음과 같이 간단합니다.

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);
    }
}

1

다음은보기 또는보기 그룹으로 설정할 수있는 모든 레이아웃 매개 변수에 대해 작동하는 솔루션입니다.

    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);

건배


이 답변에는 몇 가지 흥미로운 특성이 있으며 잠재적으로 더 강력하지만 작업이 필요하며 다양한 슈퍼 클래스를 허용하므로 setMeasuredDimension () 대신 super.onMeasure ()에 전달되는 측정 사양을 구성해야합니다.
아담

1

내 제안은 FrameLayout에서 상속되는 사용자 정의 레이아웃 클래스를 만드는 것입니다. OnMeasure () 메서드를 재정의하고 SquareFrameLayout 안에 사각형이 될 컨트롤을 넣으십시오.

이것이 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);
    }
}

보기 (또는 다른 컨트롤)를 정사각형 (및 가운데)으로 만들려면 다음과 같은 방법으로 레이아웃에 추가하면됩니다.

<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>

1

XML에 다음 줄을 추가합니다.

app:layout_constraintDimensionRatio="1:1"

1
구성을 변경하면 항상 비율을 유지하므로 app : layout_constraintDimensionRatio = "w, 1 : 1"보다 낫기 때문에 동일한 답변을 게시하고 싶었습니다
Alexey Simchenko

0

확인 SquareLayout , 안드로이드 라이브러리다양한 레이아웃에 대한 래퍼 클래스를 제공 인 핵심 기능을 잃지 않고 Squared 차원으로 렌더링합니다.

치수는 단지 레이아웃이 렌더링되기 전에 계산 , 따라서보기를 얻을되면 조정하는 등 더 재 렌더링 또는 아무것도 없다.

라이브러리를 사용하려면 다음을 build.gradle에 추가하세요.

repositories {
    maven {
        url "https://maven.google.com"
    }
}

dependencies {
    compile 'com.github.kaushikthedeveloper:squarelayout:0.0.3'
}

필요한 것은 SquareLinearLayout 입니다.


실제 기기에서는 작동하지 않습니다. (Android Studio 미리보기에서는 잘 렌더링되지만 기기에서는 작동하지 않음
Eugene Voronoy

0

누구든지 솔루션을 원합니다 Kotlin을 사용하여 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)
    }
}

0

이 코드를 시도하십시오.

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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.