動的でありながら正方形のレイアウトを行う簡単な方法


84

GridViewは本質的にであるビューの束を表示するためにを使用していますLinearLayoutsLinearLayoutsすべてを正方形にしたいが、動的なサイズにすることも必要です。つまり、2つの列があり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年

5
Math.min(widthMeasureSpec、heightMeasureSpec)の値をsuper.onMeasure()の両方の引数に渡した場合、これはより柔軟になります
Paul Wintz 2017

これはアクティビティでは簡単に機能しますが、アプリウィジェットでは機能しません。
Apollo-Roboto

73

答えるのが遅いかもしれませんが、それでも、それは新しい訪問者に役立ちます。

Android Studio 2.3で導入された新しいConstraintLayoutにより、レスポンシブレイアウトの構築が非常に簡単になりました。

親のConstraintLayoutで、子のビュー/レイアウトを動的に正方形にするには、この属性を追加します

app:layout_constraintDimensionRatio="w,1:1"

wは幅方向の制約を指定し、1:1の比率で正方形のレイアウトを保証します。


9
これは王様の答えです
itzhar 2018

2
既存のレイアウトを正方形として表示したい場合の一番の答え!
Quentin Klein

9
子レイアウトのレイアウトの幅と高さは必ず「0dp」に設定してください。そうでなければ、それは機能しません。
thilinaKj18年

また、私は意味ビュー自体のために働くあなたが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


ええ、私は同じようなことをすることになりました。ありがとう!
キャサリン

1
これがうまくいったら、これを正解としてマークしていただけますか?
David Merriman 2012年

1
@Catherineちょっとキャサリン、スニペットを投稿できますか?多分それは他の人にも役立つでしょう:
Marek Sebera 2013年

3
追加することを忘れないでください:super.onMeasure(widthMeasureSpec, widthMeasureSpec);
deKajoo 2014年

1
@deKajooを使用super.onMeasure(widthMeasureSpec, widthMeasureSpec);すると正方形が得られますが、常に幅x幅です。与えられた測定値が高さよりも広い場合、問題が発生します。私のソリューションでは、2つの測定値のうち小さい方を使用するため、指定された長方形内に収まる最大の正方形が常に得られます。
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を拡張するクラスでオーバーライドされますか?
ドロイド

正方形にしたいクラスでTtがオーバーライドされます。質問の場合、それはGridView内のLinearLayoutsです。
フェルナンドカマルゴ2014

これは、StaggeredGridLayoutで正方形のビューを使用する際のいくつかの奇妙な問題を修正するのに役立ちました。私のもっと素朴な実装は十分ではありませんでした。ありがとう!
Peterdk 2016

10

非常に簡単な方法でそれを行うことができます-2super.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回呼び出すと、描画プロセスの効率は低下しますが、他の回答が引き起こす可能性のあるレイアウトの問題を修正する簡単な方法です。


2
super.onMeasureを2回呼び出す代わりに、2回目はsetMeasuredDimension(squareLen、squareLen);を呼び出すだけで済みます。
user3802077 2016年

super.onMeasure()2回呼び出すと、ビューの新しいサイズを指定してレイアウトが正しく行われるようになります。これがないと、別の方法でレイアウトをトリガーするか、誤ったレイアウトを処理する必要があります。
リチャードルメスリエ2018

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

さまざまなレイアウトのラッパークラスを提供するAndroidライブラリであるSquareLayoutをチェックして、コア機能を失うことなく、それらを2乗次元でレンダリングします。

寸法はレイアウトがレンダリングされる直前に計算され、したがって、ビューが得られた後に調整するなどの一切再レンダリングまたは何でもありません。

ライブラリを使用するには、これを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.