объяснение пользовательского представления onMeasure


Я пытался сделать пользовательский компонент. Я продлил View класс и сделать некоторые чертежи в onDraw способ перекрытая. Почему мне нужно переопределить onMeasure? Если бы я этого не сделал, все было бы правильно. Может кто-нибудь объяснить это? Как я должен написать мой onMeasure способ? Я видел пару учебников, но каждый из них немного отличается от другого. Иногда они называют super.onMeasure в конце концов, иногда они используют setMeasuredDimension и не назвал его. В чем же разница?

ведь я хочу использовать несколько точно таких же компонентов. Я добавил Эти компоненты в мой XML файл, но я не знаю, насколько они должны быть большими. Я хочу установить его положение и размер позже (почему мне нужно установить размер в onMeasure Если в onDraw когда я его рисую, тоже работает) в классе пользовательских компонентов. Когда именно мне нужно это сделать?

2 282

2 ответа:

onMeasure() это ваша возможность сказать Android, насколько большой вы хотите, чтобы ваш пользовательский вид зависел от ограничений макета, предоставленных родителем; это также возможность вашего пользовательского представления узнать, каковы эти ограничения макета (в случае, если вы хотите вести себя по-другому в match_parent ситуация, чем a wrap_content ситуации). Эти ограничения упаковываются в MeasureSpec значения, которые передаются в метод. Вот примерная корреляция режима значения:

  • ровно означает layout_width или layout_height значение было установлено в определенное значение. Вероятно, вы должны сделать свой вид такого размера. Это также может быть вызвано, когда match_parent используется, чтобы установить размер ровно в родительский вид (это зависит от макета в рамках).
  • AT_MOST обычно означает layout_width или layout_height значение match_parent или wrap_content где требуется максимальный размер (это зависит от макета в рамках), а размер родительского измерения является значением. Вы не должны быть больше этого размера.
  • не указано обычно означает layout_width или layout_height значение wrap_content без ограничений. Вы можете быть любого размера вы хотите. Некоторые макеты также используют этот обратный вызов, чтобы выяснить желаемый размер, Прежде чем определить, какие спецификации на самом деле передать вам снова во втором запросе меры.

контракт, который существует с onMeasure() это setMeasuredDimension()должны вызывается в конце с размером, который вы хотели бы видеть. Этот метод вызывается всеми реализациями платформы, включая реализацию по умолчанию, найденную в View, именно поэтому можно смело звонить super вместо этого, если это подходит в вашем случае.

конечно, поскольку фреймворк применяет реализацию по умолчанию, вам может не понадобиться переопределять этот метод, но вы можете увидеть отсечение случаи, когда пространство просмотра меньше, чем ваше содержимое, если вы этого не сделаете, и если вы выложите свой пользовательский вид с помощью wrap_content в обоих направлениях ваш взгляд может вообще не отображаться, потому что структура не знает, насколько она велика!

как правило, если вы переопределяете View, а не другой существующий виджет, это, вероятно, хорошая идея, чтобы обеспечить реализацию, даже если это так просто, как что-то вроде этого:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int desiredWidth = 100;
    int desiredHeight = 100;

    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    //MUST CALL THIS
    setMeasuredDimension(width, height);
}

надеюсь, что это поможет.

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

  • ровно match_parent точно + размер родителя
  • AT_MOST результаты wrap_content в качестве параметров в AT_MOST MeasureSpec
  • не указано не запускается

в случае горизонтальной прокрутки, ваш код будет работа.