2013-03-13 21 views
10

Bu boyut sınıfından birini, genişlik veya yükseklik, diğer boyutun bir oranını yapmak için kullanılan kapsayıcı sınıfım var. Örneğin, genişliğin "match_parent" olduğu bir 16: 9 düzen konteynırı istiyorum. Ancak, yüksekliği "match_parent" olarak kullanırken, Android kendisini doğru şekilde göstermiyor gibi görünüyor. Yüksekliği, genişliğin bir oranı olarak ayarladığımda her şey yolunda! Tam tersi ve işe yaramıyor. Neler oluyor?RelativeLayout Özel görünümün genişliğini doğru şekilde güncelleştirmiyor

public class RatioLayout extends ViewGroup { 

private float ratio = 1.6222f; // 4 x 3 default. 1.78 is 16 x 9 
public float getRatio() { return ratio; } 
public void setRatio(float ratio) { 
    this.ratio = ratio; 
    requestLayout(); 
} 

public RatioLayout(Context context) { 
    this(context, null); 
} 

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

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

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    Log.i("Ratio", "/onMeasure"); 
    int width = MeasureSpec.getSize(widthMeasureSpec); 
    int height = MeasureSpec.getSize(heightMeasureSpec); 
    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 

//  int exactly = MeasureSpec.EXACTLY; // 1073741824  
//  int atMost = MeasureSpec.AT_MOST; // -2147483648 
//  int unspec = MeasureSpec.UNSPECIFIED; // 0 

    width = Math.round(height * ratio); 
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 

    setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); 

    int mw = getMeasuredWidth(); 
    int mh = getMeasuredHeight(); 
    Log.i("Ratio", "mw: " + mw + ", mh: " + mh); 
} 


@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b) { 
    Log.i("Ratio", "/onLayout"); 
    Log.i("Ratio", "l: " + l + ", t: " + t + ", r:" + r + ", b:" + b); 
    Log.i("Ratio", "mw: " + getMeasuredWidth() + ", mh:" + getMeasuredHeight()); 
    Log.i("Ratio", "w: " + getWidth() + ", mw:" + getHeight()); 
} 

} 

böyle bir düzen kullanmak, eylem olarak görmek için:

<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=".MainActivity" > 

<com.example.RatioLayout 
    android:id="@+id/image" 
    android:layout_width="0dp" 
    android:layout_height="match_parent" 
    android:layout_marginBottom="100dp" 
    android:layout_marginTop="100dp" 
    android:background="#FFFF00FF" /> 

</RelativeLayout> 

Ve bu benim Etkinlik olacaktır:

Günlüğü çıkışı:

I/Ratio (9445): /onMeasure 
I/Ratio (9445): mw: 1087, mh: 670 
I/Ratio (9445): /onMeasure 
I/Ratio (9445): mw: 655, mh: 404 
I/Ratio (9445): /onLayout 
I/Ratio (9445): l: 0, t: 133, r:1087, b:537 
I/Ratio (9445): mw: 655, mh:404 
I/Ratio (9445): w: 1087, mw:404 <--- NO reason this should not be 655 

Neden Android ediyorum En son ölçülen sürümü onurlandırmıyor, ancak eski bir sürümü kullanıyor ancak en son yüksekliği mi kullanıyorsunuz?

DÜZENLEME: Güncellendi. OranLayout NOT a RelativeLayout öğesinin üstünü yapın, ancak bir LinearLayout veya FrameLayout bana doğru davranışı verir. Bazı nedenlerden dolayı RelativeLayout, ölçülenWidth'ı "en önbelleğe alma" ve en fazla akımın kullanılmamasıdır.

DÜZENLEME 2: RelativeLayout.onLayout bu açıklama benim bir gýcýk

