Search code examples
javaandroidandroid-layoutandroid-custom-view

How do I draw certain views after others?


I have a simple LinearLayout containing only a custom view extending TextView (which I shall start calling "IdiomView") and a ListView. The only difference in IdiomView from a normal TextView is that I have overridden the onDraw() method to iteratively reduce the text size until the text is less than 3 lines long. My problem is that when the views are drawn, the user will see this:

 ______________
|__ACTION_BAR__|
|  IdiomView   |
|______________|
|              |
|   ListView   |
|              |
|              |
|______________|

which quickly becomes:

 ______________
|__ACTION_BAR__|
|__IdiomView __|
|              |
|   ListView   |
|              |
|              |
|              |
|______________|

i.e. the ListView is drawn and then jumps up after IdiomView has sorted out its size.

What I would like is a way to wait until my IdiomView is fully drawn, before drawing the ListView. This post What event is fired after all views are fully drawn? explains how to line up a thread after the drawing is complete by calling View.post(Runnable). The problem is my overridden onDraw() method calls onDraw() multiple times in order to calculate the whether the smaller text covers less than 3 lines, so this element probably "finishes drawing" numerous times before I would want the ListView to appear.

I appreciate all comments and answers. Here's my current code:

layout XML:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:background="@color/off_white"
    android:orientation="vertical" >

    <carter.cwords.idioms.IdiomView
        android:id="@+id/idiom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:textColor="@color/transparent"
        android:textSize="28sp"
        android:textStyle="italic"
        android:visibility="invisible" />

    <ListView
        android:id="@+id/quote_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:choiceMode="none"
        android:footerDividersEnabled="false"
        android:headerDividersEnabled="false"
        android:visibility="invisible" />

</LinearLayout>

Activity:

private IdiomView mIdiomTextView;
private ListView mQuoteList;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.idiom_of_the_day);
    mIdiomTextView = (IdiomView) findViewById(R.id.idiom);
    mQuoteList = (ListView) findViewById(R.id.quote_list);      

    // Populate page data onResume()
}

@Override
protected void onResume() {
    super.onResume();

    sendRequest(R.string.url_idiom_of_the_day, new AfterRequest(){
        @Override
        public void useResults(Document resultXml) {
            if(resultXml != null){

                Log.i(getClass().getSimpleName(), "useResults()");

                String idiomString = XmlUtilities.getTextValue(resultXml, NetworkHelper.XML_TAG_IDIOM_CONTENT);

                logDebug("idiomString: " + idiomString);
                mIdiomTextView.setText("\"" + idiomString + "\"");
                mQuoteList.setAdapter(new ContentAdapter(mContext, resultXml));
                mIdiomTextView.setVisibility(View.VISIBLE);

                mIdiomTextView.post(new Runnable(){
                    @Override
                    public void run() {
                        mQuoteList.setVisibility(View.VISIBLE);
                    }
                });
            }

        }
    });

}

IdiomView:

public class IdiomView extends TextView {

    public IdiomView(Context context) {     
        super(context);
    }

    public IdiomView(Context context, AttributeSet attrs){
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i(getClass().getSimpleName(), "onDraw(): " + this.getLineCount());
        if(this.getLineCount() > 2){
            this.setTextSize(TypedValue.COMPLEX_UNIT_PX, this.getTextSize()-1);
        }
        else{
            this.setTextColor(getResources().getColor(R.color.text));
        }
        Log.i(getClass().getSimpleName(), "onDraw(): " + this.getLineCount());

    }


}

Thank you very much.


Solution

  • Setting the text size repeatedly until you find the proper font size could be very inefficient because your TextView will need to be remeasured and redrawn each time. Instead of doing this, you could measure and set the text only once by using its TextView.getPaint() method. You could than measure the text with the Paint in a loop providing it different font sizes until the value returned by Paint.measureText() is below two times the current width of the widget.