Android 数据绑定适配器空指针异常

Android Databinding Adapter Null Pointer Exception

我正在尝试在我的 Android 应用程序中使用 Android 数据绑定。如果我在没有任何数据的情况下初始化对象,我有一个对象 Outlet。一切正常。所有数据都收集得很好。但是,如果我用数据初始化 Object 并将其传递给布局,它会使应用程序崩溃并出现 Invoking Interface method on Null Views 的抱怨。下面是我的布局文件:

    <?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>

我的 Observable 对象是这个

    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");
                        }
                    }
                });
            }
        }
    }

当我这样做的时候。 Outlet outlet = new Outlet(); 并像这样将其分配给我的绑定数据 binding.setOutlet(outlet); 我不会收到任何错误。但是,如果我这样做:

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

我得到一个 NPE Error 并且基于 NPE,这不是因为数据不存在。就好像 adapters 还没有初始化一样。我知道它一直指向 addressField 因为它是第一项。这是地址的 adapter。这是一个摘要 class。所以,我很困惑为什么他们不初始化:

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

下面是我的 Log 我无法跟踪日志以了解究竟要调试什么。但是,要说附加到 Interface method 的视图是空的。就像 Binding Layouts 还没有初始化。那么,为什么当我用空数据初始化它时它可以工作?

还有我的onCreate代码

     @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) 初始化工作方式如下:

Outlet outlet = new Outlet();

2) 赋值:

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

为了保持数据绑定,您必须初始化然后相应地更新数据。

因此您必须以结构化的方式同时执行这两项操作,即

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

以及后续更新:分配值和 setOutlet

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

更新

这只是对你问题的回答和我们在评论中的聊天的后续,以便其他人参考。