Reputation: 1305
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.
Upvotes: 3
Views: 868
Reputation: 87064
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.
Upvotes: 2
Reputation: 16265
I've never tried this on a View
before, but I think it could help direct your app to only work with the ListView
, after the IdiomView
is drawn. You may be able to use a listener to get this to happen. However, it will require you to use a code behind method.
Create an interface in IdiomView
such as
public static interface OnDrawComplete {
public abstract void onDrawComplete();
}
private OnDrawComplete mListener;
public IdiomView(Context context) {
super(context);
// this forces it to make sure it's actually an activity
// context so you can get a listener
if (context instanceof Activity)
this.mListener = context;
}
Now at the end of your drawing, call it
if (this.mListener != null)
this.mListener.onDrawComplete();
This will call the Activity that created the object and notify of it's drawing. So within your Activity, you implement the interface
public void onDrawComplete() {
// After the draw completes, it calls this callback.
// setup the rest of your layout now
mQuoteList = (ListView) findViewById(R.id.quote_list);
this.populateQuoteList();
}
You may also be able to call this.mListener.onDrawComplete();
in some sort of other View
event, but I can't find one that is appropriate. You mention the View.post(runnable)
, which may work but I've never used that sort of thing either.
The biggest potential issue is that I don't know how the IdiomView
will receive a Context or which one. In this case you need the Activity
context of the one implementing the interface.
It may be that you need to instantiate and add it to the layout manually to use this method if it doesn't get the right context.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mIdiomTextView = new mIdiomTextView(this);
// add to the Layout manually.
}
Here is more of it fleshed out
IdiomView
public class IdiomView extends TextView {
public static interface OnDrawComplete {
public abstract void onDrawComplete();
}
private OnDrawComplete mListener;
public IdiomView(Context context) {
this(context, 0);
}
public IdiomView(Context context, AttributeSet attrs){
super(context, attrs);
if (context instanceof Activity)
this.mListener = context;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// do your drawing code here
// ...
// after drawing, call the listener, which tells your
// Activity to deal with the ListView.
if (this.mListener != null)
this.mListener.onDrawComplete();
}
}
Activity
public class MyActivity extends Activity implements IdiomView.OnDrawComplete {
//...
private IdiomView mIdiomTextView;
private ListView mQuoteList;
public void onDrawComplete() {
// After the draw completes, it calls this callback.
// setup the rest of your layout now
mQuoteList = (ListView) findViewById(R.id.quote_list);
this.populateQuoteList();
// now show the ListView
mQuoteList.setVisibility(View.Visible);
}
private void populateQuoteList() {
// populate your ListView here
}
}
Upvotes: 3