Note that there are some explanatory texts on larger screens.

plurals
  1. POBest practices for dealing with expensive view height calculation?
    primarykey
    data
    text
    <p>I keep running into a sizing and layout problem for custom views and I'm wondering if anyone can suggest a "best practices" approach. The problem is as follows. Imagine a custom view where the height required for the content depends on the width of the view (similar to a multi-line TextView). (Obviously, this only applies if the height isn't fixed by the layout parameters.) The catch is that for a given width, it's rather expensive to compute the content height in these custom views. In particular, it's too expensive to be computed on the UI thread, so at some point a worker thread needs to be fired up to compute the layout and when it is finished, the UI needs to be updated.</p> <p>The question is, how should this be designed? I've thought of several strategies. They all assume that whenever the height is calculated, the corresponding width is recorded.</p> <p>The first strategy is shown in this code:</p> <pre><code>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = measureWidth(widthMeasureSpec); setMeasuredDimension(width, measureHeight(heightMeasureSpec, width)); } private int measureWidth(int widthMeasureSpec) { // irrelevant to this problem } private int measureHeight(int heightMeasureSpec, int width) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { if (width != mLastWidth) { interruptAnyExistingLayoutThread(); mLastWidth = width; mLayoutHeight = DEFAULT_HEIGHT; startNewLayoutThread(); } result = mLayoutHeight; if (specMode == MeasureSpec.AT_MOST &amp;&amp; result &gt; specSize) { result = specSize; } } return result; } </code></pre> <p>When the layout thread finishes, it posts a Runnable to the UI thread to set <code>mLayoutHeight</code> to the calculated height and then call <code>requestLayout()</code> (and <code>invalidate()</code>).</p> <p>A second strategy is to have <code>onMeasure</code> always use the then-current value for <code>mLayoutHeight</code> (without firing up a layout thread). Testing for changes in width and firing up a layout thread would be done by overriding <code>onSizeChanged</code>.</p> <p>A third strategy is to be lazy and wait to fire up the layout thread (if necessary) in <code>onDraw</code>.</p> <p>I would like to minimize the number of times a layout thread is launched and/or killed, while also calculating the required height as soon as possible. It would probably be good to minimize the number of calls to <code>requestLayout()</code> as well.</p> <p>From the docs, it's clear that <code>onMeasure</code> might be called several times during the course of a single layout. It's less clear (but seems likely) that <code>onSizeChanged</code> might also be called several times. So I'm thinking that putting the logic in <code>onDraw</code> might be the better strategy. But that seems contrary to the spirit of custom view sizing, so I have an admittedly irrational bias against it.</p> <p>Other people must have faced this same problem. Are there approaches I've missed? Is there a best approach?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload