Bir Android görünümü üst öğesinin boyutlarına göre nasıl boyutlandırılır?


104

Bir görünümü, ana düzeninin boyutuna göre nasıl boyutlandırabilirim? Örneğin RelativeLayout, tam ekranı dolduran bir cihazım var ve bir çocuk görünümünün, diyelim ki ImageViewtüm yüksekliği ve 1/2 genişliğini kaplamasını istiyorum.

Ben tüm geçersiz kılma denedim onMeasure, onLayout, onSizeChanged.... vs ve ben işe alamadım


Bunu ConstraintLayout kullanarak yapmak mümkündür, şunu kontrol edin: stackoverflow.com/questions/37318228/…
Ali Nem

Yanıtlar:


124

Hala bu konuyu okuyan olup olmadığını bilmiyorum, ancak Jeff'in çözümü sizi sadece oraya götürür (kelimenin tam anlamıyla). OnMeasure'ın yapacağı şey, görüntünün yarısını ebeveynin yarısında göstermektir. Sorun şu ki, super.onMeasure öğesinden önce çağrılması setMeasuredDimension, görünümdeki tüm çocukları orijinal boyuta göre ölçecek, ardından setMeasuredDimensionyeniden boyutlandırdığında görünümü yarıya indirecek .

Bunun yerine, aramanız setMeasuredDimension(bir onMeasure geçersiz kılma için gerektiği gibi) veLayoutParams görünümünüz için yeni bir görünüm sağlamanız ve ardından aramanız gerekir super.onMeasure. LayoutParamsSizin görünümünüzün türünden değil, görünümünüzün ana türünden türetildiğinizi unutmayın .

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
   int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   this.setMeasuredDimension(parentWidth/2, parentHeight);
   this.setLayoutParams(new *ParentLayoutType*.LayoutParams(parentWidth/2,parentHeight));
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Ebeveynle sorun yaşayacağın tek zaman, ebeveynin bir çocuk LayoutParamsolması olduğuna inanıyorum AbsoluteLayout(bu, kullanımdan kaldırıldı, ancak yine de bazen yararlıdır).


12
Tecrübelerime göre, bu nadiren işe yarıyor, özellikle. fill / match_parent veya ağırlıklar çağrıldığında. SetMeasuredDimension'dan sonra measureChildren'ı daha iyi çağırın (örneğin, MeasureSpec.makeMeasureSpec (newWidth, MeasureSpec.AT_MOST) ile).
M.Schenk

1
Does MeasureSpec.getSize(widthMeasureSpec)gerçekten doğru parent's genişliği vermek? Benim için sadece gerçek genişliğe oldukça yakın olan ancak kesin olmayan rastgele genişlikler döndürüyor.
Konsumierer

1
@ M.Schenk var. Ağırlıklı bir düzen içinde bir VideoView var. Genişliğin ekran boyutunun yüzdesi olması gerekiyor ve en boy oranını korumam gerekiyor. Bu, videoyu uzatır, bu yüzden wrap_contentuzatmaz. Vic'in cevabı bu senaryo için işe yaramıyor, ancak @ M.Schenk'in yorumu işe yarıyor. Ayrıca ekstra bir düzen geçişi kaydeder. Görünürlük için bir cevap olarak da göndermelisiniz.
dokkaebi

7
Çağrı için olmaz super.onMeasure()basitçe geçersiz kılmak ve daha erken çağrı etkilerini sıfırlamak this.setMeasuredDimension()?
Spinner

1
@Spinner'ın da bahsettiği gibi, çağrının neden super.onMeasure()önceki hesaplamaları geçersiz kılmadığını da merak ediyorum .
jwir3

39

Bunu, özel bir View oluşturarak ve onMeasure () yöntemini geçersiz kılarak çözebilirsiniz. Xml'nizde layout_width için her zaman "fill_parent" kullanırsanız, onMeasusre () yöntemine iletilen widthMeasureSpec parametresi üst öğenin genişliğini içermelidir.

public class MyCustomView extends TextView {

    public MyCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @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 / 2, parentHeight);
    }
}   

XML'niz şuna benzer:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <view
        class="com.company.MyCustomView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

2
@jeff => neden parentWidth / 2 ??
Saeed-rz

6
@Leon_SFS: Soru şunu soruyor: "tam yükseklik ve 1/2 genişlik"
EdgarT

28

Ölçülen boyutları kendiniz ayarlamakla uğraşmamanın en iyisi olduğunu buldum. Aslında ebeveyn ve çocuk görüşleri arasında devam eden biraz müzakere var ve tüm bu kodu yeniden yazmak istemiyorsunuz .

