2013-03-13 8 views
10

मेरे पास यह कंटेनर क्लास है जिसका उपयोग आयामों में से एक बनाने के लिए किया जाता है, या तो चौड़ाई या ऊंचाई, अन्य आयाम का अनुपात। उदाहरण के लिए, मुझे 16: 9 लेआउट कंटेनर चाहिए जहां चौड़ाई "match_parent" है। हालांकि, ऊंचाई को "match_parent" के रूप में उपयोग करते समय, एंड्रॉइड ठीक से खुद को रिलाउट नहीं लग रहा है। जब मैं ऊंचाई को चौड़ाई का अनुपात मानता हूं तो सब कुछ ठीक है! इसके विपरीत और यह काम नहीं करता है। क्या चल रहा है?सापेक्ष लयआउट कस्टम दृश्य की चौड़ाई को सही ढंग से अपडेट नहीं कर रहा है

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 होगा मेरे नवीनतम मापा Width का सम्मान नहीं करते हैं, लेकिन पुराने संस्करण का उपयोग करते हैं लेकिन नवीनतम ऊंचाई?

संपादित करें: अपडेट किया गया। अनुपात Layout के माता-पिता को रिलेटिवलायआउट बनाएं लेकिन लीनियरलाउट या फ़्रेमलाउट मुझे सही व्यवहार देता है। कुछ कारणों से सापेक्ष लयआउट माइकलविड्थ को "कैशिंग" कर रहा है और सबसे वर्तमान का उपयोग नहीं कर रहा है।

संपादित करें 2: RelativeLayout.onLayout में यह टिप्पणी मेरी जो मेरा मानना ​​है कि एक बग

@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 गुणों के साथ मुद्दों पैदा करता है। मैंने जो काम पाया वह इस अनुपात को घूमने के लिए लाइनरलाउट जैसे किसी अन्य व्यू ग्रुप में घूमना था। उन उत्सुक

@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 का एक सीधा बच्चे के रूप में वास्तव में क्या है।

जावा:

/** 
* 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> 
संबंधित मुद्दे