Android Databinding Adapter Null Pointer Exception

2019-09-16 01:15发布

问题:

I'm trying to use Android Databinding in my Android app. I've an Object Outlet if I initialize the object without any data. Everything works fine. And all data are well collected. But, if I initialize the Object with data and passing it to the layout, it crashes the app with complains of Invoking Interface method on Null Views. This is my Layout file below:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">

        <data>
            <import type="java.util.List"/>
            <import type="android.view.View" />
            <variable
                name="outlet"
                type="co.deliveryscience.dangote.Network.model.Outlet.Create.Outlet"/>
        </data>

        <android.support.v4.widget.NestedScrollView
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <android.support.v7.widget.CardView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="32dp"
                    android:layout_marginLeft="64dp"
                    android:layout_marginRight="64dp"
                    android:layout_marginTop="32dp"
                    android:background="#FFF"
                    android:animateLayoutChanges="true"
                    app:cardCornerRadius="4dp"
                    app:elevation="5dp">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical"
                        android:paddingBottom="16dp"
                        android:paddingLeft="16dp"
                        android:paddingRight="16dp"
                        android:paddingTop="16dp"
                        android:animateLayoutChanges="true">

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            style="@style/HeaderText"
                            android:text="@string/personal_information"
                            android:layout_marginBottom="8dp"
                            android:layout_marginTop="16dp"/>

                        <View
                            android:layout_width="match_parent"
                            android:layout_height="0.5dp"
                            android:background="@color/gray_semi_transparent"
                            android:layout_marginBottom="32dp"/>

                        <LinearLayout
                            android:id="@+id/layoutCreate"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="vertical"
                            android:layout_marginBottom="32dp">

                            <TextView
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="@string/name_of_outlet"
                                android:layout_marginBottom="8dp"/>

                            <EditText
                                android:id="@+id/nameOfOutlet"
                                android:layout_width="match_parent"
                                android:layout_height="55dp"
                                android:inputType="textCapWords"
                                android:singleLine="true"
                                android:imeOptions="actionNext"
                                android:text='@{outlet.name ?? ""}'
                                android:addTextChangedListener="@{outlet.onNameChanged}"
                                android:nextFocusDown="@+id/address"/>

                        </LinearLayout>

                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal"
                            android:baselineAligned="false">

                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:orientation="vertical"
                                android:layout_marginRight="8dp"
                                android:layout_marginEnd="8dp">

                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="@string/ownerfirstname"
                                    android:layout_marginBottom="8dp"/>

                                <EditText
                                    android:id="@+id/fName"
                                    android:layout_width="match_parent"
                                    android:layout_height="55dp"
                                    android:layout_marginBottom="16dp"
                                    android:inputType="textCapWords"
                                    android:singleLine="true"
                                    android:imeOptions="actionNext"
                                    android:text="@{outlet.firstName}"
                                    android:addTextChangedListener="@{outlet.onFirstNameChanged}"
                                    android:nextFocusRight="@+id/lName"/>

                            </LinearLayout>

                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:orientation="vertical"
                                android:layout_marginLeft="8dp"
                                android:layout_marginStart="8dp">

                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="@string/ownerlastname"
                                    android:layout_marginBottom="8dp"/>

                                <EditText
                                    android:id="@+id/lName"
                                    android:layout_width="match_parent"
                                    android:layout_height="55dp"
                                    android:layout_marginBottom="16dp"
                                    android:inputType="textCapWords"
                                    android:text='@{outlet.lastName == null ? "" : outlet.lastName}'
                                    android:addTextChangedListener="@{outlet.onLastNameChanged}"
                                    android:singleLine="true"
                                    android:imeOptions="actionNext"
                                    android:nextFocusDown="@+id/nameOfOutlet"/>

                            </LinearLayout>

                        </LinearLayout>

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            style="@style/HeaderText"
                            android:text="@string/contact_information"
                            android:layout_marginBottom="8dp"
                            android:layout_marginTop="16dp"/>

                        <View
                            android:layout_width="match_parent"
                            android:layout_height="0.5dp"
                            android:background="@color/gray_semi_transparent"
                            android:layout_marginBottom="32dp"/>

                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="@string/address"
                            android:layout_marginBottom="8dp"/>

                        <EditText
                            android:id="@+id/address"
                            android:layout_width="match_parent"
                            android:layout_height="55dp"
                            android:layout_marginBottom="16dp"
                            android:inputType="textPostalAddress"
                            android:text='@{outlet.address == null ? "" : outlet.address}'
                            android:addTextChangedListener="@{outlet.onAddresChanged}"
                            android:imeOptions="actionNext"
                            android:nextFocusDown="@+id/lga"/>

                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal"
                            android:layout_marginBottom="16dp"
                            android:baselineAligned="false">

                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:orientation="vertical"
                                android:layout_weight="1"
                                android:layout_marginRight="8dp"
                                android:layout_marginEnd="8dp">

                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="@string/lga"
                                    android:layout_marginBottom="8dp"/>

                                <AutoCompleteTextView
                                    android:id="@+id/lga"
                                    android:layout_width="match_parent"
                                    android:layout_height="55dp"
                                    android:imeOptions="actionNext"
                                    android:inputType="textCapWords"
                                    android:text="@{outlet.lga}"
                                    android:singleLine="true"
                                    android:nextFocusRight="@+id/state"/>

                            </LinearLayout>

                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:orientation="vertical"
                                android:layout_weight="1"
                                android:layout_marginStart="8dp"
                                android:layout_marginLeft="8dp">

                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="@string/state"
                                    android:layout_marginBottom="8dp"/>

                                <AutoCompleteTextView
                                    android:id="@+id/state"
                                    android:layout_width="match_parent"
                                    android:layout_height="55dp"
                                    android:imeOptions="actionNext"
                                    android:inputType="textCapWords"
                                    android:singleLine="true"
                                    android:nextFocusDown="@+id/phoneNumber1" />

                            </LinearLayout>

                        </LinearLayout>

                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:orientation="horizontal"
                            android:layout_marginBottom="16dp"
                            android:baselineAligned="true">

                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:layout_marginRight="8dp"
                                android:layout_marginEnd="8dp"
                                android:orientation="vertical">

                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="@string/phone_number_1"
                                    android:layout_marginBottom="8dp"/>

                                <EditText
                                    android:id="@+id/phoneNumber1"
                                    android:layout_width="match_parent"
                                    android:layout_height="55dp"
                                    android:layout_marginBottom="16dp"
                                    android:inputType="phone"
                                    android:imeOptions="actionNext"
                                    android:addTextChangedListener="@{outlet.onPhoneNumberChanged}"
                                    android:text="@{outlet.phoneNumber}"
                                    android:singleLine="true"
                                    android:nextFocusDown="@+id/phoneNumber2"/>

                            </LinearLayout>

                            <LinearLayout
                                android:layout_width="0dp"
                                android:layout_height="wrap_content"
                                android:layout_weight="1"
                                android:layout_marginLeft="8dp"
                                android:layout_marginStart="8dp"
                                android:orientation="vertical">

                                <TextView
                                    android:layout_width="wrap_content"
                                    android:layout_height="wrap_content"
                                    android:text="@string/phone_number_2"
                                    android:layout_marginBottom="8dp"/>

                                <EditText
                                    android:id="@+id/phoneNumber2"
                                    android:layout_width="match_parent"
                                    android:layout_height="55dp"
                                    android:layout_marginBottom="16dp"
                                    android:inputType="phone"
                                    android:imeOptions="actionNext"
                                    android:addTextChangedListener="@{outlet.onOtherPhoneNumberChanged}"
                                    android:text="@{outlet.phoneNumber}"
                                    android:singleLine="true"
                                    android:nextFocusDown="@+id/outletOwnerEmail"/>

                            </LinearLayout>

                        </LinearLayout>

                    </LinearLayout>

                </android.support.v7.widget.CardView>

            </LinearLayout>

        </android.support.v4.widget.NestedScrollView>
    </layout>

