Search code examples
androidandroid-layoutandroid-datepicker

Calling Android DatePicker constructor when adding a DatePicker widget dynamically to the layout throws exception


I'm developing an Android application that generates the layout widgets in the UI dynamically at runtime (this is because the amount and the types of widgets are undefined)

public class DynamicLayout extends Activity{

private ScrollView scrollView;
private LinearLayout linearLayout;

@Override
public void onCreate(Bundle savedInstanceState) {       

        super.onCreate(savedInstanceState);

        scrollView = new ScrollView(this);
        linearLayout = new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        scrollView.addView(linearLayout);
        setContentView(scrollView);
        new LoadWidgets().execute();
    }
 }

  class LoadWidgets extends AsyncTask<String, String, String>{
        @Override
        protected void onPreExecute() {...}

        @Override            
        protected String doInBackground(String... args) {   
        ...
        while (condition){

            String widgetType;

            /* code that that finds out what type of widget needs to be added
             * to layout and puts "EditText", "TimePicker", "DatePicker", "TextView"
             * or "ListView" in the widgetType String */

            if (widgetType.equals("EditText") )
             { 
                final EditText editText = new EditText(this);

                runOnUiThread(new Runnable() {
                    public void run() {
                        linearLayout.addView(editText);
                    }
                });
             }
        else if (widgetType.equals("TimePicker") )
             { 
                final TimePicker timePicker = new TimePicker(this);

                runOnUiThread(new Runnable() {
                    public void run() {
                        linearLayout.addView(timePicker);
                    }
                });
             }
        else if (widgetType.equals("DatePicker") )
             { 
                final DatePicker datePicker = new DatePicker(this);

                runOnUiThread(new Runnable() {
                    public void run() {
                        linearLayout.addView(datePicker);
                    }
                });

             }
        else if /* other widgets... */

        }
              @Override
              protected void onPostExecute(String s) {...}
      }
  }

it works perfectly fine for all the widgets except for DatePicker, in fact this line of code...

final DatePicker datePicker = new DatePicker(this);

throws this exception:

06-27 09:58:48.471: E/AndroidRuntime(877): Caused by: android.view.InflateException: Binary XML file line #81: Error inflating class android.widget.CalendarView
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.createView(LayoutInflater.java:613)
06-27 09:58:48.471: E/AndroidRuntime(877):  at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.onCreateView(LayoutInflater.java:660)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:685)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.DatePicker.<init>(DatePicker.java:171)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.DatePicker.<init>(DatePicker.java:145)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.DatePicker.<init>(DatePicker.java:141)
06-27 09:58:48.471: E/AndroidRuntime(877):  at com.dynamicLayoutApp.DynamicLayout.loadWidgets(DynamicLayout.java:302)
06-27 09:58:48.471: E/AndroidRuntime(877):  at com.dynamicLayoutApp.DynamicLayout.access$6(DynamicLayout.java:215)
06-27 09:58:48.471: E/AndroidRuntime(877):  at com.dynamicLayoutApp.DynamicLayout$LoadDynamicLayout.doInBackground(DynamicLayout.java:160)
06-27 09:58:48.471: E/AndroidRuntime(877):  at com.dynamicLayoutApp.DynamicLayout$LoadDynamicLayout.doInBackground(DynamicLayout.java:1)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.os.AsyncTask$2.call(AsyncTask.java:287)
06-27 09:58:48.471: E/AndroidRuntime(877):  at java.util.concurrent.FutureTask.run(FutureTask.java:234)
06-27 09:58:48.471: E/AndroidRuntime(877):  ... 4 more
06-27 09:58:48.471: E/AndroidRuntime(877): Caused by: java.lang.reflect.InvocationTargetException
06-27 09:58:48.471: E/AndroidRuntime(877):  at java.lang.reflect.Constructor.constructNative(Native Method)
06-27 09:58:48.471: E/AndroidRuntime(877):  at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.LayoutInflater.createView(LayoutInflater.java:587)
06-27 09:58:48.471: E/AndroidRuntime(877):  ... 19 more
06-27 09:58:48.471: E/AndroidRuntime(877): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.os.Handler.<init>(Handler.java:197)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.os.Handler.<init>(Handler.java:111)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.GestureDetector$GestureHandler.<init>(GestureDetector.java:250)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.GestureDetector.<init>(GestureDetector.java:350)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.view.GestureDetector.<init>(GestureDetector.java:331)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.CalendarView$WeeksAdapter.<init>(CalendarView.java:1349)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.CalendarView.setUpAdapter(CalendarView.java:1007)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.CalendarView.<init>(CalendarView.java:405)
06-27 09:58:48.471: E/AndroidRuntime(877):  at android.widget.CalendarView.<init>(CalendarView.java:333)
06-27 09:58:48.471: E/AndroidRuntime(877):  ... 22 more
06-27 09:58:53.471: E/WindowManager(877): Activity com.dynamicLayoutApp.DynamicLayout has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{40db1080 V.E..... R.....ID 0,0-563,96} that was originally added here
06-27 09:58:53.471: E/WindowManager(877): android.view.WindowLeaked: Activity com.dynamicLayoutApp.DynamicLayout has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{40db1080 V.E..... R.....ID 0,0-563,96} that was originally added here
06-27 09:58:53.471: E/WindowManager(877):   at android.view.ViewRootImpl.<init>(ViewRootImpl.java:354)
06-27 09:58:53.471: E/WindowManager(877):   at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:216)
06-27 09:58:53.471: E/WindowManager(877):   at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
06-27 09:58:53.471: E/WindowManager(877):   at android.app.Dialog.show(Dialog.java:281)

Solution

  • I resolved the issue; I moved the initialisation of DatePicker to the runOnUiThread method like this:

    ...
    else if (widgetType.equals("TimePicker") )
                runOnUiThread(new Runnable() {
                    public void run() {
                        TimePicker timePicker = new TimePicker(DynamicLayout.this);
                        linearLayout.addView(timePicker);
                    }
                });
        else if
    ...
    

    I have already known that it's not recommended to manipulate the UI from a background thread but I thought it was fine to do it when I succeded to intialise the other types of widgets from a background thread.