@Override 
protected void onLayout(boolean changed, int l, int t, int r, int b) { 
    // The layout has actually already been performed and the positions 
    // cached. Apply the cached values to the children. 
    int count = getChildCount(); 

// TODO: we need to find another way to implement RelativeLayout 
// This implementation cannot handle every case 
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 

SON DÜZENLEME Tamam olduğuna inanıyoruz "o önbelleğe alma var" doğrular gibiydi. Pes ediyorum. Bu RelativeLayout'ta yasal bir hatadır. Bu kod, bu sorunu giderir, ancak toRightOf özellikleriyle ilgili sorunlar oluşturur. Bulduğum iş, bu OranLayout'u LinerLayout gibi başka bir ViewGroup'a yerleştirmekti. Bu meraklı

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    Log.i("Ratio", "/onMeasure"); 
    int width = MeasureSpec.getSize(widthMeasureSpec); 
    int height = MeasureSpec.getSize(heightMeasureSpec); 
    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 

//  int exactly = MeasureSpec.EXACTLY; // 1073741824  
//  int atMost = MeasureSpec.AT_MOST; // -2147483648 
//  int unspec = MeasureSpec.UNSPECIFIED; // 0 

    width = Math.round(height * ratio); 
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 

    setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); 

    if (getParent() instanceof RelativeLayout) { 
     RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)getLayoutParams(); 
     Class<?> clazz = RelativeLayout.LayoutParams.class; 
     try { 
      Field left = clazz.getDeclaredField("mLeft"); 
      Field right = clazz.getDeclaredField("mRight"); 
      left.setAccessible(true); 
      right.setAccessible(true); 
      int l = left.getInt(params); 
      if (l == -1) l = params.leftMargin; // if the value is uninitialized, set it to 0; 
      if (l == -1) l = 0; // if the value is uninitialized, set it to 0; 
      // setting this seems to break the layout_marginLeft properties. 
      right.setInt(params, l + getMeasuredWidth()); 
     } catch (NoSuchFieldException e) { 
      Log.e("Ration", "error", e); 
     } catch (IllegalArgumentException e) { 
      Log.e("Ration", "error", e); 
     } catch (IllegalAccessException e) { 
      Log.e("Ration", "error", e); 
     } 
    } 

    int mw = getMeasuredWidth(); 
    int mh = getMeasuredHeight(); 
    lastWidth = mw; 
    Log.i("Ratio", "mw: " + mw + ", mh: " + mh); 
} 

cevap

1

için kod Aslında o, yani kare veya 4 Bazı boy oranını (koruyarak bir View mümkün olduğunca büyük olması yapmak olduğunu, daha önce yapmaya çalıştığını tam olarak ne yapmaya çalıştık: 3 ya da bunun gibi bir şey).

Sorunum, View'ım ScrollView boyutunda olduğunda boyutun yanlış hesaplanmasıydı. Bunu çözemedim, ancak yardımcı olması durumunda kodumu aşağıya yazacağım. Kodunuza gerçekten benzer, ancak FrameLayout'dan miras alıyorum ve ben de super.onMeasure() numaralı telefonu arıyorum.

Kullanmakta olduğum projeyi iki kez kontrol ettim ve aslında bunu RelativeLayout numaralı doğrudan çocuk olarak görüyorum.

Java:

/** 
* A FrameLayout which tries to be as big as possible while maintaining a given ratio between its width and height. 
* Formula: Height = Width/ratio; 
* Usage: 
* 1) Set layout_width and layout_height to "match_parent" 
* 2) For 4:3 for example, set ratio to 4/3 = 1.333333 etc. Or don't specify, and it will be square by default. 
*/ 
public class RatioFrameLayout extends FrameLayout 
{ 
    private final static float DEFAULT_RATIO = 1f; 

    private float ratio; 
    private boolean measured = false; 

    public RatioFrameLayout(Context context) 
    { 
     super(context); 
    } 

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

    public RatioFrameLayout(Context context, AttributeSet attrs, int defStyle) 
    { 
     super(context, attrs, defStyle); 
     readAttributes(context, attrs); 
    } 

    private void readAttributes(Context context, AttributeSet attrs) 
    { 
     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RatioFrameLayout); 

     String value = a.getString(R.styleable.RatioFrameLayout_ratio); 
     try 
     { 
      ratio = Float.parseFloat(value); 
     } 
     catch (Exception e) 
     { 
      ratio = DEFAULT_RATIO; 
     } 

     a.recycle(); 
    } 

    public float getRatio() 
    { 
     return ratio; 
    } 

    public void setRatio(float ratio) 
    { 
     this.ratio = ratio; 
    } 

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

     int targetWidth = getMeasuredWidth(); 
     int targetHeight = Math.round((float)getMeasuredWidth()/ratio); 

     if (targetHeight > getMeasuredHeight() && getMeasuredHeight() != 0) 
     { 
      targetWidth = Math.round(getMeasuredHeight() * ratio); 
      targetHeight = getMeasuredHeight(); 
     } 

     super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(targetHeight, MeasureSpec.EXACTLY)); 
    } 

    private void printMeasureSpec(String description, int value) 
    { 
     int mode = MeasureSpec.getMode(value); 
     String modeName = mode == MeasureSpec.AT_MOST ? "AT_MOST" 
       : mode == MeasureSpec.EXACTLY ? "EXACTLY" : "UNSPECIFIED"; 
     DLog.d(String.format("Measure spec for %s, mode = %s, size = %d", description, modeName, MeasureSpec.getSize(value))); 
    } 
} 

attrs.xml:

<resources> 
    <declare-styleable name="RatioFrameLayout"> 
     <attr name="ratio" format="string|reference" /> 
    </declare-styleable> 
</resources> 
İlgili konular