Intereting Posts
Как создать задержку в 1 секунду перед установкой альфы Просмотр? Android TextView имеет высоту и ширину 0 Удаление неиспользуемых ресурсов из сторонней библиотеки Модернизация 2 с данными только формы SQLite и сохранение изображений Как уничтожить Фрагмент? @Named провайдеры с одинаковыми типами возврата в конечном итоге дают java.lang.IllegalArgumentException: дубликат Изменить цвет текста строки состояния, когда primaryDark белый Как отображать экран входа только один раз? Какой java ORM подходит для ОС Android и поддерживает ленивый список? Ошибка при построении yasm / source / patched-yasm / util.h: 78: 23: error: libintl.h: Нет такого файла или каталога Как вы получаете данные с устройства Bluetooth LE Как программно изменить ширину и высоту Google Maps v2 (поддержка фрагмента)? 75 маркеров на карте -> утечки памяти -> OutOfMemoryException Попытка воспроизведения видео из необработанной папки (VideoView)

RelativeLayout не правильно обновляет ширину пользовательского вида

У меня есть этот класс контейнера, который используется для создания одного из размеров, будь то ширина или высота, отношение другого измерения. Например, мне нужен контейнер макета 16: 9, где ширина «match_parent». Однако при использовании высоты как «match_parent», Android, похоже, не правильно ретранслирует себя. Когда я устанавливаю высоту как отношение ширины, все в порядке! И наоборот, это не работает. Что происходит?

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 не оценил мою последнюю измеренную ширину, но использовал более старую версию, но самую последнюю высоту?

EDIT: Обновлено. Сделать родительский элемент RatioLayout NOT RelativeLayout, но LinearLayout или FrameLayout дает мне правильное поведение. По какой-то причине RelativeLayout «кэширует» измеренную ширину и не использует самую последнюю.

EDIT 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. Работа, которую я нашел, заключалась в том, чтобы вложить этот RatioLayout в другую ViewGroup, такую ​​как LinerLayout. Код для любопытных

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

Solutions Collecting From Web of "RelativeLayout не правильно обновляет ширину пользовательского вида"

Я на самом деле пытался сделать именно то, что вы пытаетесь сделать раньше, т. Е. Сделать представление максимально возможным, сохраняя при этом некоторое соотношение сторон (например, квадрат или 4: 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>