And my Observable Object is this

    public class Outlet extends BaseObservable {

        private static final String TAG = "Outlet";
        @SerializedName("name")
        @Expose
        private String name;
        @SerializedName("firstName")
        @Expose
        private String firstName;
        @SerializedName("lastName")
        @Expose
        private String lastName;
        @SerializedName("phoneNumber")
        @Expose
        private String phoneNumber;
        @SerializedName("otherNumber")
        @Expose
        private String otherNumber;
        @SerializedName("address")
        @Expose
        private String address;

        /**
         * 
         * @return
         *     The name
         */
        @Bindable
        public String getName() {
            return name;
        }

        /**
         * 
         * @param name
         *     The name
         */
        public void setName(String name) {
            setAtomicName(name);
            notifyPropertyChanged(BR.name);
        }

        public void setAtomicName(String basic) {
            this.name = basic;
        }

        public TextWatcher onNameChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"Name", newValue);
                setAtomicName(newValue);
            }
        };

        /**
         * 
         * @return
         *     The firstName
         */
        @Bindable
        public String getFirstName() {
            return firstName;
        }

        /**
         * 
         * @param firstName
         *     The firstName
         */
        public void setFirstName(String firstName) {
            setAtomicFirstName(firstName);
            notifyPropertyChanged(BR.firstName);
        }

        public void setAtomicFirstName(String basic) {
            this.firstName = basic;
        }

        public TextWatcher onFirstNameChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"FirstName", newValue);
                setAtomicFirstName(newValue);
            }
        };

        /**
         * 
         * @return
         *     The lastName
         */
        @Bindable
        public String getLastName() {
            return lastName;
        }

        /**
         * 
         * @param lastName
         *     The lastName
         */
        public void setLastName(String lastName) {
            setAtomicLastName(lastName);
            notifyPropertyChanged(BR.lastName);
        }

        public void setAtomicLastName(String basic) {
            this.lastName = basic;
        }

        public TextWatcher onLastNameChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"lstname", newValue);
                setAtomicLastName(newValue);
            }
        };

        /**
         * 
         * @return
         *     The phoneNumber
         */
        @Bindable
        public String getPhoneNumber() {
            return phoneNumber;
        }

        /**
         * 
         * @param phoneNumber
         *     The phoneNumber
         */
        public void setPhoneNumber(String phoneNumber) {
            setAtomicPhoneNumber(phoneNumber);
            notifyPropertyChanged(BR.phoneNumber);
        }

        public void setAtomicPhoneNumber(String basic) {
            this.phoneNumber = basic;
        }

        public TextWatcher onPhoneNumberChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"phonenumber", newValue);
                setAtomicPhoneNumber(newValue);
            }
        };

        /**
         *
         * @return
         *     The otherNumber
         */
        public String getOtherNumber() {
            return otherNumber;
        }

        /**
         *
         * @param otherNumber
         *     The otherNumber
         */
        public void setOtherNumber(String otherNumber) {
            setAtomicOtherPhoneNumber(otherNumber);
            notifyPropertyChanged(BR.phoneNumber);
        }

        public void setAtomicOtherPhoneNumber(String basic) {
            this.otherNumber = basic;
        }

        public TextWatcher onOtherPhoneNumberChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"otherphone", newValue);
                setAtomicOtherPhoneNumber(newValue);
            }
        };

        /**
         * 
         * @return
         *     The address
         */
        @Bindable
        public String getAddress() {
            return address;
        }

        /**
         * 
         * @param address
         *     The address
         */
        public void setAddress(String address) {
            setAtomicAddress(address);
            notifyPropertyChanged(BR.phoneNumber);
        }

        public void setAtomicAddress(String basic) {
            this.address = basic;
        }

        public TextWatcher onAddresChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"address", newValue);
                setAtomicAddress(newValue);
            }
        };

        @BindingAdapter({"bind:email"})
        public static void checkEmail(EditText editText, String email) {
            if (editText != null) {
                editText.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    }

                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                    }

                    @Override
                    public void afterTextChanged(Editable editable) {
                        if (editText.length() > 0) {
                            if (!Utils.emailMatchPattern(editText.getText().toString())) editText.setError("Email Requried");
                        }
                    }
                });
            }
        }
    }

