Android简易音乐重构MVVM Java版 -搭建项目(八)

  • 关于
  • 新版本配置
    • 网易云音乐api版本更新
  • 重构代码
    • 新建app类继承Application
    • 项目结构
    • 定义BaseActivity.java
    • 状态栏工具
    • 防恶意多点工具类
    • CrashHandler用于异常处理上报日志

关于

  本篇主要介绍,简易音乐重构,去掉butterkinfe,去掉mvp,精简代码优化逻辑,使用viewbinding和databinding,使用了Navigation导航组件来在部分页面跳转,升级项目版本工程版本,升级引用等等,但是最终我还是没有使用kotlin去重构这个项目,我是很想用kotlin写的,不管是出于个人目前对kotlin的热爱还是google对kotlin的支持,但是看起来目前对我这个moudle感兴趣的人用java的多点,所以这个项目目前还是用java去写,废话到此结束。
  项目框架MVVM+Retrofit+RxJava+LiveData+viewbinding+databing+Navigation导航组件+other

新版本配置

  Android studio 2021.2.1 Patch 1截至2022.6.14来说是稳定版的最新版

  工程gradle是7.2.1,gradle-wrapper.properties里的gradle是7.3.3:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {id 'com.android.application' version '7.2.1' apply falseid 'com.android.library' version '7.2.1' apply false
}task clean(type: Delete) {delete rootProject.buildDir
}
#Sat Jun 11 00:50:53 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME


  很多朋友可能会好奇三分的maven仓库啊jitpack库啊跑哪里去了,都跑到settings.gradle里面了:

  这也是很大一个变动。
  项目目标版本32,最低支持22
  接下来是moudle里面的build的变化,:

android {compileSdk 32defaultConfig {applicationId "com.tobery.personalmusic"minSdk 22targetSdk 32versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}dataBinding{enabled true}viewBinding{enabled true}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}
}

  三方引用如下:

