MVP+Databinding模式开发APP(一)
来源:互联网 发布:淘宝旺旺分流怎么设置 编辑:程序博客网 时间:2024/06/03 18:45
前言:本文适合了解入门MVP和Databinding的小伙伴们。小伙伴可能有疑问,为什么要用MVP+Databinding,因为我觉得MVP的三层非常的完美,Persenter层彻底的把Model层和View层分离,这样代码看起来整体的结构非常的清晰易懂,再结合Databinding减去findViewbyId的操作,这样代码看起来更舒服了,阅读性更高。
什么是MVC?
Model:从网络上获取的数据、数据库等数据结构
View:XML
Controller:Activity/Fragment。不仅需要设置数据、展示数据还得处理用户的事件逻辑、再与Model交互。这样在复杂逻辑的页面下Acitvity或Fragment十分的臃肿,并且耦合太高,不利用后期的维护。
什么是MVP?
MVP是MVC的升级,MVP把MVC的Model层和Controller层分离,达到解耦的目的,并且减轻了Activity\Fragment压力,把所有的业务逻辑交给P层处理。View层只负责界面展示。代码结构会变成异常的清晰,维护成本大大降低。
效果图
首先来看下包结构
首先搭建主UI界面,采用Activity嵌套4个Fragment(HomeFragment、CameraFragment、FilesFragment、MeFragment),在HomeFragment下面嵌套ViewPager每个page为一个Fragment分别为(SquareFragment、NewFragment、NearbyFragment)。
先看看公共接口类,用于存放View、Model、Persenter层的接口。把他们的接口放在一起可以减少创建类文件
布局 activity_main
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" ></FrameLayout> <RadioGroup android:id="@+id/rg_main" android:layout_width="match_parent" android:layout_height="50dp" android:orientation="horizontal"> <RadioButton android:id="@+id/rb_home" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:button="@null" android:gravity="center" android:padding="5dp" android:drawableTop="@drawable/tab_discover" android:text="发现" android:checked="true" /> <RadioButton android:id="@+id/rb_camrea" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:button="@null" android:gravity="center" android:padding="5dp" android:drawableTop="@drawable/tab_camera" android:text="相机"/> <RadioButton android:id="@+id/rb_files" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:button="@null" android:gravity="center" android:padding="5dp" android:drawableTop="@drawable/tab_gallery" android:text="本地"/> <RadioButton android:id="@+id/rb_me" android:layout_width="0dp" android:layout_weight="1" android:padding="5dp" android:layout_height="match_parent" android:button="@null" android:gravity="center" android:drawableTop="@drawable/tab_me" android:text="我"/> </RadioGroup> </LinearLayout></layout>
大家仔细看布局,根节点换成了layout,如果想使用databinding加载布局或者设置数据必须改成layout根节点。
布局比较简单,主UI下面是个按钮采用RadioGroup+RadioButton的来完成,这样的好处是可以减少处理一些setChecked()事件。代码看起来更优雅些。有些喜欢用TextView来做这个tab页签的虽然可以,但是并不合理。
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/tab_discover_icon" android:state_checked="true"/> <item android:drawable="@drawable/tab_discover_icon_normal" android:state_checked="false"/></selector>
drawableTop图片用selector状态选择器完成根据点击事件自己自动改变
在这里只粘贴一个了。
来看看MainActivity对应的接口
public class MainContract { public interface IMainView { //显示主页页签 void showHome(); //显示相机页签 void showCamera(); //显示本地页签 void showGallery(); //显示我页签 void showkMe(); //隐藏某个页签 void hideFragments(FragmentTransaction transaction); } public interface IMainPresenter { //点击主页页签 void clickHome(); //点击相机页签 void clickCamera(); //点击本地页签 void clickGallery(); //点击我页签 void clickMe(); }}
可以看到有View、Presenter的接口,因为UI框架不需要访问网络就没有model。
咋们看看view的方法名:showXXX()和hideXXXX(),看方法名称就能知道view层只负责界面的展示,并有业务逻辑方法。咋们再看看Presenter层:p层的方法都是clickXXX()用户事件的处理逻辑。所以view层只负责用户的界面展示,所有的逻辑都交给了Persenter层处理,减轻了V层的压力。
MainActivity的代码
public class MainActivity extends BaseActivity implements MainContract.IMainView { private ActivityMainBinding mainBinding; private HomeFragment homeFragment; private CameraFragment cameraFragment; private FilesFragment filesFragment; private MeFragment meFragment; private MainPresenter mainPresenter; @Override protected void onCreate(Bundle savedInstanceState) { //binding的命名规则为:布局文件名+binding,所以就.是:ActivityMainBinding //如果想使用databinding加载布局或者设置数据必须改成layout根节点 mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main); super.onCreate(savedInstanceState); } @Override public void init() { //初始化MainActivity对应的Persenter mainPresenter = new MainPresenter(this); } @Override public void initData() { //默认加载第一页 mainPresenter.clickHome(); } @Override public void setListener() { mainBinding.rgMain.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int i) { switch (i){ case R.id.rb_home: //在P层处理用户事件 mainPresenter.clickHome(); break; case R.id.rb_camrea: //在P层处理用户事件 mainPresenter.clickCamera(); break; case R.id.rb_files: //在P层处理用户事件 mainPresenter.clickGallery(); break; case R.id.rb_me: //在P层处理用户事件 mainPresenter.clickMe(); break; } } }); } @Override public void setting() { } @Override public void showHome() { FragmentTransaction fragmentTransaction = getSupportFragmentManager() .beginTransaction(); hideFragments(fragmentTransaction); if (homeFragment == null) { homeFragment = new HomeFragment(); fragmentTransaction.add(R.id.content, homeFragment); fragmentTransaction.commit(); } else { fragmentTransaction.show(homeFragment); fragmentTransaction.commit(); } } @Override public void showCamera() { FragmentTransaction fragmentTransaction = getSupportFragmentManager() .beginTransaction(); hideFragments(fragmentTransaction); if (cameraFragment == null) { cameraFragment = new CameraFragment(); fragmentTransaction.add(R.id.content, cameraFragment); fragmentTransaction.commit(); } else { fragmentTransaction.show(cameraFragment); fragmentTransaction.commit(); //cameraFragment.updateCamera(); } } @Override public void showGallery() { FragmentTransaction fragmentTransaction = getSupportFragmentManager() .beginTransaction(); hideFragments(fragmentTransaction); if (filesFragment == null) { filesFragment = new FilesFragment(); fragmentTransaction.add(R.id.content, filesFragment); fragmentTransaction.commit(); } else { fragmentTransaction.show(filesFragment); fragmentTransaction.commit(); } } @Override public void showMe() { FragmentTransaction fragmentTransaction = getSupportFragmentManager() .beginTransaction(); hideFragments(fragmentTransaction); if (meFragment == null) { meFragment = new MeFragment(); fragmentTransaction.add(R.id.content, meFragment); fragmentTransaction.commit(); } else { fragmentTransaction.show(meFragment); fragmentTransaction.commit(); } } public void hideFragments(FragmentTransaction transaction) { if (homeFragment != null) { transaction.hide(homeFragment); } if (cameraFragment != null) { transaction.hide(cameraFragment); } if (filesFragment != null) { transaction.hide(filesFragment); } if (meFragment != null) { transaction.hide(meFragment); } }}
Activity去除了findViewbyId的操作,使的代码简洁了很多。使用mainbinding获得控件,只要控件设置id名称就能自动生成binding使用的变量名。
android:id=”@+id/rg_main” 自动生成“rgMain”
public abstract class BaseActivity extends FragmentActivity { ... @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mContext =this; init(); initData(); setListener(); setting(); } //初始化一些对象或者属性 public abstract void init(); //初始化数据 public abstract void initData(); //设置监听事件 public abstract void setListener(); //后续一些参数的设置 public abstract void setting(); ...}
MainActivity继承至封装好的BaseActivity和实现view层的接口重写下面的方法。
public class MainPresenter implements MainContract.IMainPresenter { private final MainContract.IMainView iMainView;//构造方法传入view接口,P层于View层的交互用接口来进行 public MainPresenter(MainContract.IMainView iMainView){ this.iMainView =iMainView; } @Override public void clickHome() { iMainView.showkHome(); } @Override public void clickCamera() { iMainView.showCamera(); } @Override public void clickGallery() { iMainView.showGallery(); } @Override public void clickMe() { iMainView.showkMe(); }}
可以看到在MainPresenter处理所有的用户事件,也就是业务逻辑。因为构建UI不需要访问网络,所以没有Model。这实现了底部的页签了。下面来实现HomeFragment下的三个页签。
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="@android:color/black" > <RadioGroup android:id="@+id/rg_homeTabGroup" android:layout_width="200dp" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="center|center_vertical" > <RadioButton android:id="@+id/radio_square" android:layout_width="0dp" android:layout_weight="1" android:layout_height="30dp" android:button="@null" android:text="大厅" android:gravity="center" android:textSize="16sp" android:checked="true" android:textColor="@drawable/selector_tab_color_changer" android:background="@drawable/selector_square_changer" /> <RadioButton android:id="@+id/radio_news" android:layout_width="0dp" android:layout_weight="1" android:layout_height="30dp" android:button="@null" android:text="最新" android:gravity="center" android:textSize="16sp" android:textColor="@drawable/selector_tab_color_changer" android:background="@drawable/selector_news_changer" /> <RadioButton android:id="@+id/radio_nearby" android:layout_width="0dp" android:layout_weight="1" android:layout_height="30dp" android:button="@null" android:text="地图" android:gravity="center" android:textSize="16sp" android:textColor="@drawable/selector_tab_color_changer" android:background="@drawable/selector_nearby_changer" /> </RadioGroup> </FrameLayout> <android.support.v4.view.ViewPager android:id="@+id/vp_content" android:layout_width="match_parent" android:layout_height="match_parent" > </android.support.v4.view.ViewPager> </LinearLayout></layout>
public class HomeContract { public interface IHomeView { //显示大厅Fragment void showSquare(); //显示最新Fragment void showNew(); //显示地图Fragment void showNearby(); //切换页签 void tabChanger(int position); } public interface IHomePresenter { void clickSquare(); void clickNew(); void clickNearby(); void tabChanger(int position); }}
public class HomeFragment extends BaseFragment implements HomeContract.IHomeView{ private ActivityHomeBinding homeBinding; private SquareFragment squareFragment; private NewFragment newFragment; private NearbyFragment nearbyFragment; private HomePresenter homePresenter; private View rootView; @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {//databinding也有对应的填充View的方法 homeBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_home,container,false); rootView = homeBinding.getRoot(); init(); initData(); setListener(); return rootView; } @Override public void init() { //初始化Persenter homePresenter = new HomePresenter(this); } @Override public void initData() { //初始化三个页签对应的Fragment。提供给Viewpager加载。 List<Fragment> fragments=new ArrayList<>(); fragments.add(new SquareFragment()); fragments.add(new NewFragment()); fragments.add(new NearbyFragment()); CommFragmentPagerAdapter pagerAdapter=new CommFragmentPagerAdapter(getFragmentManager(), fragments); homeBinding.vpContent.setAdapter(pagerAdapter); } @Override public void setListener() { //点击顶部tab时切换 homeBinding.rgHomeTabGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup radioGroup, int i) { switch (i){ case R.id.radio_square: homePresenter.clickSquare(); break; case R.id.radio_news: homePresenter.clickNew(); break; case R.id.radio_nearby: homePresenter.clickNearby(); break; } } });//滑动Viewpager时顶部页签做出相应的改变 homeBinding.vpContent.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { homePresenter.tabChanger(position); } @Override public void onPageScrollStateChanged(int state) { } }); } @Override public void showSquare() { homeBinding.vpContent.setCurrentItem(0); } @Override public void showNew() { homeBinding.vpContent.setCurrentItem(1); } @Override public void showNearby() { homeBinding.vpContent.setCurrentItem(2); } @Override public void tabChanger(int position) { switch (position){ case 0: homeBinding.radioSquare.setChecked(true); break; case 1: homeBinding.radioNews.setChecked(true); break; case 2: homeBinding.radioNearby.setChecked(true); break; } }}
public class HomePresenter implements HomeContract.IHomePresenter { private final HomeContract.IHomeView iHomeView; public HomePresenter(HomeContract.IHomeView iHomeView){ this.iHomeView =iHomeView; } @Override public void clickSquare() { iHomeView.showSquare(); } @Override public void clickNew() { iHomeView.showNew(); } @Override public void clickNearby() { iHomeView.showNearby(); } @Override public void tabChanger(int position) { iHomeView.tabChanger(position); }}
大家看到代码比较简单。跟MainActivity几乎一样。只是把databinding的填充方法DataBindingUtil.setContentView()换成了DataBindingUtil.inflate().
这UI就全部完成啦。
因为UI框架不需要访问网络所以没有用到Model,所以未能完全的体现MVP的价值。
MVP+Databinding模式开发APP(二)
- MVP+Databinding模式开发APP(一)
- MVP+Databinding模式开发APP(二)
- Android开发App架构MVP模式
- Android Mvp+DataBinding架构模式详解
- android小白进阶MVP模式开发(三步曲),让你全面理解MVP(一)
- 从零开始搭建 一个完善的 MVP模式开发框架(一),MVP模式的简单介绍篇
- DataBinding系列(一):DataBinding初认识
- Android开发模式之--MVP设计模式一
- Android开发-数据绑定-DataBinding-AndroidStudio(一)初识DataBinding
- android中MVP模式(一)
- Android---MVP设计模式初级(一)
- android中MVP模式(一)
- android中MVP模式(一)
- android中MVP模式(一)
- 打造Android MVP模式(一)
- android中MVP模式(一)
- Android 设计模式之 MVP(一)
- Android MVP模式之(一)初识
- OpenGL中常用的 GLUT 函数
- 【C++解题报告】猴子吃枣问题
- 总结2016,展望2017
- acm 输入输出总结
- 【瞎搞】回文自动机
- MVP+Databinding模式开发APP(一)
- 学习Arduino——基础入门
- 输入n值,并利用下列格里高里公式计算并输出圆周率
- Goodbye 2016
- SpringBoot启动流程详解(搬运)
- NodeJS-stream流
- 2016.12.31 学习日记-适配器模式
- Centos下搭建SVN服务器并同步仓库内容到WEB目录
- 将文件夹内所有文件复制到指定路径