正在使用数据绑定库更新 UI
Updating UI using data binding library
上下文:
我正在使用 new data binding library.
的 v1.0-rc1
我有以下视图模型:
public class DrawerPageHeaderViewModelImpl extends BaseObservable implements DrawerPageHeaderViewModel {
@Nullable
private Location currentLocation;
public DrawerPageHeaderViewModelImpl(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
}
@Bindable
@Nullable
@Override
public String getDistanceDisplayString() {
if (currentLocation == null) {
return null;
}
float[] results = new float[1];
Location.distanceBetween(landmark.getLatitude(), landmark.getLongitude(), currentLocation.getLatitude(), currentLocation.getLongitude(), results);
final float metersToTargetLocation = results[0];
final float feetToTargetLocation = DistanceUtil.convertMetersToFeet(metersToTargetLocation);
return DistanceUtil.convertFeetToFeetOrMilesString(feetToTargetLocation);
}
@Override
public void setCurrentLocation(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
notifyPropertyChanged(BR.distanceDisplayString);
}
}
此视图模型被传递给 Fragment
并存储在实例变量中。然后视图模型绑定到 Fragment 的 onCreateView
回调中的布局(这里 headerView
是一个空的 FrameLayout
):
@Nullable
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_drawer_page, container, false);
headerView = (ViewGroup) v.findViewById(R.id.headerView);
final ViewDrawerPageHeaderBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_drawer_page_header, headerView, true);
binding.setViewModel(viewModel);
return v;
}
定期调用 viewModel.setCurrentLocation
并传递用户的当前位置:
@Override
public void update(final Observable observable, Object data) {
new Handler(Looper.getMainLooper()).post(() -> {
if (isAdded()) {
viewModel.setCurrentLocation(locationController.getCachedUserLocation());
}
});
}
当前行为:
UI 在首次创建每个 Fragment
时正确显示距离 String
。 UI 每次重新创建 Fragment
时都会正确显示距离 String
(这些片段位于 ViewPager
.
当使用新位置调用 viewModel.setCurrentLocation
时,UI 不会更新。
期望的行为:
每次使用新位置调用 viewModel.setCurrentLocation
时,UI 都会更新。
到目前为止我at/thought看过的东西:
据我所知,让视图模型实现 Observable
(在这种情况下,通过扩展 BaseObservable
)应该在 [= 时自动进行 UI 更新29=] 被调用。至少,当我看到 the Android documentation for data binding 时,这就是我带走的信息。
BaseObservable
class 维护一个 OnPropertyChangedCallback
的私有列表。如果我在 BaseObservable.notifyPropertyChanged
方法上设置调试断点:
public void notifyPropertyChanged(int fieldId) {
if(this.mCallbacks != null) {
this.mCallbacks.notifyCallbacks(this, fieldId, (Object)null);
}
}
我看到 mCallbacks
在运行时总是 null
。因此,据推测,生成的数据绑定不会调用 BaseObservable.addOnPropertyChangedCallback
来提供自动连接组件的 OnPropertyChangedCallback
。这是否意味着我需要手动完成?这似乎打败了数据绑定库的很多要点。
您好,这对我有效。由于信用不足,我不得不 post 将其作为答案。
public class DataBindingTest extends Fragment {
private LinearLayout headerView;
public DataBindingTest() {
// Required empty public constructor
}
private static Handler mHandler;
DrawerPageHeaderViewModelImpl viewModel;
private Runnable runnable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_font_test, container, false);
headerView = (LinearLayout) rootView.findViewById(R.id.headerView);
viewModel = new DrawerPageHeaderViewModelImpl(null);
mHandler = new Handler();
RecyclerItemBinding bindingView = DataBindingUtil.inflate(inflater, R.layout.recycler_item, headerView, true);
runnable = new Runnable() {
@Override
public void run() {
changeLocation();
}
};
bindingView.setViewModel(viewModel);
return rootView;
}
@Override
public void onResume() {
super.onResume();
changeLocation();
}
@Override
public void onPause() {
super.onPause();
mHandler.removeCallbacks(runnable);
}
public void changeLocation() {
viewModel.setCurrentLocation(new Location("New"));
mHandler.postDelayed(runnable, 2000);
}
public class DrawerPageHeaderViewModelImpl extends BaseObservable {
@Nullable
private Location currentLocation;
public DrawerPageHeaderViewModelImpl(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
}
@Bindable
@Nullable
public String getDistanceDisplayString() {
if (currentLocation == null) {
return null;
}
return "Some String " + new Random().nextInt(100);
}
public void setCurrentLocation(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
notifyPropertyChanged(BR.distanceDisplayString);
}
}
}
和布局文件:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.androidbolts.databindingsample.DataBindingTest.DrawerPageHeaderViewModelImpl" />
</data>
<android.support.v7.widget.CardView
android:id="@+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="100dp"
android:layout_height="130dp"
android:layout_gravity="center"
card_view:cardCornerRadius="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{viewModel.distanceDisplayString}"
app:font="@{@string/kenyan}"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</layout>
我认为你应该在 XML:
中使用像 isAdded() 或 isLocationUpdated() 这样的标志
<TextView
android:id="@+id/location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{location.isUpdated? newLocation : oldLocation}" />
上下文:
我正在使用 new data binding library.
的 v1.0-rc1我有以下视图模型:
public class DrawerPageHeaderViewModelImpl extends BaseObservable implements DrawerPageHeaderViewModel {
@Nullable
private Location currentLocation;
public DrawerPageHeaderViewModelImpl(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
}
@Bindable
@Nullable
@Override
public String getDistanceDisplayString() {
if (currentLocation == null) {
return null;
}
float[] results = new float[1];
Location.distanceBetween(landmark.getLatitude(), landmark.getLongitude(), currentLocation.getLatitude(), currentLocation.getLongitude(), results);
final float metersToTargetLocation = results[0];
final float feetToTargetLocation = DistanceUtil.convertMetersToFeet(metersToTargetLocation);
return DistanceUtil.convertFeetToFeetOrMilesString(feetToTargetLocation);
}
@Override
public void setCurrentLocation(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
notifyPropertyChanged(BR.distanceDisplayString);
}
}
此视图模型被传递给 Fragment
并存储在实例变量中。然后视图模型绑定到 Fragment 的 onCreateView
回调中的布局(这里 headerView
是一个空的 FrameLayout
):
@Nullable
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_drawer_page, container, false);
headerView = (ViewGroup) v.findViewById(R.id.headerView);
final ViewDrawerPageHeaderBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_drawer_page_header, headerView, true);
binding.setViewModel(viewModel);
return v;
}
定期调用 viewModel.setCurrentLocation
并传递用户的当前位置:
@Override
public void update(final Observable observable, Object data) {
new Handler(Looper.getMainLooper()).post(() -> {
if (isAdded()) {
viewModel.setCurrentLocation(locationController.getCachedUserLocation());
}
});
}
当前行为:
UI 在首次创建每个 Fragment
时正确显示距离 String
。 UI 每次重新创建 Fragment
时都会正确显示距离 String
(这些片段位于 ViewPager
.
当使用新位置调用 viewModel.setCurrentLocation
时,UI 不会更新。
期望的行为:
每次使用新位置调用 viewModel.setCurrentLocation
时,UI 都会更新。
到目前为止我at/thought看过的东西:
据我所知,让视图模型实现 Observable
(在这种情况下,通过扩展 BaseObservable
)应该在 [= 时自动进行 UI 更新29=] 被调用。至少,当我看到 the Android documentation for data binding 时,这就是我带走的信息。
BaseObservable
class 维护一个 OnPropertyChangedCallback
的私有列表。如果我在 BaseObservable.notifyPropertyChanged
方法上设置调试断点:
public void notifyPropertyChanged(int fieldId) {
if(this.mCallbacks != null) {
this.mCallbacks.notifyCallbacks(this, fieldId, (Object)null);
}
}
我看到 mCallbacks
在运行时总是 null
。因此,据推测,生成的数据绑定不会调用 BaseObservable.addOnPropertyChangedCallback
来提供自动连接组件的 OnPropertyChangedCallback
。这是否意味着我需要手动完成?这似乎打败了数据绑定库的很多要点。
您好,这对我有效。由于信用不足,我不得不 post 将其作为答案。
public class DataBindingTest extends Fragment {
private LinearLayout headerView;
public DataBindingTest() {
// Required empty public constructor
}
private static Handler mHandler;
DrawerPageHeaderViewModelImpl viewModel;
private Runnable runnable;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_font_test, container, false);
headerView = (LinearLayout) rootView.findViewById(R.id.headerView);
viewModel = new DrawerPageHeaderViewModelImpl(null);
mHandler = new Handler();
RecyclerItemBinding bindingView = DataBindingUtil.inflate(inflater, R.layout.recycler_item, headerView, true);
runnable = new Runnable() {
@Override
public void run() {
changeLocation();
}
};
bindingView.setViewModel(viewModel);
return rootView;
}
@Override
public void onResume() {
super.onResume();
changeLocation();
}
@Override
public void onPause() {
super.onPause();
mHandler.removeCallbacks(runnable);
}
public void changeLocation() {
viewModel.setCurrentLocation(new Location("New"));
mHandler.postDelayed(runnable, 2000);
}
public class DrawerPageHeaderViewModelImpl extends BaseObservable {
@Nullable
private Location currentLocation;
public DrawerPageHeaderViewModelImpl(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
}
@Bindable
@Nullable
public String getDistanceDisplayString() {
if (currentLocation == null) {
return null;
}
return "Some String " + new Random().nextInt(100);
}
public void setCurrentLocation(@Nullable final Location currentLocation) {
this.currentLocation = currentLocation;
notifyPropertyChanged(BR.distanceDisplayString);
}
}
}
和布局文件:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.androidbolts.databindingsample.DataBindingTest.DrawerPageHeaderViewModelImpl" />
</data>
<android.support.v7.widget.CardView
android:id="@+id/card_view"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="100dp"
android:layout_height="130dp"
android:layout_gravity="center"
card_view:cardCornerRadius="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{viewModel.distanceDisplayString}"
app:font="@{@string/kenyan}"/>
</LinearLayout>
</android.support.v7.widget.CardView>
</layout>
我认为你应该在 XML:
中使用像 isAdded() 或 isLocationUpdated() 这样的标志 <TextView
android:id="@+id/location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{location.isUpdated? newLocation : oldLocation}" />