如何处理 onMeasue 中自定义视图的权重?

发布于 2024-12-29 04:11:57 字数 2017 浏览 1 评论 0原文

我正在创建一个自定义视图,它将缩放其子 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_PARENTWRAP_CONTENT 宽度工作得很好,但是当我尝试给组件一个权重并让它以这种方式自动设置权重时,就搞砸了。

MeasureSpec.getSize(widthMeasureSpec) 返回 0getMinimumSuggestedWidth() 也会返回 0。这会产生一个奇妙的 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 ?我觉得这应该有一个简单的答案,但我不知道它是什么。

I have a custom view which I'm creating that will scale the font size of its child TextViews to fit all in a set width, so each is on its own line. Obviously it's going to need the width to figure that out.

I had overridden onMeasure() like so:

@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() calculates the width of the largest line of text and everything works with that. This code is working fine for FILL_PARENT and WRAP_CONTENT widths, but screws up when I try to give the component a weight and let it auto-set its weight that way.

MeasureSpec.getSize(widthMeasureSpec) returns 0, as getMinimumSuggestedWidth() does, too. This gives a wonderful Activity Not Responding error, since lineWidth will always be less than calculateTextWidth() would ever return, so the while loop runs forever. The XML code I used was this, essentially:

<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>

I understand why it's returning 0 - obviously, it's being set to 0 - but how do I get it to use the layout_weight? I feel like it's something that should have a simple answer, but I'm at a loss as to what it is.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

临走之时 2025-01-05 04:11:57

我最终通过一些重要的谷歌搜索找到了答案。在 这个页面我发现当android:layout_weight设置时onMeasure实际上被调用了两次。后来我发现 How Android Draws Views 中提到了 measure() 可以被调用任意多次,但它并没有立即出现在我的脑海中。第一遍显然无法给出每个子视图的大小值,因为它不知道该视图的所有兄弟视图的权重是多少。它会遍历一次并使用 MeasureSpec.UNSPECIFIED 给出 0 的宽度,以查看子级是否有任何特定约束,然后再次遍历它以使用 MeasureSpec.EXACTLY 分配实际权重。我的 Activity 在第一次通过时就搞砸了,所以它从未到达布局步骤。将我的 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);
}

I ended up figuring it out through some significant Googling. On this page I found out that onMeasure is actually called twice when android:layout_weight is set. I found out later that it's mentioned in How Android Draws Views that measure() can be called any number of times, it just didn't immediately sit in my brain. The first pass obviously can't give a value for the size of each child, since it doesn't know what the weights of all of the View's siblings are. It goes through it once and gives a width of 0 with a MeasureSpec.UNSPECIFIED to see if the child has any specific constraints, then goes through again to assign the actual weights with a MeasureSpec.EXACTLY. My Activity was getting screwed up at the first pass, so it never got to the layout step. Amending my onMeasure() to the following code fixed the problem.

@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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文