异常
在自定义View中使用databinding双向绑定,如果你的字段较多且在前台停留时间较长就会导致oom,错误信息如下
pthread_create (1040KB stack) failed: Try again
自定义view中使用双向绑定一般需要自定义一个BindingAdapter
@BindingAdapter("text")
public static void setText(FormEditText view, CharSequence text) {
view.binding.editText.setText(text);
}
// 添加InverseBindingAdapter用于从视图中获取文本值
@InverseBindingAdapter(attribute = "text")
public static String getText(FormEditText view) {
return view.binding.editText.getText().toString();
}
/**
* 添加InverseBindingListener用于在文本更改时通知数据绑定系统
*/
@BindingAdapter(value = {"textAttrChanged"})
public static void setListeners(FormEditText view, final InverseBindingListener textAttrChanged) {
if (textAttrChanged != null) {
// 在文本更改时通知数据绑定系统
view.binding.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) {
if (view.formTextWatcher != null) {
view.formTextWatcher.onTextChanged(charSequence, i, i1, i2);
}
}
@Override
public void afterTextChanged(Editable editable) {
textAttrChanged.onChange();
}
});
}
}
但是仅仅这样的话如果页面简单不会有问题,但是页面复杂就会导致oom,这是因为databinding在自定义View中一直监听字段变化导致内存溢出
这时我们需要databinding提供的一个类ObservableField
,我们使用它监听字段变化,这样可以使用观察者模式,减少无用的字段变化监听,也就减少内存消耗
ObservableField<String> textValue = new ObservableField<>()
示例代码
首先新建一个DataSource
public class FormDataSource implements BaseDataSource {
public final ObservableField<String> textValue = new ObservableField<>();
}
随便写个自定义View
public class FormEditText extends FrameLayout {
private String labelString;
private String hintString = "请输入";
private boolean required = false;
private boolean bottomBorder = true;
private int inputType = InputType.TYPE_CLASS_TEXT;
private int imeOptions = EditorInfo.IME_ACTION_NEXT;
public final FormDataSource formDataSource = new FormDataSource();
private int formatText = 0;
public FormTextWatcher formTextWatcher;
public FormEditTextBinding binding;
public FormEditText(Context context) {
super(context);
initAttr(null);
init();
}
public FormEditText(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttr(attrs);
init();
}
public FormEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttr(attrs);
init();
}
private void initAttr(AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.FormEditText);
labelString = typedArray.getString(R.styleable.FormEditText_label);
hintString = typedArray.getString(R.styleable.FormEditText_hint);
required = typedArray.getBoolean(R.styleable.FormEditText_required, false);
bottomBorder = typedArray.getBoolean(R.styleable.FormEditText_bottomBorder, true);
inputType = typedArray.getInt(R.styleable.FormEditText_formInputType, InputType.TYPE_CLASS_TEXT);
imeOptions = typedArray.getInt(R.styleable.FormEditText_formImeOptions, EditorInfo.IME_ACTION_NEXT);
formatText = typedArray.getInt(R.styleable.FormEditText_formatText, 0);
typedArray.recycle();
}
}
private void init() {
binding = FormEditTextBinding.inflate(LayoutInflater.from(getContext()),this, true);
binding.setLifecycleOwner((LifecycleOwner) getContext());
binding.setData(formDataSource);
binding.editText.setHint(hintString);
binding.tvRequired.setVisibility(required ? View.VISIBLE : View.GONE);
binding.tvLabel.setText(labelString);
binding.editText.setImeOptions(imeOptions);
binding.editText.setInputType(inputType);
// 添加底部边框
if (bottomBorder) {
binding.constraintLayout.setBackground(ContextCompat.getDrawable(getContext(), R.drawable.line_bottom));
}
}
/**
* 不要使用这个因为会导致databinding双向绑定无效
*/
public void addTextChangedListener(TextWatcher watcher) {
binding.editText.addTextChangedListener(watcher);
}
/**
* 推荐使用这个
*/
public void addTextChangedListener(FormTextWatcher formTextWatcher) {
this.formTextWatcher = formTextWatcher;
}
public boolean isRequired() {
return required;
}
public Editable getText() {
return binding.editText.getText();
}
public void setText(String text) {
formDataSource.textValue.set(text);
}
}
public interface FormTextWatcher {
public void onTextChanged(CharSequence s, int start, int before, int count);
}
xml布局
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="data"
type="你的datasource包名.FormDataSource" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="1"
android:textColor="@color/dark_color"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_required"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:lines="1"
android:text="*"
android:textColor="@color/theme_red"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/tv_label"
app:layout_constraintEnd_toStartOf="@+id/edit_text"
app:layout_constraintStart_toEndOf="@+id/tv_label"
app:layout_constraintTop_toTopOf="@+id/tv_label" />
<EditText
android:id="@+id/edit_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="12dp"
android:background="@null"
android:gravity="end|center_vertical"
android:maxLines="1"
android:text="@={data.textValue}"
android:textColor="@color/auto_color"
android:textColorHint="@color/hint_color"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@+id/tv_required" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
然后编写一个自定义BindingAdapter
···
@BindingAdapter(“text”)
public static void setText(FormEditText view, CharSequence text) {
view.formDataSource.textValue.set(text == null ? null : text.toString());
}
// 添加InverseBindingAdapter用于从视图中获取文本值
@InverseBindingAdapter(attribute = "text")
public static String getText(FormEditText view) {
return view.formDataSource.textValue.get();
}
/**
* 添加InverseBindingListener用于在文本更改时通知数据绑定系统
*/
@BindingAdapter(value = {"textAttrChanged"})
public static void setListeners(FormEditText view, final InverseBindingListener textAttrChanged) {
if (textAttrChanged != null) {
// 在文本更改时通知数据绑定系统
view.binding.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) {
if (view.formTextWatcher != null) {
view.formTextWatcher.onTextChanged(charSequence, i, i1, i2);
}
}
@Override
public void afterTextChanged(Editable editable) {
textAttrChanged.onChange();
}
});
}
}
··
使用自定义Viewdata.wspc
就是实体类的一个字段而已
<com.fox.lib_config.widgt.FormEditText
android:id="@+id/et_pounds_data_tare"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginStart="16dp"
android:paddingStart="0dp"
android:paddingEnd="16dp"
app:bottomBorder="true"
app:formImeOptions="actionNext"
app:formInputType="numberDecimal"
app:hint="请输入皮重"
app:label="皮重"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_pounds_data_gross_weight"
app:required="false"
app:text="@={data.wspc}" />