Android MVVM框架学习总结(四)

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层的引用

以加载通话记录列表代码为例

  1. 定义接口契约类
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();

}
  1. View层实现BaseView接口
public class AllFragment extends BaseFragment implements View.OnClickListener,
        OnItemClickListener, CallsContract.View, ConfsListAdapter.GetUIDataInterface {

实现数据回调接口,刷新UI。

  1. 定义Presenter的实现类CallsPresenter
public class CallsPresenter implements CallsContract.Presenter,
        CallsDataSource.LoadCallsCallback,
        LoaderManager.LoaderCallbacks<Cursor> {

该类的作用主要是与Model层交互获取数据,与View层交互回调数据。

  1. 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层代码结构
这里写图片描述

2. 参考链接

  1. Google应用架构
    https://developer.android.google.cn/topic/libraries/architecture/guide.html
  2. Android 架构示例代码
    https://github.com/googlesamples/android-architecture

猜你喜欢

转载自blog.csdn.net/u011897062/article/details/79771647