Android MVVM框架学习总结(四)
2018-03-31 18:04
288 查看
实例分析
1.Activity/Fragment databing
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> <import type="android.view.View" /> <variable name="view" type="com.grandstream.gsmarket.apps.ApkListFragment" /> <variable name="viewmodel" type="com.grandstream.gsmarket.apps.AppsViewModel" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="30dp" android:layout_marginRight="116dp" android:layout_marginTop="2dp" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/search_box" android:layout_width="0dp" android:layout_height="104dp" android:layout_weight="1" android:background="@drawable/searchbox_bg" android:focusable="true" android:focusableInTouchMode="true" android:hint="@string/search_hinit" android:paddingLeft="30dp" android:singleLine="true" android:text="@={viewmodel.searchKeyWord}" android:textColor="#ffffff" android:textColorHint="#6a748f" android:textSize="40px" /> <ImageView android:id="@+id/search_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right|center_vertical" android:background="@drawable/search_btn" android:onClick="@{() -> viewmodel.searchApp()}" android:focusable="true" /> </LinearLayout> <!--android:onClick="@{() -> viewmodel.searchApp()}"--> <com.grandstream.gsmarket.widget.MarketGridView android:id="@+id/gridview" app:appitems="@{viewmodel.items}" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="52dp" android:clickable="false" android:fadingEdge="none" android:horizontalSpacing="260dp" android:listSelector="@drawable/list_item_background" android:numColumns="2" android:verticalSpacing="12dp" /> <TextView android:id="@+id/empty" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/empty_message_top_margin" android:gravity="center_horizontal" android:text="@string/no_application" android:textColor="#ebebeb" android:textSize="36sp" android:visibility="@{viewmodel.empty ? View.VISIBLE : View.GONE}" /> </LinearLayout> </layout>
在xml文件中有一个自定义属性
app:appitems="@{viewmodel.items}"
该自定义属性需要一个BinderAdapter注解
/** * Contains {@link BindingAdapter}s for the {@link com.grandstream.gsmarket.data.entity.AppSoftwareInfo} list. */ public class AppsListBindings { /*使用Android databinding的时候使用了@BindingAdater自定义属性之后一直有这个application namespace for attribute {} will be ignored问题,虽然不报错,但是总觉得不爽将 @BindingAdapter("app:appitems") 改成@BindingAdapter({"appitems"})就ok了。*/ @BindingAdapter("appitems") public static void setItems(GridView gridView, List<AppSoftwareInfo> items) { AppListAdapter adapter = (AppListAdapter) gridView.getAdapter(); if (adapter != null) { adapter.notifyDataSetChanged(items); } } }
Fragment这样写
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mAppsFragBinding = AppsFragBinding.inflate(inflater, container, false); mAppsFragBinding.setView(this); setupListAdapter(getActivity()); showOrHideProgress(); mAppsFragBinding.setViewmodel(mAppsViewModel); mAppsFragBinding.gridview.setAdapter(mApplistAdapter); return mAppsFragBinding.getRoot(); }
setupListAdapter(getActivity())函数代码
private void setupListAdapter(Context context) { GridView gridView = mAppsFragBinding.gridview; mApplistAdapter = new AppListAdapter( (HomeActivity) getActivity(), Injection.provideAppsRepository(context), this); gridView.setAdapter(mApplistAdapter); }
ViewModel这样写
public class AppsViewModel extends BaseObservable { private static final String TAG = AppsViewModel.class.getSimpleName(); // These observable fields will update Views automatically public final ObservableList<AppSoftwareInfo> items = new ObservableArrayList<>(); public final ObservableField<String> searchKeyWord = new ObservableField<>(); public final ObservableBoolean dataLoading = new ObservableBoolean(false); private final Context mContext; private final AppsRepository mAppsRepository; private final ObservableBoolean mIsDataLoadingError = new ObservableBoolean(false); private AppsFilterType mCurrentFiltering = AppsFilterType.HOT_APPS; public AppsViewModel(Context context, AppsRepository appsRepository) { mContext = context; mAppsRepository = appsRepository; // Set initial state setFiltering(AppsFilterType.HOT_APPS); } public void start() { loadApps(false, mCurrentFiltering.getName()); } public void loadApps(boolean forceUpdate, String category) { loadApps(forceUpdate, true, category); } /** * @param forceUpdate Pass in true to refresh the data in the {@link com.grandstream.gsmarket.data.source.AppsSoftDataSource} * @param showLoadingUI Pass in true to display a loading icon in the UI */ private void loadApps(boolean forceUpdate, final boolean showLoadingUI, final String category) { if (showLoadingUI) { dataLoading.set(true); } if (forceUpdate) { mAppsRepository.refreshAppsInfo(); } // The network request might be handled in a different thread so make sure Espresso knows // that the app is busy until the response is handled. //EspressoIdlingResource.increment(); // App is busy until further notice mAppsRepository.getAppsInfo(category, new AppsSoftDataSource.LoadAppsInfoCallback() { @Override public void onAppsInfoLoaded(String category, List<AppSoftwareInfo> appList) { List<AppSoftwareInfo> appsToShow = new ArrayList<>(); // We filter the tasks based on the requestType appsToShow.addAll(appList); if (showLoadingUI) { dataLoading.set(false); } mIsDataLoadingError.set(false); items.clear(); items.addAll(appsToShow); //notifyPropertyChanged(BR.empty); // It's a @Bindable so update manually } @Override public void onDataNotAvailable() { mIsDataLoadingError.set(true); } }); } @Bindable public boolean isEmpty() { return items.isEmpty(); } public void searchApp() { Logger.d(TAG, "begin searchApp"); } public void setAppItems(List<AppSoftwareInfo> appItems) { items.clear(); items.addAll(appItems); Logger.d(TAG, "item size is " + items.size()); } /** * Sets the current app filtering type. * * @param requestType Can be {@link AppsFilterType#HOT_APPS}, * {@link AppsFilterType#ALL_APPS}, or * {@link AppsFilterType#GAME_APPS},or * {@link AppsFilterType#} */ public void setFiltering(AppsFilterType requestType) { mCurrentFiltering = requestType; // Depending on the filter type, set the filtering label, icon drawables, etc. switch (requestType) { case HOT_APPS: /*currentFilteringLabel.set(mContext.getString(R.string.label_all)); noTasksLabel.set(mContext.getResources().getString(R.string.no_tasks_all)); noTaskIconRes.set(mContext.getResources().getDrawable( R.drawable.ic_assignment_turned_in_24dp)); tasksAddViewVisible.set(true);*/ break; case ALL_APPS: break; case GAME_APPS: break; case TOOL_APPS: break; } } }
ViewModel初始化
为了优化性能,ViewModel会通过FragmentManager来管理,与Acitivity的生命周期绑定
private AppsViewModel findOrCreateViewModel() { // In a configuration change we might have a ViewModel present. It's retained using the // Fragment Manager. @SuppressWarnings("unchecked") ViewModelHolder<AppsViewModel> retainedViewModel = (ViewModelHolder<AppsViewModel>) getSupportFragmentManager() .findFragmentByTag(TASKS_VIEWMODEL_TAG); if (retainedViewModel != null && retainedViewModel.getViewmodel() != null) { // If the model was retained, return it. return retainedViewModel.getViewmodel(); } else { // There is no ViewModel yet, create it. Context context = getApplicationContext(); AppsViewModel viewModel = new AppsViewModel(context, Injection.provideAppsRepository(context)); // and bind it to this Activity's lifecycle using the Fragment Manager. ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), ViewModelHolder.createContainer(viewModel), TASKS_VIEWMODEL_TAG); return viewModel; } }
2. ListView/GridView Item的databinding
xml文件这样写,文件名app_item.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="viewmodel" type="com.grandstream.gsmarket.apps.AppItemViewModel" /> </data> <LinearLayout android:layout_width="532dp" android:layout_height="238dp" android:orientation="vertical" android:paddingLeft="42dp" android:paddingRight="38dp" android:paddingTop="24dp" android:onClick="@{() -> viewmodel.appItemClicked()}"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <RelativeLayout android:layout_width="118dp" android:layout_height="118dp"> <ImageView android:id="@+id/icon" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:scaleType="fitCenter" android:src="@{viewmodel.iconDrawable}" /> <com.grandstream.gsmarket.widget.FrameImageView android:id="@+id/operator_icon" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:src="@drawable/download_anim" android:visibility="gone" /> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="42dp" android:orientation="vertical"> <TextView android:id="@+id/appname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewmodel.appName}" android:singleLine="true" android:textColor="#ebebeb" android:textSize="46px" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:gravity="center_vertical" android:orientation="horizontal"> <TextView android:id="@+id/charge" android:layout_width="120dp" android:layout_height="wrap_content" android:text="@{viewmodel.charge}" android:singleLine="true" android:textColor="#ebebeb" android:textSize="30px" /> <TextView android:id="@+id/size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewmodel.formatSize}" android:layout_marginLeft="34dp" android:singleLine="true" android:textColor="#999999" android:textSize="30px" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:gravity="center_vertical" android:orientation="horizontal"> <RatingBar android:id="@+id/grade" android:layout_width="wrap_content" android:layout_height="wrap_content" android:isIndicator="true" android:maxHeight="20dp" android:minHeight="20dp" android:numStars="5" android:progressDrawable="@drawable/food_rating_bar_full" android:rating="2.5" /> <TextView android:id="@+id/downloadcount" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewmodel.downloadCount}" android:layout_marginLeft="34dp" android:singleLine="true" android:textColor="#999999" android:textSize="30px" /> </LinearLayout> </LinearLayout> </LinearLayout> <LinearLayout android:id="@+id/press_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:gravity="center_vertical|left" android:orientation="horizontal" android:visibility="gone"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="top" android:singleLine="true" android:text="@string/press" android:textColor="#dce3ed" android:textSize="36px" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="18dp" android:src="@drawable/gs_icon_blue" /> <TextView android:id="@+id/downoropen" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewmodel.actionString}" android:layout_gravity="center_vertical" android:layout_marginLeft="18sp" android:gravity="top" android:singleLine="true" android:textColor="#dce3ed" android:textSize="36px" /> </LinearLayout> </LinearLayout> </layout>
Adpater中的getView这样写
@Override public View getView(int position, View view, ViewGroup viewGroup) { AppSoftwareInfo info = getItem(position); AppItemBinding binding; if (view == null) { // Inflate LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); // Create the binding binding = AppItemBinding.inflate(inflater, viewGroup, false); } else { // Recycling view binding = DataBindingUtil.getBinding(view); } FrameImageView operatorIcon = binding.operatorIcon; AnimationDrawable animationDrawable = (AnimationDrawable) operatorIcon.getDrawable(); operatorIcon.setVisibility(View.GONE); final AppItemViewModel viewmodel = new AppItemViewModel( viewGroup.getContext().getApplicationContext()); viewmodel.setAppItemNavigator(mAppItemNavigator); binding.setViewmodel(viewmodel); viewmodel.setAppSoftInfo(info); return binding.getRoot();
2. 参考链接
Google应用架构https://developer.android.google.cn/topic/libraries/architecture/guide.html
Android 架构示例代码
https://github.com/googlesamples/android-architecture
相关文章推荐
- Android MVVM框架学习总结(二)
- android MVVM框架学习总结(一)
- android MVVM框架学习总结(三)
- android快速开发框架 可以一个个学习总结
- Android 框架 MVC、MVP、MVVM学习笔记
- Android系统框架学习总结
- 【项目架构】Android MVP 和MVVM框架模式 学习实例Demo之mvp篇
- android(安卓)开源框架学习总结
- Android二维码ZXing开源框架的学习总结
- Android网络框架volley学习(十一)volley源码解析总结
- nio socket 及其开源框架MINA学习总结(一)
- Android画图学习总结(一)——类的简介
- Android画图学习总结(二)——Bitmap
- Android画图学习总结(四)——Animation(中)
- Android画图学习总结(二)——Bitmap
- Android画图学习总结(四)——Animation(下)
- Android画图学习总结(四)——Animation(下)
- Android画图学习总结(一)——类的简介
- Android学习基础要点总结
- nio socket 及其开源框架MINA学习总结(一)