When I do this. Outlet outlet = new Outlet(); and assign it to my binding data like this binding.setOutlet(outlet); I wouldn't get any error. But, if I do this:

Outlet outlet = gson.fromJson(realmOutlet.getPayload(), Outlet.class);
binding.setOutlet(outlet);

I get an NPE Error and based on the NPE, it's not because the data is not there. It's like as if the adapters hasn't been initialized. I know it keeps pointing to the addressField because, it's the first item. And this is the adapter for Address. It's an abstract class. So, I'm confused why aren't they initializede:

 public TextWatcher onAddresChanged = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(String newValue) {
                Log.e(TAG+"address", newValue);
                setAtomicAddress(newValue);
            }
        };

Below is my Log I can't trace the log to know what exactly to debug. But, for it to be saying the view attaching itself to the Interface method is null. It's like, the Binding Layouts has not been initialized yet. So, why is it working when I initialize it with empty data?

And my onCreate code

     @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_create);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_create);
        realm = RealmUtils.getRealmInstance(getApplicationContext());

        setSupportActionBar(binding.createToolbar);
        action = getIntent().getStringExtra("action");

        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            initAutoCompletedTexts();
            if (action != null) {
                if (action.equals("create")) {
                    getSupportActionBar().setTitle(R.string.create);
                    outlet = new Outlet();
                    outlet.setShopkeeper(shopkeeper);
                    binding.setOutlet(outlet);
                } else {
                    getSupportActionBar().setTitle(R.string.update);
                    binding.saveBtn.setText("UPDATE");
                    initUpdateSpcific();

                }
            }
        }

        //createBinding.setCustomer(outlet);
        binding.saveBtn.setOnClickListener(view -> {
            save();
        });
    }

回答1:

There are few things to take note:

1) Initialisation works the following way:

Outlet outlet = new Outlet();

2) Assigning a value:

Outlet outlet = gson.fromJson(realmOutlet.getPayload(), Outlet.class);

In order to keep the databinded you MUST initialise and then update the data accordingly.

So you have to do both but in a structured way, i.e

Outlet outlet = new Outlet();
    Outlet outlet = gson.fromJson(realmOutlet.getPayload(), Outlet.class);      
    binding.setOutlet(outlet);

And the subsequent updates: assign the values and setOutlet

outlet = gson.fromJson(realmOutlet.getPayload(), Outlet.class);
    binding.setOutlet(outlet);

UPDATE:

This is just answer to you question and the followup of the chat we had in comments so that other can take reference from.