Dinamik ancak kare düzen yapmanın basit yolu


84

GridViewEsasen bir grup görünümü görüntülemek için a kullanıyorum LinearLayouts. İstediğim LinearLayoutsbütün dik olmalı, ama aynı zamanda onları dinamik büyüklüğünde olmak istiyorum - olduğundan, orada iki sütun vardır ve istediğim LinearLayoutsekranın boyutuna bağlı olarak germek için ancak kare kalır. Bunu xmldüzen aracılığıyla yapmanın bir yolu var mı yoksa yükseklikleri ve genişlikleri programlı olarak ayarlamam gerekiyor mu?

Yanıtlar:


113

Kare GridViewöğeler için düzgün bir çözüm , aşağıdaki gibi genişletmek RelativeLayoutveya LinearLayoutgeçersiz kılmaktır onMeasure:

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

2
Bu, setMeasuredDimension () yerine ölçü özelliğini kullanıyor, bu yüzden iyi. Ama her zaman genişliği aldığı için çok esnek değil.
Adam

3
@DavidMerriman'ın yukarıda da söylediği gibi, eğer görüş yüksekliğinden daha genişse bu, görüşün görüş alanının altına uzanmasına ve görüntünün bir kısmını kesmesine neden olacaktır.
vcapra1

5
Math.min (widthMeasureSpec, heightMeasureSpec) değerini super.onMeasure () değişkeninin her iki argümanına aktarırsa bu daha esnek olurdu
Paul Wintz

Bu, aktivitelerle kolayca çalışacak ancak bir uygulama widget'ıyla çalışmayacaktır.
Apollo-Roboto

73

Cevap vermek için geç olabilir ama yine de yeni ziyaretçilere yardımcı olacaktır.

Android Studio 2.3'te sunulan yeni ConstraintLayout ile, duyarlı düzenler oluşturmak artık oldukça kolay.

Bir üst ConstraintLayout'ta, alt öğelerinden herhangi birinin dinamik olarak kare şeklinde görüntülenmesi / düzenlenmesi için bu niteliği ekleyin

app:layout_constraintDimensionRatio="w,1:1"

w, genişlik sınırlamalarını belirtmektir ve 1: 1 oran, kare düzeni sağlar.


9
bu bir kral cevabı
itzhar

2
Mevcut bir düzeni kare olarak görüntülemek istiyorsanız en iyi cevap!
Quentin Klein

9
Alt düzende düzen genişliği ve yüksekliği için "0dp" ayarladığınızdan emin olun. Aksi takdirde işe yaramaz.
thilina Kj

Aynı zamanda görünümün kendisi için de işe yarar, yani bir contrantLayout'un çocuğunun kare olmasını istiyorsanız, layout_constraintDimensionRatio özniteliğini doğrudan çocuk görünümünde tanımlayabilirsiniz
Plinio.Santos

1
Çözüm için çok teşekkür ederim; Çözümünüzü
android_dev71

44

Xml'de genişlik ve yükseklik özelliklerini bağlamanıza izin verecek hiçbir şey yoktur. Muhtemelen yapılacak en kolay şey, alt sınıflara ayırmak LinearLayoutve geçersiz kılmaktıronMeasure

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

Bunu daha önce her zaman kare olan görünümler oluşturmak için kullandım. Yine de bir LinearLayout.

Bunu yapmaya yardımcı olacak daha fazla bilgi: http://developer.android.com/guide/topics/ui/custom-components.html http://developer.android.com/reference/android/view/View.MeasureSpec.html


Evet, benzer bir şey yaptım. Teşekkürler!
Catherine

1
Bu sizin için işe yaradıysa, bunu doğru cevap olarak işaretleyebilir misiniz?
David Merriman

1
@Catherine hey Catherine, pasajınızı gönderebilir misiniz? Belki başkaları için faydalı olabilir :-)
Marek Sebera

3
Değil eklemeyi unuttuğu yapın: super.onMeasure(widthMeasureSpec, widthMeasureSpec);!
deKajoo

