androidmvvm框架搭建navigation+fragment+bottomnavigationview(代码片段)

初学者-Study 初学者-Study     2023-02-13     694

关键词:

前言

  MVVM框架的模式在这几篇文章中相比你已经熟悉很多了,具体的架构模式如下图所示:

上层的Activity/Fragment表示为View层,通过ViewModel去操作数据,然后由Repository去控制数据的来源,可以是本地数据库也可以是网络数据。这个模式在文章和代码中都有体现,算是比较的完整了。

本文效果图如下:

正文

  MVVM框架的搭建按理来说就已经完成了,但是我们既然要弄一个实用的框架,就不能只停留于框架搭建的阶段,还要有实用的场景,我喜欢我的框架可以满足绝大部分开发中的使用。现在我们的框架虽然有了Activity,但是还没有使用过Fragment,通常Fragment是在什么时候使用呢?例如主页面五个子模块Fragment,分别表示五个功能,这样是不是会很好呢,这样就完美的将Fragment融入了进去,同时我们还可以与实际的开发模式相结合起来。嗯,不错,开始行动吧。

一、添加依赖

  使用Navigation需要添加依赖,在app的build.gradle中的dependencies闭包中添加如下依赖:

	// navigation依赖 ui 和 fragment
    implementation 'androidx.navigation:navigation-fragment:2.3.2'
    implementation 'androidx.navigation:navigation-ui:2.3.2'

然后Sync Now同步依赖项目。

二、Fragment创建

  创建Fragment可以通过快捷的方式,自带了ViewModel的,如下图所示:

  这里创建两个Fragment,NewsFragment和VideoFragment,对应的布局文件是news_fragment.xml和video_fragment.xml,ViewModel是NewsViewModel和VideoViewModel。

  下面对项目的包分一下,我把Activity、Fragment、Adapter都看为ui,那么我在com.llw.mvvm包下新建一个ui包,包下新建一个fragment包,然后将NewsFragment和VideoFragment放入fragment包,然后把adapter包也移到ui包下,同时在ui包下新建一个activity包,包下将项目中所有的Activity移入,最后将NewsViewModel和VideoViewModel放到viewmodels包下。目录结构如下图所示:

下面依次修改一下news_fragment.xml和video_fragment.xml中的内容:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
</layout>

两个xml里面的内容都是上面的这个代码,复制粘贴即可,这两个Fragment中的内容我们待会儿再写。

三、BaseActivity创建

  因为我们的Activity比较多,而可能有些Activity中的方法有重合的,或者通用的,这种情况下我们可以将一些方法放入一个基础类里面,例如BaseActivity中,下面进行创建,在activity包下新建一个BaseActivity类,代码如下:

/**
 * 基础Activity
 *
 * @author llw
 */
public class BaseActivity extends AppCompatActivity 

    protected AppCompatActivity context;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        this.context = this;
    

    protected void showMsg(CharSequence msg) 
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    

    protected void showLongMsg(CharSequence msg) 
        Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
    

    /**
     * 跳转页面
     * @param clazz 目标页面
     */
    protected void jumpActivity(final Class<?> clazz) 
        startActivity(new Intent(context, clazz));
    

    /**
     * 跳转页面并关闭当前页面
     * @param clazz 目标页面
     */
    protected void jumpActivityFinish(final Class<?> clazz) 
        startActivity(new Intent(context, clazz));
        finish();
    

    /**
     * 状态栏文字图标颜色
     * @param dark 深色 false 为浅色
     */
    protected void setStatusBar(boolean dark) 
        View decor = getWindow().getDecorView();
        if (dark) 
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
         else 
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        
    

里面也是一些简单的方法,后面在开发中有新的需要可以一直加进去,根据实际情况来,不要什么都加进去,其实没必要的。

四、启动页

  我们的这个MVVM-Demo虽然只是一个Demo,但是我们要给自己一个高一点的标准,所以我打算给一个启动页,一个简单的动画,然后进入我们的登录页,虽然我们是一个假登录,但是意思已经到位了。然后我们在登录页面上记录程序是否登录过,如果登录过下次进入程序就不再进入登录页面,而是直接进入主页面了,这样的逻辑很简单,下面来实现一下吧。

在activity包下新建一个SplashActivity,对应的布局是activity_splash.xml,xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        tools:context=".ui.activity.SplashActivity">

        <RelativeLayout
            android:layout_width="160dp"
            android:layout_height="160dp">

            <ImageView
                android:layout_width="160dp"
                android:layout_height="160dp"
                android:src="@mipmap/ic_splash_logo" />

            <TextView
                android:visibility="invisible"
                android:id="@+id/tv_mvvm"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:layout_marginBottom="46dp"
                android:text="MVVM"
                android:textColor="@color/white"
                android:textSize="28sp"
                android:textStyle="bold" />
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Model View ViewModel"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <TextView
                android:id="@+id/tv_translate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@color/white"
                android:text="Model View ViewModel"
                android:textColor="@color/white"
                android:textSize="24sp" />
        </RelativeLayout>
    </LinearLayout>
