安卓开发中MVP模式的应用(附实例)
2017-01-07 12:33
411 查看
随着安卓技术的发展,安卓UI界面给用户体验越来越好,头牌里面的翻拍布局,饿了么里面的浏览团购的界面等等,越发高大上,这样UI的任务越来越重,所以为了让视图View与业务数据处理更加细化,让View专注于处理界面布局和人机交互,同时让model只关注与数据处理,MVP框架(Model—View—Presenter)应用而生。
一、MVP模式一般包括以下几个模块:
(1)、Model,负责数据的处理,一般写一个Interfce,降低耦合性,然后写一个实现该接口的类负责数据的处理,在这个类中写一个回调接口,将数据返回给实现该接口的类。
(2)、View,负责绘制UI,与用户进行交互(在Android中为Activity);
(3)、Presenter,负责逻辑处理,是View与Model之间的桥梁纽带;
一般情况下,还有一个Bean,封装获取到的信息。
通过上面这个逻辑图看出,View与Model不直接进行数据交互,而是通过桥梁Presenter进行数据通信,所以这样大大降低了代码的耦合性,各干各的,互不干涉;View与Presenter交互式通过接口实现的,所以利于测试,程序的可扩展性大大增强。
下面结合一个获取热购团购单的android案例,进行讲解。
(1)看看案例的src目录结构
可以发现Presenter(ITuanPresenter接口)与View(ITuanView接口)、Model(ITuanModel接口)都是通过接口进行交互的,这样大大降低了程序的耦合性,增强了扩展性
下面一个个来看
1、bean包中,是一个团购信息TuanItem,用来封装团购信息;
2、model包中,一个接口,一个实现该接口的类
接口代码
4、Presenter包,因为要加载网络图片,用了第三方框架Image-loader,要适配数据,写了一个继承BaseAdapter的TuanAdapter。
剩余两个一个处理数据的接口ITuanPresenter,一个是实现了该接口的TuanPresenter,代码如下
Image-Loader代码:
适配器代码:
实现上面接口处理逻辑的代码:
下面看看逻辑是如何处理的:
在View中的Activity中,进行用户交互,用户点击了loading按钮,执行onClick方法中的代码块
再回调方法onSuccess中,又调用了ITuanView接口的setTuanData()方法,实现该方法的类就是Activity,在该类中,进行了适配器设置,从而更新了Ui
现在大家有没有一种感觉,这个逻辑是从View走起,到Presenter,再到Model,返回来到Presenter,再到VIiew,是不是如下图指示
可以看到,View只负责Ui的更新和用户的交互,把逻辑处理扔给Presenter去做,而Presenter调用Model处理完数据后,在通过接口更新UI的信息。
哦,差点忘了,布局代码:
GirdView适配的布局代码:
项目下载地址:MVP模式demo
一、MVP模式一般包括以下几个模块:
(1)、Model,负责数据的处理,一般写一个Interfce,降低耦合性,然后写一个实现该接口的类负责数据的处理,在这个类中写一个回调接口,将数据返回给实现该接口的类。
(2)、View,负责绘制UI,与用户进行交互(在Android中为Activity);
(3)、Presenter,负责逻辑处理,是View与Model之间的桥梁纽带;
一般情况下,还有一个Bean,封装获取到的信息。
通过上面这个逻辑图看出,View与Model不直接进行数据交互,而是通过桥梁Presenter进行数据通信,所以这样大大降低了代码的耦合性,各干各的,互不干涉;View与Presenter交互式通过接口实现的,所以利于测试,程序的可扩展性大大增强。
下面结合一个获取热购团购单的android案例,进行讲解。
(1)看看案例的src目录结构
可以发现Presenter(ITuanPresenter接口)与View(ITuanView接口)、Model(ITuanModel接口)都是通过接口进行交互的,这样大大降低了程序的耦合性,增强了扩展性
下面一个个来看
1、bean包中,是一个团购信息TuanItem,用来封装团购信息;
package bean; public class TuanItem { private String row_num ,deal_id,sub_title,img_domain,img, origin_price,current_price,sale_count,supplier_id,supplier_name, shop_count,rate,rate_total_count; public String getRow_num() { return row_num; } public void setRow_num(String row_num) { this.row_num = row_num; } public String getDeal_id() { return deal_id; } public void setDeal_id(String deal_id) { this.deal_id = deal_id; } public String getSub_title() { return sub_title; } public void setSub_title(String sub_title) { this.sub_title = sub_title; } public String getImg_domain() { return img_domain; } public void setImg_domain(String img_domain) { this.img_domain = img_domain; } public String getImg() { return img; } public void setImg(String img) { this.img = img; } public String getOrigin_price() { return origin_price; } public void setOrigin_price(String origin_price) { this.origin_price = origin_price; } public String getCurrent_price() { return current_price; } public void setCurrent_price(String current_price) { this.current_price = current_price; } public String getSale_count() { return sale_count; } public void setSale_count(String sale_count) { this.sale_count = sale_count; } public String getSupplier_id() { return supplier_id; } public void setSupplier_id(String supplier_id) { this.supplier_id = supplier_id; } public String getSupplier_name() { return supplier_name; } public void setSupplier_name(String supplier_name) { this.supplier_name = supplier_name; } public String getShop_count() { return shop_count; } public void setShop_count(String shop_count) { this.shop_count = shop_count; } public String getRate() { return rate; } public void setRate(String rate) { this.rate = rate; } public String getRate_total_count() { return rate_total_count; } public void setRate_total_count(String rate_total_count) { this.rate_total_count = rate_total_count; } @Override public String toString() { return "RichGroupItem [row_num=" + row_num + ", deal_id=" + deal_id + ", sub_title=" + sub_title + ", img_domain=" + img_domain + ", img=" + img + ", origin_price=" + origin_price + ", current_price=" + current_price + ", sale_count=" + sale_count + ", supplier_id=" + supplier_id + ", supplier_name=" + supplier_name + ", shop_count=" + shop_count + ", rate=" + rate + ", rate_total_count=" + rate_total_count + "]"; } }
2、model包中,一个接口,一个实现该接口的类
接口代码
package model; public interface ITuanModel { void getTuanData(); }实现该接口类的代码,该类中,写了一个回调接口onTuanListener,用来把获取到的团购信息回调给实现该接口的类
package model; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import bean.TuanItem; import com.android.volley.Response.Listener; import com.android.volley.toolbox.JsonObjectRequest; import com.android.volley.toolbox.Volley; public class TuanModel implements ITuanModel{ private Context context; private String urlString="http://api.xiaogan88.com/deal/list"; private String countString;//条数 private JSONArray jsonArray; private OnTuanListener listener; public TuanModel(Context context) { super(); this.context=context; } @Override public void getTuanData() { getTuanJSON(); } private void getTuanJSON(){ Volley.newRequestQueue(context).add(new JsonObjectRequest(urlString, null, new Listener<JSONObject>() { @Override public void onResponse(JSONObject jsonObject) { try { countString=jsonObject.getString("total_count"); jsonArray=jsonObject.getJSONArray("data"); System.out.println("json_total_count>>>>>>>>>>>>>>"+countString); System.out.println("json_data>>>>>>>>>>>>>>"+jsonArray.get(0).toString()); listener.onSuccess(getTuanList(jsonArray,Integer.parseInt(countString))); } catch (JSONException e) { listener.onError(); e.printStackTrace(); } } },null)); } private List<TuanItem> getTuanList(JSONArray jsonArray,int count){ List<TuanItem> TuanList=new ArrayList<TuanItem>();//RichGroupItem集合 for(int i=0;i<count;i++){ try { JSONObject jsonObject=(JSONObject) jsonArray.opt(i); TuanItem richGroupItem=new TuanItem(); richGroupItem.setRow_num(jsonObject.getString("row_num")); richGroupItem.setDeal_id(jsonObject.getString("deal_id")); richGroupItem.setSub_title(jsonObject.getString("sub_title")); richGroupItem.setImg_domain(jsonObject.getString("img_domain")); richGroupItem.setImg(jsonObject.getString("img")); richGroupItem.setOrigin_price(jsonObject.getString("origin_price")); richGroupItem.setCurrent_price(jsonObject.getString("current_price")); richGroupItem.setSale_count(jsonObject.getString("sale_count")); richGroupItem.setSupplier_id(jsonObject.getString("supplier_id")); richGroupItem.setSupplier_name(jsonObject.getString("supplier_name")); richGroupItem.setShop_count(jsonObject.getString("shop_count")); richGroupItem.setRate(jsonObject.getString("rate")); richGroupItem.setRate_total_count(jsonObject.getString("rate_total_count")); TuanList.add(richGroupItem); } catch (JSONException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } return TuanList; } public void setOnTuanListener(OnTuanListener listener){ this.listener=listener; } public interface OnTuanListener { void onSuccess(List<TuanItem> list); void onError(); } }3、View包中,接口ITuanView,实现了该接口跟用户交互的Activity类
package view; import android.widget.BaseAdapter; public interface ITuanView { void setTuanData(BaseAdapter adapter); }实现该接口的类
package view; import presenter.ITuanPresenter; import presenter.TuanPresenter; import com.example.suoweimvpdemo.R; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.GridView; public class TuanActivity extends Activity implements ITuanView{ private Button loading; private GridView gridView; private ITuanPresenter iTuanPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCre d45b ate(savedInstanceState); setContentView(R.layout.richgroup); loading=(Button) findViewById(R.id.load_button); gridView=(GridView) findViewById(R.id.richGroup_girdview); loading.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { iTuanPresenter=new TuanPresenter(TuanActivity.this); iTuanPresenter.loadingTuanData(); } }); } @Override public void setTuanData(BaseAdapter adapter) { gridView.setAdapter(adapter); } }
4、Presenter包,因为要加载网络图片,用了第三方框架Image-loader,要适配数据,写了一个继承BaseAdapter的TuanAdapter。
剩余两个一个处理数据的接口ITuanPresenter,一个是实现了该接口的TuanPresenter,代码如下
Image-Loader代码:
package presenter; import java.io.File; import android.content.Context; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiscCache; import com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; public class ImageLoaderUtil { public static String IMAGE_CACHE_PATH = "SuoWeiAPP/imageloader/Cache"; public static ImageLoader initedImageLoader(Context context) { File cacheDir = com.nostra13.universalimageloader.utils.StorageUtils .getOwnCacheDirectory(context, IMAGE_CACHE_PATH); DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder() .cacheInMemory(true).cacheOnDisc(true).build(); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder( context).defaultDisplayImageOptions(defaultOptions) .memoryCache(new LruMemoryCache(12 * 1024 * 1024)) .memoryCacheSize(12 * 1024 * 1024) .discCacheSize(32 * 1024 * 1024).discCacheFileCount(100) .discCache(new UnlimitedDiscCache(cacheDir)) .threadPriority(Thread.NORM_PRIORITY - 2) .tasksProcessingOrder(QueueProcessingType.LIFO).build(); ImageLoader.getInstance().init(config); return ImageLoader.getInstance(); } }
适配器代码:
package presenter; import java.util.List; import bean.TuanItem; import com.example.suoweimvpdemo.R; import com.nostra13.universalimageloader.core.ImageLoader; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class TuanAdapter extends BaseAdapter{ private ImageLoader imageLoader; private List<TuanItem> list; private LayoutInflater inflater; public TuanAdapter(List<TuanItem> list,Context context){ inflater=LayoutInflater.from(context); this.list=list; imageLoader=ImageLoaderUtil.initedImageLoader(context); } @Override public int getCount() { // TODO 自动生成的方法存根 return list.size(); } @Override public Object getItem(int position) { // TODO 自动生成的方法存根 return list.get(position); } @Override public long getItemId(int position) { // TODO 自动生成的方法存根 return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyViewHonder viewHonder=null; if(convertView==null){ viewHonder=new MyViewHonder(); convertView=inflater.inflate(R.layout.richgroup_item,parent,false); viewHonder.imageView=(ImageView) convertView.findViewById(R.id.richGroup_iv_image); viewHonder.supplier_name=(TextView) convertView.findViewById(R.id.richGroup_tv_supplier_name); viewHonder.sub_name=(TextView) convertView.findViewById(R.id.richGroup_sub_title); viewHonder.origin_price=(TextView) convertView.findViewById(R.id.richGroup_tv_origin_price); viewHonder.current_price=(TextView) convertView.findViewById(R.id.richGroup_tv_current_price); convertView.setTag(viewHonder); } else { viewHonder=(MyViewHonder) convertView.getTag(); } viewHonder.sub_name.setText(list.get(position).getSub_title()); viewHonder.supplier_name.setText(list.get(position).getSupplier_name()); viewHonder.origin_price.setText(list.get(position).getOrigin_price()); viewHonder.current_price.setText(list.get(position).getCurrent_price()); String imageUrl=list.get(position).getImg_domain()+"/deal/280.170/"+list.get(position).getImg(); System.out.println("imageurl>>>>>>"+imageUrl); imageLoader.displayImage(imageUrl, viewHonder.imageView); return convertView; } class MyViewHonder{ ImageView imageView; TextView supplier_name; TextView sub_name; TextView origin_price; TextView current_price; } }接口代码:
package presenter; public interface ITuanPresenter { void loadingTuanData(); }
实现上面接口处理逻辑的代码:
package presenter; import java.util.List; import bean.TuanItem; import android.content.Context; import model.ITuanModel; import model.TuanModel; import model.TuanModel.OnTuanListener; import view.ITuanView; public class TuanPresenter implements ITuanPresenter,OnTuanListener{ private ITuanView iTuanView; private ITuanModel iTuanModel; private Context context; public TuanPresenter(Context context) { super(); this.context=context; this.iTuanView=(ITuanView) context; iTuanModel=new TuanModel(context); ((TuanModel) iTuanModel).setOnTuanListener(this); } @Override public void onSuccess(List<TuanItem> list) { iTuanView.setTuanData(new TuanAdapter(list, context)); } @Override public void onError() { } @Override public void loadingTuanData() { iTuanModel.getTuanData(); } }效果图
下面看看逻辑是如何处理的:
在View中的Activity中,进行用户交互,用户点击了loading按钮,执行onClick方法中的代码块
iTuanPresenter=new TuanPresenter(TuanActivity.this); iTuanPresenter.loadingTuanData();可以看到,点击之后把业务逻辑马上交给Presenter进行处理,调用ITuanPresenter接口的loadingTuanData()方法,而TuanPresenter类实现了该方法,所以调用了TuanPresenter类中的以下方法
@Override public void loadingTuanData() { iTuanModel.getTuanData(); }这个方法中,又马上调用了ITuanModel接口的getTuanData()方法,而TuanModel类实现了该方法,所以又调用了TuanModel类中的以下方法
@Override public void getTuanData() { getTuanJSON(); }在getTuanJSON()方法中,用到网络请求框架Volley获取到团购的JSON信息,解析JSON信息后把每个TuanItem装入List集合中,然后了,大家看到了,在TuanModel中写了一个OnTuanListener接口,通过该接口把处理好的数据List回调给实现该接口的对象,再往下看,那个实现了该接口,原来还是Presenter中的TuanPresenter类
@Override public void onSuccess(List<TuanItem> list) { iTuanView.setTuanData(new TuanAdapter(list, context)); } @Override public void onError() { }
再回调方法onSuccess中,又调用了ITuanView接口的setTuanData()方法,实现该方法的类就是Activity,在该类中,进行了适配器设置,从而更新了Ui
@Override public void setTuanData(BaseAdapter adapter) { gridView.setAdapter(adapter); }
现在大家有没有一种感觉,这个逻辑是从View走起,到Presenter,再到Model,返回来到Presenter,再到VIiew,是不是如下图指示
可以看到,View只负责Ui的更新和用户的交互,把逻辑处理扔给Presenter去做,而Presenter调用Model处理完数据后,在通过接口更新UI的信息。
哦,差点忘了,布局代码:
<?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" > <Button android:id="@+id/load_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="loading" /> <GridView android:id="@+id/richGroup_girdview" android:layout_width="match_parent" android:layout_height="wrap_content" android:fadingEdge="vertical" android:fadingEdgeLength="10dip" android:horizontalSpacing="5dp" android:numColumns="2" android:scrollbars="none" android:stretchMode="columnWidth" android:transcriptMode="disabled" android:verticalSpacing="5dp" > </GridView> </LinearLayout>
GirdView适配的布局代码:
<?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="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" android:layout_marginTop="8dp" android:orientation="vertical" android:padding="4dp" > <ImageView android:id="@+id/richGroup_iv_image" android:layout_width="match_parent" android:layout_height="100dip" android:layout_gravity="center|top" android:contentDescription="@string/app_name" android:padding="0dp" android:scaleType="fitCenter" android:src="@drawable/ic_launcher" /> <TextView android:id="@+id/richGroup_tv_supplier_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="2dp" android:ellipsize="end" android:maxLines="1" android:paddingLeft="4dp" android:paddingRight="4dp" android:textIsSelectable="false" android:textSize="18sp" /> <TextView android:id="@+id/richGroup_sub_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="2dp" android:ellipsize="end" android:maxLines="2" android:paddingLeft="4dp" android:paddingRight="4dp" android:textIsSelectable="false" android:textSize="14sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="25dp" android:layout_gravity="bottom" android:gravity="center_vertical" android:padding="3dp" > <ImageView android:id="@+id/iv_count" android:layout_width="18dp" android:layout_height="18dp" android:contentDescription="@string/app_name" android:scaleType="centerInside" /> <TextView android:id="@+id/richGroup_tv_current_price" android:layout_width="50dp" android:layout_height="15dp" android:layout_marginStart="5dip" android:gravity="center_vertical" /> <ImageView android:id="@+id/richGroup_iv_comment" android:layout_width="20dp" android:layout_height="20dp" android:contentDescription="@string/app_name" /> <TextView android:id="@+id/richGroup_tv_origin_price" android:layout_width="50dp" android:layout_height="15dp" android:layout_marginStart="5dip" android:gravity="center_vertical" /> </LinearLayout> </LinearLayout>
项目下载地址:MVP模式demo
相关文章推荐
- Android 应用开发实例之情景模式
- 大规模应用的开发与MVP设计模式
- iOS应用开发中运用设计模式中的组合模式的实例解析
- MVP模式在Android开发中的应用
- iOS应用运用设计模式中的Strategy策略模式的开发实例
- Android - 开发实例(12):安卓开发中最佳单例模式实现
- iOS应用开发中使用设计模式中的观察者模式的实例
- MVP模式在Android中的应用实例
- 实例讲解iOS应用的设计模式开发中的Visitor访问者模式
- MVP模式在Android开发中的应用
- MVP模式在Android开发中的应用
- JQuery Smart UI 快捷开发实例应用(二)— 开发模式【从项目开发流程说起】
- MVP模式在Android开发中的应用
- MVP模式在Android开发中的应用
- MVP模式在Android开发中的应用
- MVP模式在Android开发中的应用
- MVP模式在Android开发中的应用
- MVP模式在Android开发中的应用
- iOS应用开发中运用设计模式中的组合模式的实例解析
- MVP安卓开发模式