MVP+Databinding模式开发APP(一)

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值