</layout>

这里面有一个图标ic_splash_logo.png,我这里贴一下,不过你最好到我的源码去找,这样不会有水印,而且图片格式也是对的。

针对于启动页我特别弄了一个主题样式,在themes.xml下增加如下代码样式:

	<style name="SplashTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:statusBarColor" tools:targetApi="lollipop">#00FFFFFF</item><!--设置状态栏的颜色-->
    </style>

然后我们修改AndroidManifest.xml中的代码,因为之前的启动Activity是LoginActivity,需要改一下。如下图所示:

下面我们增加一个动画的帮助工具类,在utils包下新建一个EasyAnimation类,里面的代码如下:

public class EasyAnimation 

    /**
     * 开始眨眼动画
     *
     * @param view 需要设置动画的View
     */
    public static void startBlink(View view) 
        AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
        alphaAnimation.setDuration(500);
        alphaAnimation.setStartOffset(20);
        alphaAnimation.setRepeatMode(Animation.REVERSE);
        alphaAnimation.setRepeatCount(Animation.INFINITE);
        view.startAnimation(alphaAnimation);
    

    /**
     * 开始眨眼动画
     *
     * @param view           需要设置动画的View
     * @param alphaAnimation 透明度动画(自行配置)
     */
    public static void startBlink(View view, AlphaAnimation alphaAnimation) 
        view.startAnimation(alphaAnimation);
    


    /**
     * 停止眨眼动画
     *
     * @param view 需要清除动画的View
     */
    public static void stopBlink(View view) 
        if (view != null) 
            view.clearAnimation();
        
    

    /**
     * 移动指定View的宽度
     *
     * @param view
     */
    public static void moveViewWidth(View view, TranslateCallback callback) 
        view.post(() -> 
            //通过post拿到的tvTranslate.getWidth()不会为0。
            TranslateAnimation translateAnimation = new TranslateAnimation(0, view.getWidth(), 0, 0);
            translateAnimation.setDuration(1000);
            translateAnimation.setFillAfter(true);
            view.startAnimation(translateAnimation);

            //动画监听
            translateAnimation.setAnimationListener(new Animation.AnimationListener() 
                @Override
                public void onAnimationStart(Animation animation) 

                

                @Override
                public void onAnimationEnd(Animation animation) 
                    //检查Android版本
                    callback.animationEnd();
                

                @Override
                public void onAnimationRepeat(Animation animation) 

                
            );
        );
    

    /**
     * 移动指定View的宽度
     *
     * @param view               需要位移的View
     * @param callback           位移动画回调
     * @param translateAnimation 位移动画 (自行配置)
     */
    public static void moveViewWidth(View view, TranslateCallback callback, TranslateAnimation translateAnimation) 
        view.post(() -> 
            //通过post拿到的tvTranslate.getWidth()不会为0。

            view.startAnimation(translateAnimation);

            //动画监听
            translateAnimation.setAnimationListener(new Animation.AnimationListener() 
                @Override
                public void onAnimationStart(Animation animation) 

                

                @Override
                public void onAnimationEnd(Animation animation) 
                    //检查Android版本
                    callback.animationEnd();
                

                @Override
                public void onAnimationRepeat(Animation animation) 

                
            );
        );
    

    public interface TranslateCallback 
        //动画结束
        void animationEnd();
    


因为在启动页需要知道程序有没有登录,因此在Constant中增加一个常量,如下所示:

	/**
     * 是否登录过
     */
    public static final String IS_LOGIN = "isLogin";

下面我们修改一下SplashActivity的代码,使用这个常量来判断需要跳转到那个页面,代码如下:

public class SplashActivity extends BaseActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        ActivitySplashBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_splash);
        setStatusBar(true);
        EasyAnimation.moveViewWidth(binding.tvTranslate, () -> 
            binding.tvMvvm.setVisibility(View.VISIBLE);
            jumpActivity(MVUtils.getBoolean(Constant.IS_LOGIN) ? MainActivity.class : LoginActivity.class);
        );
    

这里我继承了BaseActivity,然后设置了状态栏深色模式,因为我们的页面是白色的,如果状态栏也是白色就看不出来了,后面就是在动画结束的时候跳转页面,很简单的代码。这个页面的代码就写完了,下面我们修改LoginActivity中的代码,首先是修改继承的Activity为BaseActivity。里面的代码如下:

