Какой правильный способ переопределить onMeasure ()? Я видел разные подходы. Например, профессиональная разработка для Android использует MeasureSpec для вычисления размеров, а затем завершается вызовом setMeasuredDimension (). Например:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
}
С другой стороны, согласно этому сообщению , «правильный» способ - использовать MeasureSpec, вызвать setMeasuredDimensions (), затем вызвать setLayoutParams () и закончить вызовом super.onMeasure (). Например:
@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);
}
Так какой же путь правильный? Ни один из подходов не сработал для меня на 100%.
Я думаю, на самом деле я спрашиваю, знает ли кто-нибудь об учебнике, в котором объясняются onMeasure (), макет, размеры дочерних представлений и т. Д.?
android
android-layout
У Авалос
источник
источник
Ответы:
Другие решения не являются исчерпывающими. В некоторых случаях они могут работать, и это хорошее место для начала, но их работа может не гарантироваться.
Когда вызывается onMeasure, вы можете иметь или не иметь права изменять размер. Значения, которые передаются вашему onMeasure (
widthMeasureSpec
,heightMeasureSpec
), содержат информацию о том, что разрешено делать вашему дочернему представлению. В настоящее время существует три значения:MeasureSpec.UNSPECIFIED
- Ты можешь быть таким большим, как хочешьMeasureSpec.AT_MOST
- Насколько вы хотите (до размера спецификации), этоparentWidth
в вашем примере.MeasureSpec.EXACTLY
- Нет выбора. Родитель выбрал.Это сделано для того, чтобы Android мог выполнить несколько проходов, чтобы найти нужный размер для каждого элемента, подробнее см. Здесь .
Если вы не будете следовать этим правилам, ваш подход не гарантирован.
Например, если вы хотите проверить, разрешено ли вам вообще изменять размер, вы можете сделать следующее:
final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
Используя эту информацию, вы узнаете, можете ли вы изменить значения, как в вашем коде. Или если вам нужно сделать что-то другое. Быстрый и простой способ определить желаемый размер - использовать один из следующих методов:
int resolveSizeAndState (размер int, int measureSpec, int childMeasuredState)
int resolveSize (int размер, int measureSpec)
Первый доступен только для Honeycomb, а второй доступен для всех версий.
Примечание: вы можете обнаружить это
resizeWidth
илиresizeHeight
всегда ошибаться. Я обнаружил, что это так, если я просилMATCH_PARENT
. Я смог исправить это, запросивWRAP_CONTENT
в моем родительском макете, а затем на этапе НЕПРЕДВИДЕННО, запросив размерInteger.MAX_VALUE
. Таким образом вы получите максимальный размер, разрешенный вашим родителем при следующем прохождении onMeasure.источник
Документация является авторитетом по этому вопросу: http://developer.android.com/guide/topics/ui/how-android-draws.html и http://developer.android.com/guide/topics/ui/custom. -components.html
Подводя итог: в конце вашего переопределенного
onMeasure
метода вы должны вызватьsetMeasuredDimension
.Вы не должны звонить
super.onMeasure
после звонкаsetMeasuredDimension
, это просто сотрет все, что вы установили. В некоторых ситуациях может потребоваться вызватьsuper.onMeasure
первый, а затем изменить результаты, вызвавsetMeasuredDimension
.Не вызывайте
setLayoutParams
вonMeasure
. Разметка происходит за второй проход после измерения.источник
Я думаю, это зависит от родителя, которого вы отменяете.
Например, если вы расширяете ViewGroup (например, FrameLayout), когда вы измерили размер, вы должны позвонить, как показано ниже
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
потому что вы можете захотеть, чтобы ViewGroup выполняла отдых (делайте некоторые вещи в дочернем представлении)
Если вы расширяете View (например, ImageView), вы можете просто вызвать
this.setMeasuredDimension(width, height);
, потому что родительский класс будет делать то же самое, что и вы обычно.Одним словом, если вы хотите, чтобы некоторые функции, предлагаемые вашим родительским классом, были бесплатными, вы должны позвонить
super.onMeasure()
(обычно передайте параметр измерения режима MeasureSpec.EXACTLY), в противном случаеthis.setMeasuredDimension(width, height);
достаточно вызова .источник
вот как я решил проблему:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { .... setMeasuredDimension( measuredWidth, measuredHeight ); widthMeasureSpec = MeasureSpec.makeMeasureSpec( measuredWidth, MeasureSpec.EXACTLY ); heightMeasureSpec = MeasureSpec.makeMeasureSpec( measuredHeight, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Более того, это было необходимо для компонента ViewPager
источник
super.onMeasure
очистит размеры, установленные с помощьюsetMeasuredDimension
.Если изменить размер представления внутри
onMeasure
все, что вам нужно, этоsetMeasuredDimension
вызов. Если вы меняете размер снаружи,onMeasure
вам необходимо позвонитьsetLayoutParams
. Например, изменение размера текстового представления при изменении текста.источник
Зависит от используемого вами элемента управления. Инструкции в документации работают для некоторых элементов управления (TextView, Button, ...), но не для других (LinearLayout, ...). Для меня очень хорошо сработал способ позвонить суперу, когда я закончу. На основании статьи в приведенной ниже ссылке.
http://humptydevelopers.blogspot.in/2013/05/android-view-overriding-onmeasure.html
источник
Я предполагаю, что setLayoutParams и пересчет измерений - это обходной путь для правильного изменения размера дочерних представлений, поскольку это обычно делается в производном классе onMeasure.
Однако это редко работает правильно (по какой-то причине ...), лучше вызвать measureChildren (при создании ViewGroup) или при необходимости попробовать что-то подобное.
источник
вы можете взять этот фрагмент кода в качестве примера onMeasure () ::
public class MyLayerLayout extends RelativeLayout { public MyLayerLayout(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int parentWidth = MeasureSpec.getSize(widthMeasureSpec); int parentHeight = MeasureSpec.getSize(heightMeasureSpec); int currentChildCount = getChildCount(); for (int i = 0; i < currentChildCount; i++) { View currentChild = getChildAt(i); //code to find information int widthPercent = currentChildInfo.getWidth(); int heightPercent = currentChildInfo.getHeight(); //considering we will pass height & width as percentage int myWidth = (int) Math.round(parentWidth * (widthPercent / 100.0)); int myHeight = (int) Math.round(parentHeight * (heightPercent / 100.0)); //Considering we need to set horizontal & vertical position of the view in parent AlignmentTraitValue vAlign = currentChildInfo.getVerticalLocation() != null ? currentChildlayerInfo.getVerticalLocation() : currentChildAlignmentTraitValue.TOP; AlignmentTraitValue hAlign = currentChildInfo.getHorizontalLocation() != null ? currentChildlayerInfo.getHorizontalLocation() : currentChildAlignmentTraitValue.LEFT; int topPadding = 0; int leftPadding = 0; if (vAlign.equals(currentChildAlignmentTraitValue.CENTER)) { topPadding = (parentHeight - myHeight) / 2; } else if (vAlign.equals(currentChildAlignmentTraitValue.BOTTOM)) { topPadding = parentHeight - myHeight; } if (hAlign.equals(currentChildAlignmentTraitValue.CENTER)) { leftPadding = (parentWidth - myWidth) / 2; } else if (hAlign.equals(currentChildAlignmentTraitValue.RIGHT)) { leftPadding = parentWidth - myWidth; } LayoutParams myLayoutParams = new LayoutParams(myWidth, myHeight); currentChildLayoutParams.setMargins(leftPadding, topPadding, 0, 0); currentChild.setLayoutParams(myLayoutParams); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
источник