suku suku - 4 months ago 29
Android Question

SetSelection on a spinner crashes when layout_weight is assigned to spinner

I have done a simplified experiment to identify where I am having this problem. It was a long question with a lot of code earlier. Now I have kept a small and simple code:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_spinner_test);

Spinner spin1 = (Spinner) findViewById(R.id.spin1);
spin1.setAdapter(new ProfileSpinnerAdapter(this, R.array.feet));
spin1.setSelection(0); //does not crash
spin1.setSelection(getResources().getStringArray(R.array.feet).length - 1); //it crashes
//it crashes for any value greater than 0 and less than array length.


This is the error:

java.lang.NullPointerException: Attempt to read from field 'int android.view.ViewGroup$LayoutParams.width' on a null object reference
at android.widget.TextView.checkForRelayout(TextView.java:6830)
at android.widget.TextView.onRtlPropertiesChanged(TextView.java:8948)
at android.view.View.resolveRtlPropertiesIfNeeded(View.java:13118)
at android.view.View.measure(View.java:17557)
at android.widget.Spinner.setUpChild(Spinner.java:657)
at android.widget.Spinner.makeView(Spinner.java:610)
at android.widget.Spinner.getBaseline(Spinner.java:456)
at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1294)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:615)
at android.view.View.measure(View.java:17562)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5536)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
at android.view.View.measure(View.java:17562)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5536)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1436)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:722)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:613)
at android.view.View.measure(View.java:17562)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5536)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2871)
at android.view.View.measure(View.java:17562)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2015)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1173)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1379)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5891)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
at android.view.Choreographer.doCallbacks(Choreographer.java:580)
at android.view.Choreographer.doFrame(Choreographer.java:550)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5294)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)


This error comes because I have set the layout_width=0dp. But at the same time layout_weight = 1.
NOTE: The spinner is properly inflated when setSelection(0). So the problem is not with straight forward inflation.

Here is the xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context="in.jiyofit.the_app.SpinnerTestActivity">

<Spinner
android:id="@+id/spin1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />




The entire code works perfectly if layout_width = Any non zero dp or match_parent or wrap_content while there is no layout_weight.

But with the same above layout the following code works correctly with no crashes:

Spinner spin1 = (Spinner) findViewById(R.id.spin1);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.feet, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spin1.setAdapter(adapter);
spin1.setSelection(getResources().getStringArray(R.array.feet).length - 1);


Thus I can conclude that there is some problem with the ProfileSpinnerAdapter class that I have made which conflicts with layout_weight = 1 and width = 0dp

Here is the adapter:

public class ProfileSpinnerAdapter extends BaseAdapter {
String[] array;
Context ctx;

public ProfileSpinnerAdapter(Context context, int arrayID) {
this.ctx = context;
this.array = ctx.getResources().getStringArray(arrayID);
}

@Override
public int getCount() {
return array.length;
}

@Override
public Object getItem(int position) {
return array[position];
}

@Override
public long getItemId(int position) {
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = new TextView(ctx);
textView.setText(array[position]);
textView.setTextSize(16);
if(array[position].length() > 6){
Typeface hindiFont = Typeface.createFromAsset(ctx.getAssets(),"fonts/mfdev010.ttf");
textView.setTextSize(22);
textView.setTypeface(hindiFont);
}
if(position == 0){
textView.setTextColor(ContextCompat.getColor(ctx, R.color.primaryText));
}
return textView;
}

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView textView = new TextView(ctx);
textView.setText(array[position]);
textView.setTextSize(16);
textView.setPadding(0, 5, 0, 0);
if(array[position].length() > 6){
Typeface hindiFont = Typeface.createFromAsset(ctx.getAssets(),"fonts/mfdev010.ttf");
textView.setTextSize(22);
textView.setTypeface(hindiFont);
textView.setPadding(10,5,10,5);
}
textView.setBackgroundColor(ContextCompat.getColor(ctx, R.color.white));
textView.setTextColor(ContextCompat.getColor(ctx, R.color.primaryText));
textView.setGravity(Gravity.CENTER);
/*
the adapter fills the number of elements based in the getCount
so either getCount returns value conditionally for an array of different size in getDropDownView
or the requisite value at position is hidden
*/
if(position == 0){
textView.setVisibility(View.GONE);
textView.setHeight(0);
}
return textView;
}
}


The cause for the error is in the adapter. It gives no error if spinner width = 100dp and only gives error when layout_weight attribute is put on the spinner

Answer

I have solved the problem with putting the TextView in a layout group programatically:

@Override
public View getView(int position, View arg1, ViewGroup arg2){

    ...

    // without this rtl the app crashes on lollipop devices with shawnlin number-picker
    RelativeLayout rtlContainer = new RelativeLayout(mContext);
    rtlContainer.addView(textView);
    return rtlContainer;
}

Also interesting, that the crash occurred after I included this numberpicker library to my project. I don't know what is the coherence between the library and the crash, but if I exclude it, the crash disappear again.

Also I found a related android issue. The reason provided is: Since the TextView is not wrapped in a ViewGroup, any interaction with the view causes the application to crash with a null pointer exception. The TextView is trying to measure some ViewGroup.LayoutParams.