Как работать с весами в onMeasue для пользовательских представлений? - PullRequest
6 голосов
/ 26 января 2012

У меня есть пользовательский вид, который я создаю, который будет масштабировать размер шрифта его дочернего элемента TextViews, чтобы он соответствовал всем на заданной ширине, поэтому каждый из них находится на отдельной строке. Очевидно, что понадобится ширина, чтобы понять это.

Я переопределил onMeasure() вот так:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int lineWidth = widthSize - getPaddingLeft() - getPaddingRight();

  // Change the text size until the largest line of text fits.
  while (lineWidth < calculateTextWidth()) {
    for (TextView textView : this.childViews) {
      float newSize = textView.getTextSize() - 1;
      textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, newSize);
    }
  }

  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

calculateTextWidth() вычисляет ширину самой большой строки текста, и все с этим работает. Этот код работает нормально для ширины FILL_PARENT и WRAP_CONTENT, но он облажается, когда я пытаюсь определить вес компонента и позволяю ему автоматически устанавливать его вес таким образом.

MeasureSpec.getSize(widthMeasureSpec) возвращает 0, как и getMinimumSuggestedWidth(). Это дает замечательную ошибку Activity Not Responding, так как lineWidth всегда будет меньше, чем calculateTextWidth() когда-либо вернется, поэтому цикл while выполняется вечно. Код XML, который я использовал, был следующим:

<com.my.widgets.ScalingTextViewGroup
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:orientation="vertical"
  android:gravity="center_horizontal"
  >
    <TextView
        android:id="@+id/text_1"
        android:text="Text 1"
        android:textSize="20sp" />
    <TextView
        android:id="@+id/text_2"
        android:text="Text 2"
        android:textSize="18sp" />
    <TextView
        android:id="@+id/text_3"
        android:text="Text 3"
        android:textSize="18sp" />
    <TextView
        android:id="@+id/text_4"
        android:text="Text 4"
        android:textSize="18sp" />
</com.my.widgets.ScalingTextViewGroup>

Я понимаю, почему он возвращает 0 - очевидно, он установлен на 0 - но как мне заставить его использовать layout_weight? Я чувствую, что это должен быть простой ответ, но я не знаю, что это такое.

1 Ответ

10 голосов
/ 26 января 2012

В итоге я понял это с помощью какого-то значительного поиска в Google.На этой странице я обнаружил, что onMeasure фактически вызывается дважды , когда установлен android:layout_weight.Позже я узнал, что в Как Android рисует представления упоминается, что measure() можно вызывать любое количество раз, просто это не сразу у меня в голове.Первый проход, очевидно, не может дать значение для размера каждого дочернего элемента, так как он не знает, каков вес всех братьев и сестер View.Он проходит через него один раз и дает ширину 0 с помощью MeasureSpec.UNSPECIFIED, чтобы увидеть, есть ли у дочернего элемента какие-либо конкретные ограничения, затем снова проходит , чтобы назначить фактические веса с помощью MeasureSpec.EXACTLY.Моя активность была испорчена при первом проходе, поэтому она никогда не доходила до шага макета.Исправление моего onMeasure () в следующем коде решило проблему.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  if (widthMode != MeasureSpec.UNSPECIFIED) {
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int lineWidth = widthSize - getPaddingLeft() - getPaddingRight();

    // Change the text size until the largest line of text fits.
    while (lineWidth < calculateTextWidth()) {
      for (TextView textView : this.childViews) {
        float newSize = textView.getTextSize() - 1;
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, newSize);
      }
    }
  }

  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
...