Авторский способ переопределить onMeasure ()? - PullRequest
87 голосов
/ 15 сентября 2011

Как правильно переопределить 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 (), макет, размеры дочерних представлений и т. Д.?

Ответы [ 8 ]

67 голосов
/ 26 апреля 2012

Другие решения не являются исчерпывающими.В некоторых случаях они могут работать, и с них можно начинать, но они не гарантированно работают.

Когда вызывается onMeasure, вы можете иметь или не иметь права изменять размер.Значения, которые передаются вашему onMeasure (widthMeasureSpec, heightMeasureSpec), содержат информацию о том, что разрешено делать вашему дочернему представлению.В настоящее время есть три значения:

  1. MeasureSpec.UNSPECIFIED - Вы можете быть такими большими, как вам хотелось бы
  2. MeasureSpec.AT_MOST - такими большими, как вы хотите (до размера спецификации), Это parentWidth в вашем примере.
  3. 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 resolSizeAndState (int size, int measureSpec, int childMeasuredState)

int resolSize (int size, int measureSpec)

Хотя первое доступно только в Honeycomb, второе доступно во всех версиях.

Примечание. Вы можете обнаружить, что resizeWidth или resizeHeight всегда ложны.Я обнаружил, что это тот случай, когда я запрашивал MATCH_PARENT.Я смог исправить это, запросив WRAP_CONTENT на моем родительском макете, а затем на этапе НЕУТОЧЕННЫЙ запросить размер Integer.MAX_VALUE.Это дает вам максимальный размер, который ваш родитель разрешает при следующем прохождении через onMeasure.

39 голосов
/ 20 октября 2011

Документация является авторитетной по этому вопросу: 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.Макет происходит во второй проход после измерения.

2 голосов
/ 13 марта 2016

Я думаю, что от родительского элемента зависит то, что вы переопределяете.

Например, если вы расширяете ViewGroup (например, FrameLayout), когда вы измерили размер, вы должны вызвать, как показано ниже

super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

потому что вы, возможно, захотите, чтобы ViewGroup выполняла работу по отдыху (делайте некоторые вещи в дочернем представлении)

Если вы расширяете представление (например, ImageView), вы можете просто вызвать this.setMeasuredDimension(width, height);, потому чтородительский класс будет просто делать то, что вы обычно делаете.

Одним словом, если вы хотите, чтобы некоторые функции, которые родительский класс предлагал бесплатно, вы должны вызвать super.onMeasure() (обычно передайте SpecE MeasureSpec.EXACTLY mode measure spec),в противном случае достаточно набрать this.setMeasuredDimension(width, height);.

0 голосов
/ 20 августа 2014

вот как я решил проблему:

@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

0 голосов
/ 19 мая 2013

Зависит от используемого вами элемента управления. Инструкции в документации работают для некоторых элементов управления (TextView, Button, ...), но не для других (LinearLayout, ...). Способ, который работал очень хорошо для меня, состоял в том, чтобы позвонить в супер, как только я закончу. На основании статьи в ссылке ниже.

http://humptydevelopers.blogspot.in/2013/05/android-view-overriding-onmeasure.html

0 голосов
/ 28 января 2013

При изменении размера представлений внутри onMeasure все, что вам нужно, это вызов setMeasuredDimension.Если вы изменяете размер за пределами onMeasure, вам нужно позвонить setLayoutParams.Например, изменение размера текстового представления при изменении текста.

0 голосов
/ 24 июля 2012

вы можете взять этот фрагмент кода в качестве примера 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);
    }
}
0 голосов
/ 21 декабря 2011

Полагаю, setLayoutParams и пересчет измерений - это обходной путь для правильного изменения размера дочерних представлений, как это обычно делается в onMeasure производного класса.

Однако это редко работает правильно (по любой причине ...), лучше вызывать measureChildren (при создании ViewGroup) или при необходимости попробуйте нечто подобное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...