Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API11 (and now12) and Spannable causing runtime NPE

Tags:

android

Update 14th May

It's the mix of text sizes that breaks it, if I replace the

<item name  = "android:textSize">16sp</item> 

with just a change of colour like

<item name="android:textColor">#00ff00</item>

then it runs OK.

The references to TextLine,measure in the logcat should have given me a clue. I'd still like to fix it though, I'm sure others will have a requirement for a mix of sizes in a single text line.

Udpdated 12 May - minimal code example is shown at the end of this post

Code which works fine under SDK 2.everything, throws a null pointer exception when run in a 3.0 emulator. I've narrowed it down to the only occurrence of a SpannableString in my code. I'm using this to put some text of differing font sizes in a banner area at the top of the screen. The logcat is:

FATAL EXCEPTION: main
java.lang.NullPointerException
    at android.text.style.TextAppearanceSpan.updateDrawState(TextAppearanceSpan.java:209)
    at android.text.TextLine.handleRun(TextLine.java:848)
    at android.text.TextLine.measureRun(TextLine.java:399)
    at android.text.TextLine.measure(TextLine.java:278)
    at android.text.TextLine.metrics(TextLine.java:252)
    at android.text.Layout.measurePara(Layout.java:1432)
    at android.text.Layout.getDesiredWidth(Layout.java:89)
    at android.text.Layout.getDesiredWidth(Layout.java:68)
    at android.widget.TextView.onMeasure(TextView.java:5713)
    at android.view.View.measure(View.java:10577)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:581)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:365)
    at android.view.View.measure(View.java:10577)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:581)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:365)
    at android.view.View.measure(View.java:10577)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:581)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:365)
    at android.view.View.measure(View.java:10577)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4270)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:267)
    at android.view.View.measure(View.java:10577)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:764)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:519)
    at android.view.View.measure(View.java:10577)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4270)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:267)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:1876)
    at android.view.View.measure(View.java:10577)
    at android.view.ViewRoot.performTraversals(ViewRoot.java:885)
    at android.view.ViewRoot.handleMessage(ViewRoot.java:1944)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:126)
    at android.app.ActivityThread.main(ActivityThread.java:3997)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:491)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
    at dalvik.system.NativeStart.main(Native Method)

The code is in a handler called by a timer, it writes to a text view with the following essential lines (null checks etc omitted)

TextView tvBanner = (TextView) findViewById(R.id.RefText);
.....
int startSpan = altDispStr.indexOf("\n");
int endSpan = altDispStr.length();
spanRange = new SpannableString(altDispStr);
spanRange.setSpan(new TextAppearanceSpan(this,
    R.style.custompoint), startSpan, endSpan,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

....
....
tvBanner.setText(spanRange);

When I trap it in the debugger (not easy with the 3.0 emulator and 3GB of RAM) it gets through the handler method OK, - spanRange is not null, but the NPE and FC happens sometime later in the main looper.

The textview (RefTxt) is in an xml called infobar.xml which is included in the main.xml. The infobar's essential components are:

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/BannerRelView"
android:layout_alignParentTop="true"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="#800000ff"
android:layout_width="wrap_content">
    <TextView
       android:id="@+id/BannerOptionsButton"      
       android:layout_alignParentRight="true"   
       ....     
     </TextView>
     <TextView
         android:id="@+id/BannerGPS"
         android:layout_toLeftOf="@+id/BannerOptionsButton"    
         ....
      </TextView>
      <TextView
         android:id="@+id/RefText"
         android:layout_toLeftOf="@+id/BannerGPS"
         ......
      </TextView>
</RelativeLayout>

If I replace the SpannableString with a plain old String then the app runs in 3.0 emulators. I'm wondering if there are any extra measures I need to take with Spannables and tablet devices?

When it dies, the debug perspective shows crash state - mt and tl are both not null

Update example I've reduced this to the smallest code example which demonstrates the error, it's OK under 2.1 but crashes with the same NPE under 3.0

Activity code:

public class SpanTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String dispStr = "I'm the first line\nI'm the second line";
        TextView tvBanner = (TextView) findViewById(R.id.textView1);
        int startSpan = dispStr.indexOf("\n");
        int endSpan = dispStr.length();
        Spannable spanRange = new SpannableString(dispStr);
        spanRange.setSpan(new TextAppearanceSpan(this, R.style.custompoint), startSpan, endSpan, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        tvBanner.setText(spanRange);
    }
}

main.xml is a LinearLayout containg just the one TextView. My styles.xml containing custompoint is:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style
        name="custompoint">
        <item name="android:textSize">24sp</item>
        <item name="android:textStyle">bold</item>
    </style>
</resources>

.

like image 617
NickT Avatar asked May 11 '11 16:05

NickT


1 Answers

Your TextView is using a TextPaint object that doesn't have the linkColor set. I see the style/theme you are using is somehow not setting the linkColor. Try adding a linkColor to the style and see if that solves your problem.

EDIT: you should probably inherit the default style, and that will give you what you want. Not sure why the behavior changed from 2.1 to 3.0, but...

like image 182
Femi Avatar answered Sep 22 '22 07:09

Femi