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(二)

0 0
原创粉丝点击