public class LoginActivity extends BaseActivity 

    private ActivityLoginBinding dataBinding;
    private LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        //数据绑定视图
        dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);
        loginViewModel = new LoginViewModel();
        //Model → View
        User user = new User("admin", "123456");
        loginViewModel.getUser().setValue(user);
        //获取观察对象
        MutableLiveData<User> user1 = loginViewModel.getUser();
        user1.observe(this, user2 -> dataBinding.setViewModel(loginViewModel));

        dataBinding.btnLogin.setOnClickListener(v -> 
            if (loginViewModel.user.getValue().getAccount().isEmpty()) 
                showMsg("请输入账号");
                return;
            
            if (loginViewModel.user.getValue().getPwd().isEmpty()) 
                showMsg("请输入密码");
                return;
            
            //记录已经登录过
            MVUtils.put(Constant.IS_LOGIN,true);
            showMsg("登录成功");
            jumpActivity(MainActivity.class);
        );
    

这里就没啥好说的,就是使用了BaseActivity中的方法。同时我修改了一下布局中的代码,我将这两个TextView隐藏了

同时我们修改一下图片显示之前的占位图或者说是默认背景图。两个图片如下:



然后一个加载图片出错时显示的图片:

首先是MainActivity中,显示必应图片的位置,修改一下activity_main.xml

然后打开CustomImageView,增加如下代码:

	private static final RequestOptions OPTIONS = new RequestOptions()
            .placeholder(R.drawable.wallpaper_bg)//图片加载出来前,显示的图片
            .fallback(R.drawable.wallpaper_bg) //url为空的时候,显示的图片
            .error(R.mipmap.ic_loading_failed);//图片加载失败后,显示的图片

将这个值配置进去,如下图所示:

下面我们运行一下看是什么效果。

效果还可以的,下面进入主页面的代码编写。

五、主页面

  当到了每日壁纸页面时,我们需要再提供一个入口可以进入下一个页面,现在的每日壁纸页面不能算是真正意义上的主页面,因此我们写一个入口,可以在MainActivity中增加一个浮动按钮,页面上下滑动时控制按钮的显示和消失。下面在activity_main.xml中增加如下布局代码:

<com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab_home"
            android:layout_width="wrap_contentandroidmvvm框架搭建高德地图定位天气查询bottomsheetdialog(代码片段)

AndroidMVVM框架搭建(八)高德地图定位、天气查询、BottomSheetDialog前言正文一、集成SDK二、基础配置①权限配置②配置Key三、显示地图①MapFragment②Navigation绑定③Fragment中地图生命周期绑定四、显示当前所在地①定位动态... 查看详情

androidmvvm框架搭建hiltviewbindingactivityresultapi(代码片段)

AndroidMVVM框架搭建(十)Hilt、ViewBinding、ActivityResultAPI前言正文一、依赖二、Hilt使用1.Hilt应用类2.ViewModel使用3.单例使用三、ViewBinding使用1.ViewBinding介绍2.启用ViewBinding3.ViewBinding使用介绍4.忽略布局文件四、ActivityResultAPI使 查看详情

androidmvvm框架搭建viewmodel+livedata+databinding(代码片段)

AndroidMVVM框架搭建(一)ViewModel+LiveData+DataBinding前言正文一、创建项目二、ViewModel使用①绑定Activity②页面布局绘制③实现登录二、LiveData使用①可修改数据②数据观察三、DataBinding使用①单向绑定②双向绑定四、源... 查看详情

androidmvvm框架搭建okhttp+retrofit+rxjava(代码片段)

AndroidMVVM框架搭建(二)Retrofit+RxJava前言正文一、引入依赖二、工具类三、构建网络框架1.Base2.异常处理3.拦截器4.网络请求服务四、使用网络框架1.创建返回实体2.创建ApiService3.创建数据存储4.项目环境配置5.必应图片显... 查看详情

androidmvvm框架搭建permissionalertdialog拍照和相册选取(代码片段)

AndroidMVVM框架搭建(七)Permission、AlertDialog、拍照和相册选取前言正文一、数据库升级二、数据操作二、自定义Dialog①DialogViewHelper②AlertController③AlertDialog④样式⑤布局三、权限请求①权限配置②权限工具类四、DataBinding... 查看详情

androidmvvm框架搭建mmkv+room+rxjava2(代码片段)

AndroidMVVM框架搭建(三)MMKV+Room+RxJava2前言正文一、添加依赖二、MMKV1.初始化2.数据存取3.使用三、Room1.@Entity2.@Dao3.@Database4.初始化5.使用6.优化四、RxJava21.Flowable&Completable2.CustomDisposable3. 查看详情

androidmvvm框架搭建高德地图定位天气查询bottomsheetdialog(代码片段)

AndroidMVVM框架搭建(八)高德地图定位、天气查询、BottomSheetDialog前言正文一、集成SDK二、基础配置①权限配置②配置Key三、显示地图①MapFragment②Navigation绑定③Fragment中地图生命周期绑定四、显示当前所在地①定位动态... 查看详情

androidmvvm框架搭建okhttp+retrofit+rxjava(代码片段)

AndroidMVVM框架搭建(二)Retrofit+RxJava前言正文一、引入依赖二、工具类三、构建网络框架1.Base2.异常处理3.拦截器4.网络请求服务四、使用网络框架1.创建返回实体2.创建ApiService3.创建数据存储4.项目环境配置5.必应图片显... 查看详情

androidmvvm框架搭建viewmodel+livedata+databinding(代码片段)

AndroidMVVM框架搭建(一)ViewModel+LiveData+DataBinding前言正文一、创建项目二、ViewModel使用①绑定Activity②页面布局绘制③实现登录二、LiveData使用①可修改数据②数据观察三、DataBinding使用①单向绑定②双向绑定四、源... 查看详情

androidmvvm框架搭建tablayoutviewpager城市地图天气切换(代码片段)

AndroidMVVM框架搭建(九)TabLayout、ViewPager、城市地图切换前言正文一、父Fragment加载子Fragment①Fragment适配器②TabLayout组合ViewPager二、抽屉菜单三、行政区搜索四、行政区展示①省市级联②返回上一级五、地址转坐标六、切... 查看详情

androidmvvm框架使用(功能开发)记事本(代码片段)

AndroidMVVM框架使用功能开发之记事本前言正文一、记事本页面二、编辑页面三、增加笔记表①Bean②Dao③数据库升级迁移④新增存储库类⑤新增ViewModel⑥添加笔记四、显示笔记列表五、修改笔记六、删除笔记七、源码八、开心一下... 查看详情

androidmvvm框架搭建recyclerview+viewpager2+basequickadapter(代码片段)

AndroidMVVM框架搭建(四)RecyclerVIew+ViewPager2+BaseQuickAdapter前言正文一、图片列表数据二、新增访问地址和接口三、访问接口四、RecyclerView显示数据五、绑定点击事件六、协调布局使用七、保存本地数据库1.Entity2.Dao3.版... 查看详情

androidmvvm框架搭建recyclerview+viewpager2+basequickadapter(代码片段)

AndroidMVVM框架搭建(四)RecyclerVIew+ViewPager2+BaseQuickAdapter前言正文一、图片列表数据二、新增访问地址和接口三、访问接口四、RecyclerView显示数据五、绑定点击事件六、协调布局使用七、保存本地数据库1.Entity2.Dao3.版... 查看详情

androidmvvm框架搭建mmkv+room+rxjava2(代码片段)

AndroidMVVM框架搭建(三)MMKV+Room+RxJava2前言正文一、添加依赖二、MMKV1.初始化2.数据存取3.使用三、Room1.@Entity2.@Dao3.@Database4.初始化5.使用6.优化四、RxJava21.Flowable&Completable2.CustomDisposable3.使用五、源码前言... 查看详情

androidmvvm框架搭建permissionalertdialog拍照和相册选取(代码片段)

AndroidMVVM框架搭建(七)Permission、AlertDialog、拍照和相册选取前言正文一、数据库升级二、数据操作二、自定义Dialog①DialogViewHelper②AlertController③AlertDialog④样式⑤布局三、权限请求①权限配置②权限工具类四、DataBinding... 查看详情

androidmvvm框架搭建permissionalertdialog拍照和相册选取(代码片段)

AndroidMVVM框架搭建(七)Permission、AlertDialog、拍照和相册选取前言正文一、数据库升级二、数据操作二、自定义Dialog①DialogViewHelper②AlertController③AlertDialog④样式⑤布局三、权限请求①权限配置②权限工具类四、DataBinding... 查看详情

androidmvvm框架搭建腾讯x5webview+drawerlayout+navigationview(代码片段)

AndroidMVVM框架搭建(六)腾讯X5WebView+DrawerLayout+NavigationView前言正文一、添加依赖二、使用WebView三、获取新闻详情①新闻详情数据②新闻详情数据API③WebRepository④WebViewModel⑤页面数据处理四、传递新闻参数五、热门... 查看详情

androidmvvm框架搭建腾讯x5webview+drawerlayout+navigationview(代码片段)

AndroidMVVM框架搭建(六)腾讯X5WebView+DrawerLayout+NavigationView前言正文一、添加依赖二、使用WebView三、获取新闻详情①新闻详情数据②新闻详情数据API③WebRepository④WebViewModel⑤页面数据处理四、传递新闻参数五、热门... 查看详情