1
@deKajoo Kullanmak super.onMeasure(widthMeasureSpec, widthMeasureSpec);size bir kare verir, ancak her zaman genişlik x genişliktir. Verilen ölçüler uzun olduklarından daha genişse, bir problem yaşarsınız. Benim çözümüm iki ölçümden daha küçük olanı kullanıyor, böylece her zaman verilen dikdörtgene sığacak en büyük kareyi elde edersiniz.
David Merriman

43

Bu şekilde yaptım:

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

Bu uygulama ile, genişlik ve yükseklik arasında daha düşük boyut varsayılarak düzeniniz kare olacaktır. Ve LinearLayout içinde ağırlık kullanmak gibi dinamik değerlerle bile ayarlanabilir.


"onMeasure" yöntemi, GridView?
An-droid

Kare olmasını istediğiniz sınıfta Tt geçersiz kılınmıştır. Sorunun durumu için, GridView içindeki LinearLayouts.
Fernando Camargo

Bu, StaggeredGridLayout'ta bir kare görünüm kullanırken bazı garip sorunları çözmeme yardımcı oldu. Daha saf uygulamam yeterince iyi değildi. Teşekkürler!
Peterdk

10

Bunu çok basit bir şekilde yapabiliriz - sadece super.onMeasure()iki kez arayın .

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()İki kez arayarak , bu, çizim süreci açısından daha az etkilidir, ancak diğer yanıtların neden olabileceği düzen sorunlarını düzeltmenin basit bir yoludur.


2
super.onMeasure'ı iki kez çağırmak yerine, ikinci kez yalnızca setMeasuredDimension (squareLen, squareLen) 'i çağırmanız gerekir;
user3802077

super.onMeasure()İki kez aramak , görünümün yeni boyutu verildiğinde mizanpajın doğru şekilde yapılmasını sağlar. Bu olmadan bir düzeni farklı bir şekilde tetiklememiz veya yanlış düzenlerle uğraşmamız gerekir.
Richard Le Mesurier

4

Bu kadar basit:

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

2

XML'de aşağıdaki satırı ekleyin:

app:layout_constraintDimensionRatio="1:1"

1
Aynı yanıtı göndermek istedim çünkü app'den daha iyi: layout_constraintDimensionRatio = "w, 1: 1" yapılandırmayı değiştirirseniz her zaman oranı
koruduğu için

1

İşte grubu görüntülemek veya görüntülemek için ayarlanabilen tüm düzen parametreleri için çalışan bir çözüm:

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

Şerefe


Bu yanıtın bazı ilginç özellikleri vardır ve potansiyel olarak daha sağlamdır, ancak üzerinde çalışılması gerekir ve çeşitli süper sınıflara izin vereceğinden setMeasuredDimension () yerine super.onMeasure () 'a iletilen ölçü özelliklerini oluşturmalıdır.
Adam

1

Benim önerim, FrameLayout'tan miras alan özel bir düzen sınıfı oluşturmaktır. OnMeasure () yöntemini geçersiz kılın ve kare olmasını istediğiniz denetimi bu SquareFrameLayout içine koyun.

Xamarin.Android'de bu şekilde yapılır:

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

Bir Görünümün (veya başka bir kontrolün) kare (ve ortalanmış) olmasını istiyorsanız, bunu mizanpajınıza aşağıdaki şekilde eklemeniz yeterlidir:

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

0

Farklı Mizanpajlar için bir sarmalayıcı sınıfı sağlayan ve herhangi bir temel işlevi kaybetmeden onları Kare boyutlandırılmış hale getiren bir Android Kitaplığı olan SquareLayout'a göz atın .

Boyutlar Düzeni render hemen önce hesaplanır , dolayısıyla Görünüm elde edildiğinde ayarlamak için gibi hiçbir yeniden oluşturma ya da bir şey yoktur.

Kitaplığı kullanmak için bunu build.gradle dosyanıza ekleyin:

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

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

İhtiyacınız olan , SquareLinearLayout'tur .


Gerçek cihazda benim için çalışmıyor :( Android Studio önizlemesinde iyi
işleniyor

0

Kotlin ile çözüm isteyen herkes için, işte yaptığım şey 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

Bu kodu deneyin:

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);
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.