MVC、MVP及MVVM代码组织方式比较
MVC代码组织方式
V层
代码名称 applist.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<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:paddingLeft="30dp"
android:singleLine="true"
android:textColor="#ffffff"
android:textColorHint="#6a748f"
android:hint="@string/search_hinit"
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:focusable="true" />
</LinearLayout>
<com.base.module.pack.ui.MarketGridView
android:id="@+id/gridview"
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="gone"
/>
</LinearLayout>
</LinearLayout>
C层
// onCreate
....
mApplistAdapter = new ApplistAdapter(getActivity());
initApplistData(mWebUrl);
....
mView = inflater.inflate(R.layout.applist, null);
mSearchInputView = (EditText) mView.findViewById(R.id.search_box);
mSearchInputView.setNextFocusLeftId(mLeftFocusId);
mSearchBtn = (ImageView) mView.findViewById(R.id.search_btn);
mSearchBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
initApplistData(getUrl(mSearchInputView.getText().toString().trim()));
}
});
mGridView = (GridView) mView.findViewById(R.id.gridview);
mEmptyView =(TextView) mView.findViewById(R.id.empty);
mGridView.setOnItemClickListener(this);
mGridView.setOnItemSelectedListener(this);
mGridView.setAdapter(mApplistAdapter);
mGridView.setOnFocusChangeListener(this);
mGridView.setNextFocusDownId(mSearchInputView.getId());
mGridView.setNextFocusLeftId(mLeftFocusId);
return mView;
private void initApplistData(final String url){
List<Biz_appSoftwareInfo> cacheResult = AppListCache.getAppList(mApptypecode);
if (cacheResult.size() > 0){
Message msg = new Message();
msg.what = LOAD_APPLIST_FINISH;
msg.arg1 = 1;
msg.obj = cacheResult;
mHandler.sendMessage(msg);
} else {
showProgressDialog(R.string.prompt,R.string.loading);
}
ThreadManager.execute(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.what = LOAD_APPLIST_FINISH;
msg.arg1 = 1;
List<Biz_appSoftwareInfo> result = null;
try {
Log.i(url);
result = JsonHelpUtil.JsonToJavaObj(new URL(url), Biz_appSoftwareInfo.class);
AppListCache.cache(mApptypecode, result);
if(TextUtils.isEmpty(GOOGLE_PLAY_DOWNLODURL)){
if(result != null){
for(Biz_appSoftwareInfo bz:result){
List<Biz_appVersionInfo> versionInfo = bz.getBiz_appVersionInfo();
Collections.sort(versionInfo);
if(versionInfo != null && versionInfo.size()>0){
String packageName = versionInfo.get(0).getPackagename();
if(GOOGLE_PLAY.equals(packageName)){
GOOGLE_PLAY_DOWNLODURL = versionInfo.get(0).getDownloadurl();
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
Log.e(" get applist fail");
msg.arg1 = 0;
}finally{
msg.obj = result;
mHandler.sendMessage(msg);
}
}
});
}
M层
Biz_appVersionInfo 、Biz_appVersionInfo等java bean、相关数据集合、json到java bean的
转换类,工具类。
MVP代码组织方式
V层,类似于MVC
M层,类似于MVC
P层
通过一系列借口与V层及Model层的交互,P层同时持有V及M层的引用
以加载通话记录列表代码为例
- 定义接口契约类
public class CallsContract {
public interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showCalls(Cursor calls);
void showEmpty();
void onDataNotAvaiable();
}
public interface Presenter extends BasePresenter {
CallsFilterType getFiltering();
void setFiltering(CallsFilter requestType);
void loadCalls();
boolean deleteCall(int isConf, String callId, String confName);
boolean deleteCalls(List<Long> ids, List<String> numbers);
String getCallerName(String id, int isConf, TextView nameView);
void deleteConf(String id);
void deleteCall(String number);
}
}
其中,BaseView 及BasePresenter代码如下:
//BaseView
public interface BaseView<T> {
void setPresenter(T presenter);
}
//BasePresenter
public interface BasePresenter {
void start();
void stop();
}
- View层实现BaseView接口
public class AllFragment extends BaseFragment implements View.OnClickListener,
OnItemClickListener, CallsContract.View, ConfsListAdapter.GetUIDataInterface {
实现数据回调接口,刷新UI。
- 定义Presenter的实现类CallsPresenter
public class CallsPresenter implements CallsContract.Presenter,
CallsDataSource.LoadCallsCallback,
LoaderManager.LoaderCallbacks<Cursor> {
该类的作用主要是与Model层交互获取数据,与View层交互回调数据。
- V层、P层及Model层交互
//1. 创建Presenter
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Logger.d("[AllFragment.onCreate]");
mainHandler = new MainHandler();
//创建Presenter
setPresenter(getCallsType());
}
protected CallsFilterType getCallsType() {
return CallsFilterType.ALL_CALLLOG;
}
protected void setPresenter(CallsFilterType callsType) {
LoaderProvider loaderProvider = new LoaderProvider(mContext);
CallsFilter callsFilter = CallsFilter.from(callsType);
LoaderManager loaderManager = getLoaderManager();
LocalCallsDataSource dataSource = LocalCallsDataSource.getInstance(mContentResolver);
CallsContract.Presenter presenter = new CallsPresenter(loaderProvider, loaderManager,
dataSource, this, callsFilter);
setPresenter(presenter);
}
//2. 启动数据加载,在Presenter中通过Loader加载
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mPresenter.start();
Logger.d("[AllFragment.onActivityCreated]");
}
//3. 加载数据完成,在Presenter中通过View接口的引用回调到View层刷新UI
@Override
public void setLoadingIndicator(boolean active) {
if (active) {
boolean isShowing = isVisible();
if (isShowing) {
showLoadingProgressBar();
}
} else {
hideLoadingProgressBar();
}
}
@Override
public void showEmpty() {
mEmptyView.setText(R.string.no_call);
}
@Override
public void showCalls(Cursor calls) {
mAdapter.onResume();
changeCheckFlag(false);
updateTitle(false);
mAdapter.changeCursor(calls);
if (calls != null && calls.getCount() > 0) {
mCallHistoryListView.requestFocus();
}
}
@Override
public void onDataNotAvaiable() {
Logger.e(TAG, "[onDataNotAvaiable],no data show!!");
}
相关代码,请参考GVC3210 CallHistory 模块。
View及Presenter层代码组织结构
Model层代码组织结构
注意: MVP引起的内存泄露,MVP有很多的优点,例如易于维护,易于测试,松耦合,复用性高,健壮稳定,易于扩展等。但是,由于Presenter经常性的需要执行一些耗时操作,那么当我们在操作未完成时候关闭了Activity,会导致Presenter一直持有Activity的对象,造成内存泄漏。
怎么样解决这个问题呢,我们只要在Activity或者Fragment关闭的时候将Presenter中的引用释放掉就可以了,但是如果有所的界面都去操作那样就变得很麻烦,所以我们在BaseActivity或BaseFragment中去操作,具体代码如下:
public abstract class BasePresenter<T> {
protected Reference<T> mViewRef;//View接口类型弱引用
public void attachView(T view) {
mViewRef = new WeakReference<T>(view); //建立关联
}
protected T getView() {
return mViewRef.get();//获取View
}
public boolean isViewAttached() {//判断是否与View建立了关联
return mViewRef != null && mViewRef.get() != null;
}
public void detachView() {//解除关联
if (mViewRef != null) {
mViewRef.clear();
mViewRef = null;
}
}
}
这里定义了四个方法,View通过泛型传递进来,Presenter对这个View持有弱引用。
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends LibBaseActivity {
protected T mPresenter;//Presenter对象
@SuppressWarnings("unchecked")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();//创建Presenter
mPresenter.attachView((V) this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
protected abstract T createPresenter();
}
BaseActivity含有两个泛型参数,第一个是View接口类型,第二个是Presenter的具体类型,在onCreate()中创建通过createPresenter创建一个具体的Presenter,在onDestroy()中释放Presenter中的引用。
MVVM代码组织方式
APK从网络加载APP信息为例
文件结构
├── ApkListFragment.java
├── AppItemNavigator.java
├── AppItemViewModel.java
├── AppListAdapter.java
├── AppsFilterType.java
├── AppsListBindings.java
└── AppsViewModel.java
1.Activity/Fragment databing
View层
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层
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;
}
}
Model层
类似与MVP
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();
AppItemViewModel这样写
public class AppViewModel extends BaseObservable {
private Context mContext;
// This navigator is s wrapped in a WeakReference to avoid leaks because it has references to an
// activity. There's no straightforward way to clear it for each item in a list adapter.
@Nullable
private WeakReference<AppItemNavigator> mNavigator;
public AppViewModel(final Context context){
mContext = context;
mAppSoftInfoObservable.addOnPropertyChangedCallback(new OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
AppSoftwareInfo appSoftInfo = mAppSoftInfoObservable.get();
if (appSoftInfo != null) {
appName.set(appSoftInfo.appname());
charge.set(appSoftInfo.ischarge() == 1 ? "收费":"免费");
size.set(appSoftInfo.appVersionInfo().get(0).size());
rate.set(appSoftInfo.gradenum());
downloadCount.set(appSoftInfo.downloadCount()+"");
packageName = appSoftInfo.appVersionInfo().get(0).packagename();
setupAppIcon(appSoftInfo.iconurl(), packageName);
} /*else {
title.set(mContext.getString(R.string.no_data));
description.set(mContext.getString(R.string.no_data_description));
}*/
}
});
}
public void setAppItemNavigator(AppItemNavigator itemNavigator){
mNavigator = new WeakReference<>(itemNavigator);
}
/**
* Called by the Data Binding library when the row is clicked.
*/
public void appItemClicked() {
AppSoftwareInfo appInfo = mAppSoftInfoObservable.get();
if (appInfo == null) {
// Click happened before task was loaded, no-op.
return;
}
if (mNavigator != null && mNavigator.get() != null) {
mNavigator.get().openAppDetails(appInfo);
}
}
private void setupAppIcon(String iconUrl, String packageName){
int iconId = AppIconHelper.getIconRes118(packageName);
Drawable defResource = mContext.getResources().getDrawable(iconId);
iconDrawable.set(defResource);
if(iconId == R.drawable.pic_default){
Glide.with(mContext).load(iconUrl)
.into(new SimpleTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource,
@Nullable Transition<? super Drawable> transition) {
iconDrawable.set(resource);
}
});
}
}
protected String packageName="";
public final ObservableField<Integer> appIcon = new ObservableField<>();
public final ObservableField<Drawable> iconDrawable = new ObservableField<>();
public final ObservableField<Integer> operatorIcon = new ObservableField<>();
public final ObservableField<String> appName = new ObservableField<>();
public final ObservableField<String> charge = new ObservableField<>();
public final ObservableField<String> size = new ObservableField<>();
public final ObservableField<Float> rate = new ObservableField<>();
public final ObservableField<String> downloadCount = new ObservableField<>();
public final ObservableField<String> action = new ObservableField<>();
public final ObservableField<AppSoftwareInfo> mAppSoftInfoObservable = new ObservableField<>();
public void setAppSoftInfo(AppSoftwareInfo appSoftInfo){
mAppSoftInfoObservable.set(appSoftInfo);
}
public String getFormatSize(){
return size.get()+" M";
}
public String getActionString(){
return Utils.isPackageExit(mContext, packageName)?
"打开":"下载";
}
}
这里通过对象的变化来更新绑定的基本类型字段,也可以让对象对象继承BaseObserver,然后在xml文件中绑定对象的属性或者get方法。
View及ViewModel层代码结构
Model层代码结构