dependencies {implementation 'androidx.appcompat:appcompat:1.3.0'implementation 'com.google.android.material:material:1.4.0'implementation 'androidx.constraintlayout:constraintlayout:2.0.4'// ViewModelimplementation "androidx.lifecycle:lifecycle-viewmodel:2.4.0"implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0"//glide引用implementation 'com.github.bumptech.glide:glide:4.11.0'annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'//网络请求retrofitimplementation 'com.squareup.retrofit2:retrofit:2.9.0'//RxJavaimplementation 'io.reactivex.rxjava2:rxjava:2.1.7'//RxAndroidimplementation 'io.reactivex.rxjava2:rxandroid:2.0.1'//Retrofit 支持Rxjava 的支持库implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'//rxjava框架implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'implementation 'com.google.code.gson:gson:2.8.6'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'//音频implementation 'com.github.EspoirX:StarrySky:v2.6.5'// MultiDex的依赖implementation 'androidx.multidex:multidex:2.0.0'// Log、吐司打印implementation 'com.github.getActivity:ToastUtils:10.3'implementation 'com.github.Tobeyr1:DialogLoading:1.0.4'//bannerimplementation 'io.github.youth5201314:banner:2.2.2'implementation "androidx.viewpager2:viewpager2:1.0.0"implementation 'com.github.Tobeyr1:liveDataCallAdapter:1.0.2'testImplementation 'junit:junit:4.13.2'androidTestImplementation 'androidx.test.ext:junit:1.1.3'androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

  目前的引用就这么多,后续也会有加减。

网易云音乐api版本更新

  api当然不是我做的了,我只是个使用者,GitHub地址,当然了,项目里也提供了可供使用的api网址

重构代码

  (说明)其实项目是重新建的项目,然后对部分copy,大多数重新编写。

新建app类继承Application

public class app extends Application {private final CountDownLatch mCountDownLatch = new CountDownLatch(1);private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();private static final int CORE_POOL_SIZE = Math.max(2,Math.min(CPU_COUNT-1,4));private static app instance;@Overridepublic void onCreate() {super.onCreate();instance = this;ToastUtils.init(this, new BlackToastStyle());CrashHandler.getInstance().init(this);ExecutorService pool = Executors.newFixedThreadPool(CORE_POOL_SIZE);pool.submit(new Runnable() {@Overridepublic void run() {MultiDex.install(instance);mCountDownLatch.countDown();}});pool.submit(new Runnable() {@Overridepublic void run() {StarrySky.init(instance).apply();}});try {//如果await之前没有调用countDown那么就会一直阻塞在这里mCountDownLatch.await();}catch (InterruptedException e){e.printStackTrace();}}
}

项目结构

  entity文件存放实体类,http则是网络请求的类api等,ui页面viewModel等,util则是一些帮助类等,widget存放的自定义view,暂时这样。

定义BaseActivity.java

public abstract class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);//状态栏工具StatusBarUtil.setColor(this,getResources().getColor(R.color.colorPrimary),0);}@Overrideprotected void onPause() {super.onPause();}@Overrideprotected void onDestroy() {System.gc();super.onDestroy();}}

状态栏工具

/*** 2019年7月18日13:55:00* 沉浸式管理工具类*/
public class StatusBarUtil {public static final String TAG = "StatusBarUtil";private static final int DEFAULT_STATUS_BAR_ALPHA = 112;private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view;private static final int FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view;private static final int TAG_KEY_HAVE_SET_OFFSET = -123;/*** 设置状态栏颜色** @param activity 需要设置的 activity* @param color    状态栏颜色值*/public static void setColor(Activity activity, @ColorInt int color) {setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA);}/*** 设置状态栏颜色* API等级19:Android 4.4 KitKat   API等级20:Android 4.4W   API等级21:Android 5.0 Lollipop* API等级22:Android 5.1 Lollipop  API等级23:Android 6.0 Marshmallow   API等级24:Android 7.0 Nougat** @param activity       需要设置的activity* @param color          状态栏颜色值* @param statusBarAlpha 状态栏透明度*/@SuppressLint("ObsoleteSdkInt")public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) {//21以上if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));//19到21} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//DecorView是顶级View,内部有titlebar和contentParent两个子元素,// contentParent的id是content,而我们设置的main.xml布局则是contentParent里面的一个子元素ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);if (fakeStatusBarView != null) {if (fakeStatusBarView.getVisibility() == View.GONE) {fakeStatusBarView.setVisibility(View.VISIBLE);}fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));} else {//第一次加载会先创建并添加decorView.addView(createStatusBarView(activity, color, statusBarAlpha));}setRootView(activity);}}/*** 为滑动返回界面设置状态栏颜色** @param activity 需要设置的activity* @param color    状态栏颜色值*/public static void setColorForSwipeBack(Activity activity, int color) {setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA);}/*** 为滑动返回界面设置状态栏颜色** @param activity       需要设置的activity* @param color          状态栏颜色值* @param statusBarAlpha 状态栏透明度*/public static void setColorForSwipeBack(Activity activity, @ColorInt int color,@IntRange(from = 0, to = 255) int statusBarAlpha) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content));View rootView = contentView.getChildAt(0);int statusBarHeight = getStatusBarHeight(activity);if (rootView instanceof CoordinatorLayout) {final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView;if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {coordinatorLayout.setFitsSystemWindows(false);contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight;if (isNeedRequestLayout) {contentView.setPadding(0, statusBarHeight, 0, 0);coordinatorLayout.post(new Runnable() {@Overridepublic void run() {coordinatorLayout.requestLayout();}});}} else {coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha));}} else {contentView.setPadding(0, statusBarHeight, 0, 0);contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));}setTransparentForWindow(activity);}}/*** 设置状态栏纯色 不加半透明效果** @param activity 需要设置的 activity* @param color    状态栏颜色值*/public static void setColorNoTranslucent(Activity activity, @ColorInt int color) {setColor(activity, color, 0);}/*** 设置状态栏颜色(5.0以下无半透明效果,不建议使用)** @param activity 需要设置的 activity* @param color    状态栏颜色值*/@Deprecatedpublic static void setColorDiff(Activity activity, @ColorInt int color) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}transparentStatusBar(activity);ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);// 移除半透明矩形,以免叠加View fakeStatusBarView = contentView.findViewById(FAKE_STATUS_BAR_VIEW_ID);if (fakeStatusBarView != null) {if (fakeStatusBarView.getVisibility() == View.GONE) {fakeStatusBarView.setVisibility(View.VISIBLE);}fakeStatusBarView.setBackgroundColor(color);} else {contentView.addView(createStatusBarView(activity, color));}setRootView(activity);}/*** 使状态栏半透明* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @param activity 需要设置的activity*/public static void setTranslucent(Activity activity) {setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA);}/*** 使状态栏半透明* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @param activity       需要设置的activity* @param statusBarAlpha 状态栏透明度*/public static void setTranslucent(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}setTransparent(activity);addTranslucentView(activity, statusBarAlpha);}/*** 针对根布局是 CoordinatorLayout, 使状态栏半透明* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @param activity       需要设置的activity* @param statusBarAlpha 状态栏透明度*/public static void setTranslucentForCoordinatorLayout(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}transparentStatusBar(activity);addTranslucentView(activity, statusBarAlpha);}/*** 设置状态栏全透明* @param activity 需要设置的activity*/public static void setTransparent(Activity activity) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}transparentStatusBar(activity);setRootView(activity);}/*** 使状态栏透明(5.0以上半透明效果,不建议使用)* 适用于图片作为背景的界面,此时需要图片填充到状态栏* @param activity 需要设置的activity*/@Deprecatedpublic static void setTranslucentDiff(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 设置状态栏透明activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);setRootView(activity);}}/*** 为DrawerLayout 布局设置状态栏变色* @param activity     需要设置的activity* @param drawerLayout DrawerLayout* @param color        状态栏颜色值*/public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA);}/*** 为DrawerLayout 布局设置状态栏颜色,纯色* @param activity     需要设置的activity* @param drawerLayout DrawerLayout* @param color        状态栏颜色值*/public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {setColorForDrawerLayout(activity, drawerLayout, color, 0);}/*** 为DrawerLayout 布局设置状态栏变色* @param activity       需要设置的activity* @param drawerLayout   DrawerLayout* @param color          状态栏颜色值* @param statusBarAlpha 状态栏透明度*/public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color,@IntRange(from = 0, to = 255) int statusBarAlpha) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().setStatusBarColor(Color.TRANSPARENT);} else {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}// 生成一个状态栏大小的矩形// ic_option_add statusBarView 到布局中ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID);if (fakeStatusBarView != null) {if (fakeStatusBarView.getVisibility() == View.GONE) {fakeStatusBarView.setVisibility(View.VISIBLE);}fakeStatusBarView.setBackgroundColor(color);} else {contentLayout.addView(createStatusBarView(activity, color), 0);}// 内容布局不是 LinearLayout 时,设置padding topif (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {contentLayout.getChildAt(1).setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(),contentLayout.getPaddingRight(), contentLayout.getPaddingBottom());}// 设置属性setDrawerLayoutProperty(drawerLayout, contentLayout);addTranslucentView(activity, statusBarAlpha);}/*** 设置 DrawerLayout 属性* @param drawerLayout              DrawerLayout* @param drawerLayoutContentLayout DrawerLayout 的内容布局*/private static void setDrawerLayoutProperty(DrawerLayout drawerLayout, ViewGroup drawerLayoutContentLayout) {ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1);drawerLayout.setFitsSystemWindows(false);drawerLayoutContentLayout.setFitsSystemWindows(false);drawerLayoutContentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);}/*** 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用)* @param activity     需要设置的activity* @param drawerLayout DrawerLayout* @param color        状态栏颜色值*/@Deprecatedpublic static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 生成一个状态栏大小的矩形ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID);if (fakeStatusBarView != null) {if (fakeStatusBarView.getVisibility() == View.GONE) {fakeStatusBarView.setVisibility(View.VISIBLE);}fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA));} else {// ic_option_add statusBarView 到布局中contentLayout.addView(createStatusBarView(activity, color), 0);}// 内容布局不是 LinearLayout 时,设置padding topif (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0);}// 设置属性setDrawerLayoutProperty(drawerLayout, contentLayout);}}/*** 为 DrawerLayout 布局设置状态栏透明* @param activity     需要设置的activity* @param drawerLayout DrawerLayout*/public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA);}/*** 为 DrawerLayout 布局设置状态栏透明* @param activity     需要设置的activity* @param drawerLayout DrawerLayout*/public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout,@IntRange(from = 0, to = 255) int statusBarAlpha) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}setTransparentForDrawerLayout(activity, drawerLayout);addTranslucentView(activity, statusBarAlpha);}/*** 为 DrawerLayout 布局设置状态栏透明* @param activity     需要设置的activity* @param drawerLayout DrawerLayout*/public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().setStatusBarColor(Color.TRANSPARENT);} else {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);// 内容布局不是 LinearLayout 时,设置padding topif (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0);}// 设置属性setDrawerLayoutProperty(drawerLayout, contentLayout);}/*** 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用)* @param activity     需要设置的activity* @param drawerLayout DrawerLayout*/@Deprecatedpublic static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 设置状态栏透明activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 设置内容布局属性ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);contentLayout.setFitsSystemWindows(true);contentLayout.setClipToPadding(true);// 设置抽屉布局属性ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1);vg.setFitsSystemWindows(false);// 设置 DrawerLayout 属性drawerLayout.setFitsSystemWindows(false);}}/*** 为头部是 ImageView 的界面设置状态栏全透明* @param activity       需要设置的activity* @param needOffsetView 需要向下偏移的 View*/public static void setTransparentForImageView(Activity activity, View needOffsetView) {setTranslucentForImageView(activity, 0, needOffsetView);}/*** 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度)* @param activity       需要设置的activity* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageView(Activity activity, View needOffsetView) {setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);}/*** 为头部是 ImageView 的界面设置状态栏透明* @param activity       需要设置的activity* @param statusBarAlpha 状态栏透明度* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha,View needOffsetView) {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return;}setTransparentForWindow(activity);addTranslucentView(activity, statusBarAlpha);if (needOffsetView != null) {Object haveSetOffset = needOffsetView.getTag(TAG_KEY_HAVE_SET_OFFSET);if (haveSetOffset != null && (Boolean) haveSetOffset) {return;}ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity),layoutParams.rightMargin, layoutParams.bottomMargin);needOffsetView.setTag(TAG_KEY_HAVE_SET_OFFSET, true);}}/*** 为 fragment 头部是 ImageView 的设置状态栏透明* @param activity       fragment 对应的 activity* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) {setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);}/*** 为 fragment 头部是 ImageView 的设置状态栏透明* @param activity       fragment 对应的 activity* @param needOffsetView 需要向下偏移的 View*/public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) {setTranslucentForImageViewInFragment(activity, 0, needOffsetView);}/*** 为 fragment 头部是 ImageView 的设置状态栏透明* @param activity       fragment 对应的 activity* @param statusBarAlpha 状态栏透明度* @param needOffsetView 需要向下偏移的 View*/public static void setTranslucentForImageViewInFragment(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha,View needOffsetView) {setTranslucentForImageView(activity, statusBarAlpha, needOffsetView);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {clearPreviousSetting(activity);}}/*** 隐藏伪状态栏 View* @param activity 调用的 Activity*/public static void hideFakeStatusBarView(Activity activity) {ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);if (fakeStatusBarView != null) {fakeStatusBarView.setVisibility(View.GONE);}View fakeTranslucentView = decorView.findViewById(FAKE_TRANSLUCENT_VIEW_ID);if (fakeTranslucentView != null) {fakeTranslucentView.setVisibility(View.GONE);}}@TargetApi(Build.VERSION_CODES.KITKAT)private static void clearPreviousSetting(Activity activity) {ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);if (fakeStatusBarView != null) {decorView.removeView(fakeStatusBarView);ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);rootView.setPadding(0, 0, 0, 0);}}/*** 添加半透明矩形条* @param activity       需要设置的 activity* @param statusBarAlpha 透明值*/private static void addTranslucentView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID);if (fakeTranslucentView != null) {if (fakeTranslucentView.getVisibility() == View.GONE) {fakeTranslucentView.setVisibility(View.VISIBLE);}fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0));} else {contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha));}}/*** 生成一个和状态栏大小相同的彩色矩形条* @param activity 需要设置的 activity* @param color    状态栏颜色值* @return 状态栏矩形条*/private static View createStatusBarView(Activity activity, @ColorInt int color) {return createStatusBarView(activity, color, 0);}/*** 生成一个和状态栏大小相同的半透明矩形条* @param activity 需要设置的activity* @param color    状态栏颜色值* @param alpha    透明值* @return 状态栏矩形条*/private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) {// 绘制一个和状态栏一样高的矩形View statusBarView = new View(activity);LinearLayout.LayoutParams params =new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);return statusBarView;}/*** 设置根布局参数*/private static void setRootView(Activity activity) {ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content);for (int i = 0, count = parent.getChildCount(); i < count; i++) {View childView = parent.getChildAt(i);if (childView instanceof ViewGroup) {childView.setFitsSystemWindows(true);((ViewGroup) childView).setClipToPadding(true);}}}/*** 设置透明*/private static void setTransparentForWindow(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().setStatusBarColor(Color.TRANSPARENT);activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}}/*** 使状态栏透明*/@TargetApi(Build.VERSION_CODES.KITKAT)private static void transparentStatusBar(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);activity.getWindow().setStatusBarColor(Color.TRANSPARENT);} else {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}}/*** 创建半透明矩形 View* @param alpha 透明值* @return 半透明 View*/private static View createTranslucentStatusBarView(Activity activity, int alpha) {// 绘制一个和状态栏一样高的矩形View statusBarView = new View(activity);LinearLayout.LayoutParams params =new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID);return statusBarView;}/*** 获取状态栏高度* @param context context* @return 状态栏高度*/public static int getStatusBarHeight(Context context) {// 获得状态栏高度int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");return context.getResources().getDimensionPixelSize(resourceId);}/*** 计算状态栏颜色* @param color color值* @param alpha alpha值* @return 最终的状态栏颜色*/private static int calculateStatusColor(@ColorInt int color, int alpha) {if (alpha == 0) {return color;}float a = 1 - alpha / 255f;int red = color >> 16 & 0xff;int green = color >> 8 & 0xff;int blue = color & 0xff;red = (int) (red * a + 0.5);green = (int) (green * a + 0.5);blue = (int) (blue * a + 0.5);return 0xff << 24 | red << 16 | green << 8 | blue;}
}

  新增values.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<resources><item name="statusbarutil_fake_status_bar_view" type="id"/><item name="statusbarutil_translucent_view" type="id"/>
</resources>

防恶意多点工具类

public class ClickUtil {//两次按钮点击时间间隔不能少于1sprivate static final long MIN_CLICK_DELAY_TIME = 800L;private static long lastClickTime;public static boolean enableClick() {boolean isEnabled = false;long curClickTime = System.currentTimeMillis();if ((curClickTime - lastClickTime) > MIN_CLICK_DELAY_TIME) {isEnabled = true;}lastClickTime = curClickTime;return isEnabled;}}

CrashHandler用于异常处理上报日志

/*** @ClassName: CrashHandler* @Author: Tobey_r1* @CreateDate: 2022/2/16 10:26* @Description: UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.需要在Application中注册,为了要在程序启动器就监控整个程序。* @UpdateUser: 更新者* @UpdateDate: 2022/2/16 10:26* @UpdateRemark: 更新说明* @Version: 1.0*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {public static final String TAG = CrashHandler.class.getSimpleName();//系统默认的UncaughtException处理类private Thread.UncaughtExceptionHandler mDefaultHandler;//CrashHandler 实例private static CrashHandler instance;private Context mContext;//用来存储设备信息和异常信息private Map<String,String> infos = new HashMap<>();//用于格式化日期,作为日志文件名的一部分@SuppressLint("SimpleDateFormat")private DateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");private CrashHandler(){}/*** @description 单例* @return 返回crashhandler实例* @author 13115* @time 2022/2/16 10:54*/public static CrashHandler getInstance(){if (instance == null) {synchronized (CrashHandler.class){if (instance == null){instance = new CrashHandler();}}}return instance;}/*** @description 初始化* @param context context* @author 13115* @time 2022/2/16 11:14*/public void init(Context context){mContext = context;//获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();//设置该CrashHandler微程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this);}/*** @description 当UncaughtException发生时会转入该函数来处理* @return* @author 13115* @time 2022/2/16 10:40*/@Overridepublic void uncaughtException(@NonNull  Thread t, @NonNull  Throwable e) {if (!handleException(e)&&mDefaultHandler !=null){//如果用户没有处理或者init则让系统默认的异常处理器处理mDefaultHandler.uncaughtException(t, e);}else {try {Thread.sleep(3000);} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}//退出程序System.exit(1);}}/*** @description 自定义错误处理,收集错误信息,上传错误信息* @param ex* @return true:处理了该异常信息;false:未处理* @author 13115* @time 2022/2/16 11:22*/private boolean handleException(final Throwable ex){if (ex == null) return false;//收集设备参数信息collectDeviceInfo(mContext);//发送错误日志//crashReport(ex);//Toast提示new Thread(){@Overridepublic void run() {Looper.prepare();Toast.makeText(mContext,ex==null?"":ex.toString(),Toast.LENGTH_SHORT).show();Looper.loop();Looper.myLooper().quit();}}.start();return true;}private void collectDeviceInfo(Context mContext) {try {PackageManager pm = mContext.getPackageManager();PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),PackageManager.GET_ACTIVITIES);if (pi != null) {String versionName = pi.versionName == null ? "null": pi.versionName;String versionCode = pi.versionCode + "";infos.put("versionName", versionName);infos.put("versionCode", versionCode);}} catch (PackageManager.NameNotFoundException e) {}infos.put("OS Version:", Build.VERSION.RELEASE);infos.put("-", Build.VERSION.SDK_INT+"");infos.put("Model:", Build.MODEL);infos.put("BRAND:", Build.BRAND);}
}

  看了下markdown字数,2.6w字,所以本篇先记录到这,后续文章重构ui以及http模块。有问题欢迎批评指正,觉得不错的也请点个赞谢谢。

Android简易音乐重构MVVM Java版 -搭建项目(八)相关推荐

  1. Android简易音乐重构MVVM Java版-新增推荐菜单及侧边栏展示(十二)

    Android简易音乐重构MVVM Java版-新增推荐菜单及侧边栏展示(十二) 关于 效果图 添加侧边栏 添加推荐歌单 新增RecommendAdapter适配器 修改DiscoverFragmen ...

  2. Android简易音乐重构MVVM Java版-新增推荐雷达歌单及重构首页(十三)

    Android简易音乐重构MVVM Java版-新增推荐雷达歌单及重构首页(十三) 关于 效果图 修改ApiService 添加HomeDiscoverEntity实体类 添加BannerExtInf ...

  3. Android简易音乐重构MVVM Java版-新增歌曲播放界面+状态栏黑科技(十七)

    Android简易音乐重构MVVM Java版-新增歌曲播放界面(十七) 关于 效果 新增歌曲播放界面 增加歌词view `LyricView` 修改ApiService 添加引用 添加SongPla ...

  4. Android简易音乐重构MVVM Java版-新增推荐、雷达歌单详情列表界面(十八)

    Android简易音乐重构MVVM Java版-新增推荐.雷达歌单详情列表界面(十八) 关于 效果 修改ApiService 增加歌单列表实体类RecommendListEntity 新增歌单列表界面 ...

  5. java版QQ项目 源代码共享

    最近总有人联系我要java 版QQ源代码,当然你想学习,我不会拒绝给你,请拿到源代码的朋友,认真阅读一下,同时我想对要源代码的朋友提出几点要求,希望大家认真看一下: 1.我写的这个QQ虽然不是很完善, ...

  6. Android简易音乐播放器

    1.制作一个简易的音乐播放器 使用软件:Android studio + jdk1.8 + Gradle6.5(其他版本也可以) 2.activity_main.xml文件(主页面的编写) 先看一下示 ...

  7. Android简易音乐播放器实现代码

    本文实例为大家分享了Android音乐播放器的具体代码,供大家参考,具体内容如下 1.播放项目内的音乐 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1 ...

  8. 我的世界服务端java_Minecraft java版搭建服务端

    1.在D盘新建文件夹 2.下载服务端,根据服务端的版本下载服务端 下载服务端途径:官方启动器打开后新建版本,对应版本有对应服务端. https://mcversions.net/ 这个网站有收录大部分 ...

  9. 最全的Android开源音乐播放器源码汇总

    收集了很多音乐播放器类的Android项目源码,非常不错的开源项目,会让你事半功倍,希望大家补充...谢谢! Android基于经纬度切歌的冲绳音乐播放器源码 http://neast.cn/foru ...

最新文章

  1. 安装计算机过程中的注意事项,西门子step7安装过程中的注意事项
  2. 微软 AI 设计原则:成为弱者,再带来惊喜
  3. android 添加附件功能,Android实现带附件的邮件发送功能
  4. php expat+DOM+SimpleXML XML读取
  5. [雪峰磁针石博客]大数据Hadoop工具python教程9-Luigi工作流...
  6. SAP ABAP实用技巧介绍系列之 ABAP XSLT 使用attribute增加新的属性
  7. JS编程建议——52:建议使用splice删除数组
  8. 百度360之争的背后
  9. keep-alive + vuex + mint + Infinite scroll 保存分页列表数据
  10. java里函数式表达式_java8入门(lambda表达式、函数式接口相关)
  11. java gbk文件转utf8_java 将GBK编码文件转为UTF-8编码
  12. mysql decimal 18 2_sql语句 decimal(18,0)什么意思
  13. 浙江大学14届计算机学院孙晓宇,郑州外国语学校2011年保送生录取名单
  14. 凭什么软件测试入门就有一万+工资,为什么?我不相信。
  15. 201771010112罗松《面向对象程序设计(java)》第六周学习总结
  16. 001-三阶魔方-概述及层先法
  17. 【博学谷学习记录】超强总结,用心分享 | 【Java】自定义异常
  18. 自己做量化交易软件(9通通量化框架的雏形建立
  19. 网吧计算机系统 影子,影子系统怎么用?实现像网吧电脑一样重启后自动还原系统教程(2)...
  20. 【Web技术】1161- 你不知道的前端音视频知识

热门文章

  1. 教师招聘说课视频初中计算机,教师资格证面试说课备考—初中计算机说课稿《工作表》...
  2. iPhoneX设计稿适配Android,设计干货:iPhone X APP UI设计尺寸和适配【完整版】
  3. Unity 环境变量设置
  4. BUUCTF做题Upload-Labs记录pass-11~pass-20
  5. 金融评测指标empyrical库详解Sortino、calmar、omega、sharpe、annual_return、max_drawdown
  6. 反向代理和正向代理以及Nginx工具的简要使用
  7. matlab习题 —— 数据的基本处理
  8. 大数据培训出来有哪些就业方向
  9. Unity读取点云数据
  10. 如何在网上赚钱?80%的人都缺乏的赚钱思维