Переопределите onMeasure () RelativeLayout, чтобы ограничить его соотношение сторон - PullRequest
4 голосов
/ 20 декабря 2011

У меня есть расширенный RelativeLayout, который, когда программно позиционируется и измеряется с использованием RelativeLayout.LayoutParams, должен ограничивать себя заданным соотношением сторон. Как правило, я хотел бы, чтобы оно ограничивалось 1: 1, поэтому, если RelativeLayout.LayoutParams содержит width 200 и height 100, пользовательский RelativeLayout будет ограничиваться 100 x 100.

Я уже привык переопределять onMeasure() в обычных пользовательских View s для достижения аналогичных целей. Например, я создал свой собственный конвертер изображений SVG, а пользовательский View, который отображает изображение SVG, имеет переопределенный onMeasure(), который гарантирует, что вызов setMeasuredDimension() содержит измерения, которые (a) соответствуют исходным спецификациям измерений и (b) соответствуют соотношению сторон исходного изображения SVG.

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

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

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

    // Restrict the aspect ratio to 1:1, fitting within original specified dimensions
    int chosenDimension = Math.min(chosenWidth, chosenHeight);
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Что на самом деле происходит, когда я это делаю, так это то, что, как ни странно, высота должным образом ограничена, как я и предполагал, а ширина - нет. Для иллюстрации:

  • Задание высоты 200 и ширины 100 в RelativeLayout.LayoutParams приводит к тому, что мой пользовательский RelativeLayout имеет высоту 100 и ширину 100. -> Правильно.

  • Задание высоты 100 и ширины 200 в RelativeLayout.LayoutParams приводит к тому, что мой пользовательский RelativeLayout имеет высоту 100 и ширину 200. -> Неправильно.

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

Уточнение: результирующие значения ширины и высоты, которые я читаю, взяты из использования getWidth() и getHeight(). Эти значения считываются в будущем, после того, как процесс макета был выполнен снова.

1 Ответ

17 голосов
/ 20 декабря 2011

Я обошел это сейчас, также установив width и height из LayoutParams, которые в настоящее время удерживаются RelativeLayout в onMeasure().

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

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

    // Restrict the aspect ratio to 1:1, fitting within original specified dimensions
    int chosenDimension = Math.min(widthSize, heightSize);
    widthMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);
    heightMeasureSpec = MeasureSpec.makeMeasureSpec(chosenDimension, MeasureSpec.AT_MOST);

    getLayoutParams().height = chosenDimension;
    getLayoutParams().width = chosenDimension;
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    
    }

Теперь это работает так, как нужно: размер RelativeLayout (и последующие вызовы getWidth() и getHeight()) теперь согласуются с ограничениями размера, примененными в моем переопределенном onMeasure().

...