您的位置:首页 > 移动开发 > Android开发

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: