关键词:
Android MVVM框架搭建(五)Navigation + Fragment + BottomNavigationView
前言
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⑤页面数据处理四、传递新闻参数五、热门... 查看详情