Yine de yapabileceğiniz şey, measureSpecs'i değiştirip onlarla süper çağırmaktır. Görüşünüz, onun ebeveyninden değiştirilmiş bir mesaj aldığını asla bilemez ve sizin için her şeyi halleder:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    int myWidth = (int) (parentHeight * 0.5);
    super.onMeasure(MeasureSpec.makeMeasureSpec(myWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
}

16

XML'de bir mizanpaj ve görünüm tanımladığınızda, bir görünümün mizanpaj genişliğini ve yüksekliğini sarma içeriği veya dolgu üst öğesi olarak belirtebilirsiniz. Alanın yarısını kaplamak biraz daha zordur, ancak diğer yarısında istediğiniz bir şey varsa aşağıdakine benzer bir şey yapabilirsiniz.

   <LinearLayout android:layout_width="fill_parent"
                android:layout_height="fill_parent">
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
        <ImageView android:layout_height="fill_parent"
                 android:layout_width="0dp"
                 android:layout_weight="1"/>
   </LinearLayout>

İki şeye aynı ağırlığı vermek, ekranın aynı oranını kaplayacak şekilde esneyecekleri anlamına gelir. Düzenler hakkında daha fazla bilgi için geliştirici belgelerine bakın.


Bunu önermiyorum, ama ... bir hack olarak, yukarıdaki yaklaşımı kullanırsanız, bu görünümlerden birini "kukla" yapabilir ve alanın yarısını doldurması için android: visibility = "görünmez"
ayarını yapabilirsiniz

Alanın sadece yarısını kullanmanın amacı nedir? Diğer yarısında herhangi bir şey sergilemeyi planlıyor musunuz? Kalan alanla yaptığınız şey en iyi yaklaşımı gösterecektir
RickNotFred

bu kesinlikle 1 numara olmalıdır - eğer xml'nizde yapabiliyorsanız, neden java'nızda yapasınız?
2014

9

Mayras XML yaklaşımının düzgün olabileceğine inanıyorum . Bununla birlikte, yalnızca ağırlık toplamını ayarlayarak tek bir görünümle daha doğru hale getirmek mümkündür. Bunu artık bir hack olarak adlandırmayacağım ama bence en basit yaklaşım:

<LinearLayout android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:weightSum="1">
    <ImageView android:layout_height="fill_parent"
               android:layout_width="0dp"
               android:layout_weight="0.5"/>
</LinearLayout>

Bunun gibi herhangi bir ağırlığı kullanabilirsiniz, örneğin 0,6 (ve merkezleme) düğmeler için kullanmayı sevdiğim ağırlıktır.


3

Bunu dene

int parentWidth = ((parentViewType)childView.getParent()).getWidth();
int parentHeight = ((parentViewType)childView.getParent()).getHeight();

daha sonra, chileView parametrelerini ayarlamak için LinearLayout.LayoutParams kullanabilirsiniz.

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(childWidth,childLength);
childView.setLayoutParams(params);

2

Artık PercentRelativeLayout'u kullanabilirsiniz. Boom! Sorun çözüldü.


2
PercentRelativeLayout, API seviyesi 26.0.0-beta1'de kullanımdan kaldırıldı.
dzikovskyy

"API seviyesinde" ne demek istiyorsun? Kütüphane sürüm numarasını mı kastediyorsunuz? PRL, destek kitaplığındadır, API seviyesi yoktur.
androidguy

API seviyesi ile Android versiyonunu kastediyorum. Buradaki bilgiler developer.android.com/reference/android/support/percent/… . API seviyesi hakkında daha fazla bilgiyi burada geliştirici.android.com
guide/topics/manifest/…

Herhangi bir nedenle 26.0.0 veya daha yüksek bir sürüme ihtiyacınız olmadığı sürece bu sınıfı kullanmaya devam edebilirsiniz.
androidguy

1

Roman, mizanpajınızı Java kodunda (ViewGroup soyundan) yapmak istiyorsanız, bu mümkündür. İşin püf noktası, hem onMeasure hem de onLayout yöntemlerini uygulamanız gerektiğidir. Önce onMeasure çağrılır ve orada alt görünümü "ölçmeniz" gerekir (istenen değere etkili bir şekilde boyutlandırmanız). OnLayout çağrısında yeniden boyutlandırmanız gerekir. Bu diziyi yapamazsanız veya onMeasure kodunuzun sonunda setMeasuredDimension () öğesini çağırmazsanız, sonuç alamazsınız. Bu neden bu kadar karmaşık ve kırılgan bir şekilde tasarlandığımı aşıyor.


1

Diğer taraf boşsa. Sanırım en basit yol, boş bir görünüm eklemek (örneğin, doğrusal bir düzen) ve ardından her iki görünümün genişliğini fill_parent ve her ikisinin de ağırlıklarını 1 olarak ayarlamak olacaktır.

Bu bir LinearLayout'ta çalışır ...


0

Bunun gibi bir şey:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.company.myapp.ActivityOrFragment"
    android:id="@+id/activity_or_fragment">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/linearLayoutFirst"
    android:weightSum="100">   <!-- Overall weights sum of children elements -->

    <Spinner
        android:layout_width="0dp"   <!-- It's 0dp because is determined by android:layout_weight -->
        android:layout_weight="50"
        android:layout_height="wrap_content"
        android:id="@+id/spinner" />

</LinearLayout>


<LinearLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:layout_below="@+id/linearLayoutFirst"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:id="@+id/linearLayoutSecond">

    <EditText
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="75"
        android:inputType="numberDecimal"
        android:id="@+id/input" />

    <TextView
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:layout_weight="25"
        android:id="@+id/result"/>

</LinearLayout>
</RelativeLayout>

-1

Uzun zamandır bu soruyla uğraşıyorum, DrawerLayout'un ebeveyninin ikinci çocuğu olan çekmece genişliğini değiştirmek istiyorum. Son zamanlarda anladım, ancak daha iyi bir fikir olup olmadığını bilmiyorum.

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    super.onLayout(changed, l, t, r, b)
    (getChildAt(1).layoutParams as LayoutParams).width = measuredWidth/2
}
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.