前言:本文适合了解入门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(二)