前言
以前在學(xué)習(xí)Android的時(shí)候,第一次接觸設(shè)計(jì)模式,就是MVC。當(dāng)時(shí),寫(xiě)的代碼不多,聽(tīng)得懵懵懂懂。在后來(lái),深切感受到是在第一次組隊(duì)寫(xiě)項(xiàng)目的時(shí)候,由于時(shí)間緊迫,前期我們就做了分工,我當(dāng)時(shí)的任務(wù)就是搞定接口文檔,并封裝一個(gè)網(wǎng)絡(luò)請(qǐng)求類(lèi),別的組員就暫時(shí)通過(guò)假數(shù)據(jù)做著自己的模塊。
突然,我就感覺(jué)到其實(shí) APP 很簡(jiǎn)單,就是UI+數(shù)據(jù),沒(méi)有真正數(shù)據(jù)的 APP 是死的,一旦有了數(shù)據(jù)與之互動(dòng),那么它就變活了,當(dāng)然了,活得這么樣又是另外一碼事。那么,一手UI,一手?jǐn)?shù)據(jù),UI如何顯示,數(shù)據(jù)什么時(shí)候更新,什么時(shí)候更新UI等等中間的互動(dòng)就需要一個(gè)橋梁。
MVC
MVC, Model View Controller,這里的橋梁就是Controller,簡(jiǎn)單來(lái)說(shuō)就是通過(guò) Controller 的控制區(qū)操作 Model 層的數(shù)據(jù),然后返回給 View 層顯示。
- Model : javaBean 業(yè)務(wù)邏輯相關(guān)
- View : xml文件
- Controller : activity fragment
在這里看 View 層,它是 xml 文件,它僅僅是一些布局文件,控制能力基本沒(méi)有,如果想要去控制其中的控件隱藏/顯示,只能將代碼寫(xiě)到 activity 中,造成了 activity 既是 View 層也是 Controller 層的窘境。也就造成了 android中大部分app用的是view-model 。
Most of the modern Android applications just use View-Model architecture,everything is connected with Activity.
這就導(dǎo)致了,如果是一個(gè)邏輯很復(fù)雜的頁(yè)面,activity/fragment的代碼將相當(dāng)?shù)凝嫶笥纺[,維護(hù)起來(lái)也不方便。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
initRetrofit();
initView();
initEvent();
initData();
}
private void initData() {
getDatas(REFRESH_DATA);
}
private void initRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
mGankioService = retrofit.create(GankioService.class);
}
private void initEvent() {
mSr.setOnRefreshListener(mOnRefreshListener);
}
private void initView() {
mSr = (SwipeRecyclerView) findViewById(R.id.activity_swipe_recycler);
/**設(shè)置布局管理器*/
mLayoutManager = new GridLayoutManager(this, 3);
mSr.setLayoutManager(mLayoutManager);
/**設(shè)置adapter*/
mMyadapter = new MyAdapter();
mSr.setAdapter(mMyadapter);
}
class MyAdapter extends SwipeRecyclerView.Adapter<MyAdapter.MyViewHolder> {
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder vh = new MyViewHolder(LayoutInflater.from(HomeActivity.this).inflate(R.layout.activity_recycler_item, parent, false));
return vh;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
final String url = mDatas.get(position).url;
Glide.with(holder.mImageView.getContext())
.load(url)
.centerCrop()
.placeholder(R.color.app_primary_dark)
.crossFade()
.into(holder.mImageView);
holder.mImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(HomeActivity.this, BitmapActivity.class);
intent.putExtra("url", url);
startActivity(intent);
}
});
}
@Override
public int getItemCount() {
if (mDatas == null)
return 0;
return mDatas.size();
}
@Override
protected void loadMoreDatas(RecyclerView recyclerView, int dx, int dy) {
int position = mLayoutManager.findLastVisibleItemPosition();
int itemCount = mMyadapter.getItemCount();
if (position + 1 == itemCount) {
getDatas(LOADMORE_DATA);
}
}
public class MyViewHolder extends RecyclerView.ViewHolder {
ImageView mImageView;
public MyViewHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.item_imageview);
}
}
}
private void getDatas(final int action) {
if (action == REFRESH_DATA) {
page = 1;
} else {
page++;
}
mGankioService.getWelfare(50, page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BaseModel<List<Welfare>>>() {
@Override
public void onStart() {
mSr.mSwipeRefreshLayout.setRefreshing(true);
}
@Override
public void onCompleted() {
mSr.mSwipeRefreshLayout.setRefreshing(false);
}
@Override
public void onError(Throwable e) {
Log.i(">>>", "onError " + e.toString());
}
@Override
public void onNext(BaseModel<List<Welfare>> listBaseModel) {
if (action == REFRESH_DATA) {
mDatas.clear();
}
mDatas.addAll(listBaseModel.results);
mMyadapter.notifyDataSetChanged();
}
});
}
private SwipeRecyclerView.OnRefreshListener mOnRefreshListener = new SwipeRecyclerView.OnRefreshListener() {
@Override
public void onRefresh() {
getDatas(REFRESH_DATA);
}
};
這是我之前用 mvc 寫(xiě)的gank.io其中一個(gè)顯示妹子圖的 activity。可以看到通過(guò) retrofit 獲取數(shù)據(jù),并顯示數(shù)據(jù)。
MVP
上文已經(jīng)提到了 MVC 的缺陷,接著便演化出來(lái)了 MVP 。
- Model : javaBean 業(yè)務(wù)邏輯相關(guān)
- View : activity fragemnt
- presenter : 負(fù)責(zé)完成View于Model間的交互
從圖中可以看出, View 層和 Model 層已經(jīng)完全的解耦了,以前的 Controller 換成了 Presenter 作為它們之間的橋梁,用于操作 View 層發(fā)出的事件傳遞到 Presenter 中,Presenter 層再去操作 Model 層,獲取數(shù)據(jù)并返回給 View 層。
以下就將上文的代碼利用 MVP 實(shí)現(xiàn)。
-
Model
在以上代碼中,對(duì)數(shù)據(jù)進(jìn)行的操作有,最開(kāi)始的初始化數(shù)據(jù),刷新數(shù)據(jù),加載更多數(shù)據(jù)。對(duì)此創(chuàng)建一個(gè)
HomeModel
public interface HomeModel { void initializeDatas(); void refreshDatas(); void loadMoreDatas(); }
實(shí)現(xiàn)之:
public class HomeModelmp implements HomeModel { public static final int REFRESH_DATA = 1; public static final int LOADMORE_DATA = 2; private HomeOnListener mListener; private int page; public HomeModelmp(HomeOnListener l) { this.mListener = l; } @Override public void initializeDatas() { getDatas(REFRESH_DATA); } @Override public void refreshDatas() { getDatas(REFRESH_DATA); } @Override public void loadMoreDatas() { getDatas(LOADMORE_DATA); } private void getDatas(final int action) { if (action == REFRESH_DATA) { page = 1; } else { page++; } ServiceManager.getInstance().mService.getWelfare(20, page) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<BaseModel<List<Welfare>>>() { @Override public void onStart() { mListener.onStart(); } @Override public void onCompleted() { mListener.onCompleted(); } @Override public void onError(Throwable e) { mListener.onError(e); } @Override public void onNext(BaseModel<List<Welfare>> listBaseModel) { mListener.onNext(action, listBaseModel); } }); } public interface HomeOnListener { void onStart(); void onCompleted(); void onError(Throwable e); void onNext(int action, BaseModel<List<Welfare>> listBaseModel); } }
-
View
View 層就是 activity ,其中將只有純粹的UI操作。同樣創(chuàng)建一個(gè)接口:
public interface HomeView { /** * 加載中 */ void onLoading(); /** * 加載完成 */ void onLoadCompleted(); /** * 加載布局 */ View inflateLayout(ViewGroup parent, boolean b); /** * 跳轉(zhuǎn) */ void skipActivity(String url); /** * 獲取最后一個(gè)可見(jiàn)item位置 */ int findLastPosition(); }
將activity實(shí)現(xiàn)該接口:
public class HomeActivity extends AppCompatActivity implements HomeView { private SwipeRecyclerView mSr; private HomePresenter mHomePresenter; private GridLayoutManager mLayoutManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); if (mHomePresenter == null) { mHomePresenter = new HomePresenter(this); } initView(); initEvent(); initData(); } private void initData() { mHomePresenter.initialize(); } private void initEvent() { mSr.setOnRefreshListener(mOnRefreshListener); } private void initView() { mSr = (SwipeRecyclerView) findViewById(R.id.activity_swipe_recycler); /**設(shè)置布局管理器*/ mLayoutManager = new GridLayoutManager(this, 3); mSr.setLayoutManager(mLayoutManager); /**設(shè)置adapter*/ mHomePresenter.setAdapter(mSr); } @Override public void onLoading() { mSr.mSwipeRefreshLayout.setRefreshing(true); } @Override public void onLoadCompleted() { mSr.mSwipeRefreshLayout.setRefreshing(false); } @Override public View inflateLayout(ViewGroup parent, boolean b) { return LayoutInflater.from(HomeActivity.this).inflate(R.layout.activity_recycler_item, parent, false); } @Override public void skipActivity(String url) { Intent intent = new Intent(HomeActivity.this, BitmapActivity.class); intent.putExtra("url", url); startActivity(intent); } @Override public int findLastPosition() { return mLayoutManager.findLastVisibleItemPosition(); } private SwipeRecyclerView.OnRefreshListener mOnRefreshListener = new SwipeRecyclerView.OnRefreshListener() { @Override public void onRefresh() { mHomePresenter.onRefreshData(); } };
-
Presenter
Presenter作為 View 和 Model 之間交互的橋梁,也就是在合適的地方調(diào)用各自的方法。
public class HomePresenter implements HomeModelmp.HomeOnListener { private HomeView mHomeView; private List<Welfare> mDatas = new ArrayList<>(); private HomeActivity view; private HomeModel mHomeModel; private HomeAdapter mAdapter; public HomePresenter(HomeView homeView) { this.mHomeView = homeView; this.mHomeModel = new HomeModelmp(this); } public void initialize(){ mDatas.clear(); mHomeModel.initializeDatas(); } public void onRefreshData() { mDatas.clear(); mHomeModel.refreshDatas(); } @Override public void onStart() { mHomeView.onLoading(); } @Override public void onCompleted() { mHomeView.onLoadCompleted(); } @Override public void onError(Throwable e) { } @Override public void onNext(int action, BaseModel<List<Welfare>> listBaseModel) { if (action == HomeModelmp.REFRESH_DATA) { mDatas.clear(); } mDatas.addAll(listBaseModel.results); mAdapter.notifyDataSetChanged(); } public void setAdapter(SwipeRecyclerView sr) { mAdapter = new HomeAdapter(); sr.setAdapter(mAdapter); } public class HomeAdapter extends SwipeRecyclerView.Adapter<HomeAdapter.MyViewHolder> { @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyViewHolder vh = new MyViewHolder(mHomeView.inflateLayout(parent, false)); return vh; } @Override public void onBindViewHolder(MyViewHolder holder, int position) { final String url = mDatas.get(position).url; Glide.with(holder.mImageView.getContext()) .load(url) .centerCrop() .placeholder(R.color.app_primary_dark) .crossFade() .into(holder.mImageView); holder.mImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mHomeView.skipActivity(url); } }); } @Override public int getItemCount() { if (mDatas == null) return 0; return mDatas.size(); } @Override protected void loadMoreDatas(RecyclerView recyclerView, int dx, int dy) { int position = mHomeView.findLastPosition(); int itemCount = getItemCount(); if (position + 1 == itemCount) { mHomeModel.loadMoreDatas(); } } public class MyViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public MyViewHolder(View itemView) { super(itemView); mImageView = (ImageView) itemView.findViewById(R.id.item_imageview); } } } }
Ok,大功告成。這里僅是貼出了關(guān)鍵代碼,github地址:Gnak.io:https://github.com/cgzysan/Gank.io</br>
參考資料: