2013-03-13 14 views
10

このコンテナクラスは、幅または高さのいずれかの次元を他の次元の比率にするために使用します。たとえば、幅が「match_parent」の16:9のレイアウトコンテナが必要です。しかし、高さを「match_parent」として使用すると、Android自体が正しく再レイアウトされていないように見えます。高さを幅の比率にするとすべてが上手です!逆もありますが、動作しません。どうしたの?RelativeLayoutがカスタムビューの幅を正しく更新しない

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

} 

、アクションでそれを参照してください。このようなレイアウトを使用するには:

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

そして、これが私の活動のようになります。出力をログ

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 

なぜAndroidのでしょう最新のmeasuredWidthを尊重するのではなく、古いバージョンで最新の高さを使用しますか?

EDIT:更新されました。 RatioLayoutの親をにしないでください。をRelativeLayoutにしますが、LinearLayoutまたはFrameLayoutは正しい動作をします。なんらかの理由で、RelativeLayoutはmeasuredWidthを「キャッシング」し、最新のものを使用しません。

EDIT 2: RelativeLayout.onLayoutでこのコメントは私の私がFINAL EDIT [OK]を

@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) { 

バグであると考えている "それは、キャッシングだ" 確認するようです。あきらめる。これはRelativeLayoutの正当なバグです。このコードの種類は修正されていますが、toRightOfプロパティで問題が発生します。私が見つけた問題は、このRatioLayoutをLinerLayoutのような別のViewGroupにネストすることでした。 :コードは、これらの好奇心

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

答えて

1

のために、私は実際にあなたが前にやろうとしている正確に何をしようとしたいくつかのアスペクト比(すなわち、正方形または4を保持したまま、つまり、Viewはできるだけ大きいことにします3かそのようなもの)。

私の問題は、私のビューがScrollViewにあるとき、サイズが正しく計算されなかったということでした。私はこれを理解することができませんでしたが、助けになる場合に備えて私のコードを掲載します。これはあなたのコードと実際に似ていますが、私はFrameLayoutから継承しており、私はsuper.onMeasure()と呼んでいます。

私はこれを使っていたプロジェクトをダブルチェックしましたが、実際にはRelativeLayoutという直接の子として持っています。

の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> 
関連する問題