6

I have a custom view which I quickly learned to not do object allocations in and moved all my Paint allocations into a different method, fine.

I need to use a StaticLayout however for some text that had some spannable 'stuff' applied.

    layoutBpm = new StaticLayout(bpmValue,
            textPaintBPM, arcRadius, Layout.Alignment.ALIGN_NORMAL, 0, 1,
            false);

    layoutBpm.draw(canvas);

This seemed to make sense to me to have in onDraw but I am of course getting warned to avoid this. It is important to me that I do everything I can to avoid any performance issues so I am trying to do this properly.

I can't seem to find any documentation I can understand that explains what some of the parameters to StaticLayout actually do (float spacingmult? float spacingadd?), but the third one there I think is maximum width for the StaticLayout text which I can only get from onMeasure as it relates to my canvas size. This leaves me wanting to put the assignment in onMeasure or onDraw, neither of which it seems I am meant to do. Is it okay to put StaticLayout assignment in onDraw or is there a better way to do this?

Hope this makes sense, I am very new to this. Thank you for any help.

(edit: I assume putting this assignment in a method and calling from onDraw/onMeasure is just being silly and will stop Eclipse warning me but wont actually help?)

4

1 回答 1

6

出现警告的原因是,您通常不需要在绘制操作中多次重新创建对象。onDraw()与 a 中的其他方法相比,可以调用数百次View。大多数时候,正在重新创建的对象是使用确切的参数重新创建的。其他时候,更改对象状态的开销比创建新对象的开销要小。

在 a 的情况下StaticLayout,您只需要在文本更改或调整填充、间距或最大宽度时创建一个新的。如果文本经常更改,那么您可能需要考虑DynamicLayout每次都会重新测量自己。重新测量的开销比创建新对象的开销更大,但它的发生频率不可能比onDraw()调用更频繁。

如果文本不经常更改并且您绝对必须使用静态布局,那么您可以摆脱这种结构。

StaticLayout myLayout;
String textSource = defaultSource;
Paint textPaint = defaultPaint;
int textWidth = defaultWidth; 

public CustomView(Context ctx) {
    super(ctx);
    createLayout();
}

public void setText(String text) {
   textSource = text;
   createLayout();
}

public void setWidth(int width) {
   textWidth = width;
   createLayout();
}

@Override
public void onDraw(Canvas canvas) {
   myLayout.draw(canvas);
}

private void createLayout() {
   myLayout = new StaticLayout(textSource, textPaint, textWidth, Layout.Alignment.ALIGN_NORMAL, 0, 1, false);
}

基本上,您只在某些情况发生变化时才创建新布局。否则,您只需重用您创建的最后一个对象。

EDIT:

跳过测量传递的一种方法是调用View#measure新调整大小的视图本身。所以你的createLayout()方法是这样的。

   private void createLayout() {
       myLayout = new StaticLayout(textSource, textPaint, textWidth, Layout.Alignment.ALIGN_NORMAL, 0, 1, false);
       int textWidthSpec = MeasureSpec.makeMeasureSpec(maxLayoutWidth, MeasureSpec.AT_MOST);
       int textHeightSpec = MeasureSpec.makeMeasureSpec(maxLayoutHeight, MeasureSpec.AT_MOST);
       myLayout.measure(textWidthSpec, textHeightSpec);
       forceLayout();
    }

基本上,它的作用是告诉布局 View 可以达到的最大值。它会衡量自己和孩子。forceLayout() 将在父级(您的自定义视图)上调用,它将根据新测量重新布局其他内容。

我应该注意,我从来没有用 StaticLayout 做过这个,所以我不知道会发生什么。似乎在创建视图时可能已经处理了这种类型的测量,但可能没有。

我希望这有帮助。

于 2013-06-13T17:57:04.247 回答