感谢开源,感谢大神,才让我们这些菜鸟成长!
附上云阅开源项目地址:点我吧。
1.轮播图的实现。
现在的APP基本都会实现这个功能吧,然后一直都找不到好的第三方库,能够满足各种需求。然而碰到了这个开源库...
- gradle配置: implementation 'com.youth.banner:banner:1.4.9'
- github地址: https://github.com/youth5201314/banner
- 参考文章: Android-图片轮播(banner)
2.MVVM-DataBinding架构开发。
它本质上就是MVC、MVP的改进版。实现数据和视图相互绑定。MVVM就是讲其中的View的状态和行为抽象化,让我们将视图UI和业务逻辑分开。
- gradle配置: (在module的build.gradle中的android模块中加入如下代码块)
dataBinding {enabled =
true} - 官方地址: https://developer.android.google.cn/topic/libraries/data-binding/index.html
- 中文翻译地址: https://blog.csdn.net/jjwwmlp456/article/details/54915981
- 参考文章: https://www.jianshu.com/p/ba4982be30f8
3.解决Bug:Android点击应用图标后会重新进入启动页。
- 问题描述:发现应用打包安装后按home键切换到后台后,点击应用图标又重新打开了一个,并没有回到原来的界面。
- 解决方案与参考文章:https://blog.csdn.net/jianiuqi/article/details/54091181
4.第三方库Glide的简单使用。
implementation
'com.github.bumptech.glide:glide:4.7.1' - Glide参考文章:https://mrfu.me/2016/02/27/Glide_Getting_Started/
- Glide的github地址:https://github.com/bumptech/glide
- 处理图片效果的Glide库,如果选择这个第三方库,则无需添加glide,它里面自带就有glide
implementation
'jp.wasabeef:glide-transformations:2.0.1 - 处理图片效果的github地址:https://github.com/wasabeef/picasso-transformations
- 图片处理参考文章:https://www.jianshu.com/p/976c86fa72bc
5.分包策略,解决65535方法限制的问题。
implementation
'com.android.support:multidex:1.0.3' - 参考文章:https://blog.csdn.net/djy1992/article/details/51162013
- 官网文档:https://developer.android.com/tools/building/multidex.html#about
6.避免在1s中多次点击按钮。
方法:自己新建一个抽象类,集成OnClickListener,重写里面的onClick函数。
public abstract class PerfectClickListener
implements OnClickListener {public static final int MIN_CLICK_DELAY_TIME = 1000
;private long lastClickTime = 0
;private int id = -1
;@Overridepublic void onClick(View v) {long currentTime =
Calendar.getInstance().getTimeInMillis();int mId =
v.getId();if (id !=
mId) {id =
mId;lastClickTime =
currentTime;onNoDoubleClick(v);return;}if (currentTime - lastClickTime >
MIN_CLICK_DELAY_TIME) {lastClickTime =
currentTime;onNoDoubleClick(v);}}protected abstract void onNoDoubleClick(View v);
} View Code 使用场景:在启动页中,有一个跳转按钮,避免用户多次点击启动多个主页。
7.如何使用android.support.v7.widget.Toolbar
implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
implementation "com.android.support:design:$rootProject.supportLibraryVersion"
implementation "com.android.support:support-v4:$rootProject.supportLibraryVersion"
implementation "com.android.support:cardview-v7:$rootProject.supportLibraryVersion"
这里使用了rootProject来间接设置,注意这个rootProject要在项目的build.gradle中具体配置。具体百度吧。
<
android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorTheme"
app:contentInsetStart="0.0dp"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ToolbarStyle">
这里使用了自定义主题,在style文件中自己创建一个。
- 对于popupTheme具体含义参考这篇文章:https://blog.csdn.net/lovexieyuan520/article/details/48931185
- 具体使用方法参考这篇文章:https://blog.csdn.net/javazejian/article/details/50451853
8.设置主题点击波纹效果。
- 首先得引入谷歌的材料设计库,上面第7点中有相关引入方式。
- 然后再布局文件中:android:background="?attr/...." (这里省略号写一些波纹样式即可)
- 参考文章:https://yq.aliyun.com/articles/12407
9.左右滑动ViewPager时,没获取焦点没响应。
原来仅仅是在定义Viewpager的时候,添加如下代码即可。
android:descendantFocusability="blocksDescendants"
- 参考文章:https://www.cnblogs.com/eyu8874521/archive/2012/10/17/2727882.html
- 参数意义:
beforeDescendants:viewgroup会优先其子类控件而获取到焦点afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
10.设置状态栏颜色以及其他方法。
- 首先自定义一个视图叫做StatusBarView。
public class StatusBarView
extends View {public StatusBarView(Context context, AttributeSet attrs) {super(context, attrs);}public StatusBarView(Context context) {super(context);}
} public class StatusBarUtil {public static final int DEFAULT_STATUS_BAR_ALPHA = 112
;/*** 设置状态栏颜色** @param activity 需要设置的 activity* @param color 状态栏颜色值*/public static void setColor(Activity activity, @ColorInt
int color) {setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA);}/*** 设置状态栏颜色** @param activity 需要设置的activity* @param color 状态栏颜色值* @param statusBarAlpha 状态栏透明度*/public static void setColor(Activity activity, @ColorInt
int color,
int statusBarAlpha) {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));} else if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.KITKAT) {activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);ViewGroup decorView =
(ViewGroup) activity.getWindow().getDecorView();int count =
decorView.getChildCount();if (count > 0 && decorView.getChildAt(count - 1)
instanceof StatusBarView) {decorView.getChildAt(count - 1
).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));} else {StatusBarView statusView =
createStatusBarView(activity, color, statusBarAlpha);decorView.addView(statusView);}setRootView(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;}activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);// 生成一个状态栏大小的矩形ViewGroup decorView =
(ViewGroup) activity.getWindow().getDecorView();int count =
decorView.getChildCount();if (count > 0 && decorView.getChildAt(count - 1)
instanceof StatusBarView) {decorView.getChildAt(count - 1
).setBackgroundColor(color);} else {StatusBarView statusView =
createStatusBarView(activity, color);decorView.addView(statusView);}setRootView(activity);}/*** 使状态栏半透明* <p>* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @param activity 需要设置的activity*/public static void setTranslucent(Activity activity) {setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA);}/*** 使状态栏半透明* <p>* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @param activity 需要设置的activity* @param statusBarAlpha 状态栏透明度*/public static void setTranslucent(Activity activity,
int statusBarAlpha) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}setTransparent(activity);addTranslucentView(activity, statusBarAlpha);}/*** 针对根布局是 CoordinatorLayout, 使状态栏半透明* <p>* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @param activity 需要设置的activity* @param statusBarAlpha 状态栏透明度*/public static void setTranslucentForCoordinatorLayout(Activity activity,
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以上半透明效果,不建议使用)* <p>* 适用于图片作为背景的界面,此时需要图片填充到状态栏** @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,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);}// 生成一个状态栏大小的矩形// 添加 statusBarView 到布局中ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0
);if (contentLayout.getChildCount() > 0 && contentLayout.getChildAt(0)
instanceof StatusBarView) {contentLayout.getChildAt(0
).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));} else {StatusBarView statusBarView =
createStatusBarView(activity, color);contentLayout.addView(statusBarView, 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());}// 设置属性ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1
);drawerLayout.setFitsSystemWindows(false);contentLayout.setFitsSystemWindows(false);contentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);addTranslucentView(activity, statusBarAlpha);}/*** 为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
);if (contentLayout.getChildCount() > 0 && contentLayout.getChildAt(0)
instanceof StatusBarView) {contentLayout.getChildAt(0
).setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA));} else {// 添加 statusBarView 到布局中StatusBarView statusBarView =
createStatusBarView(activity, color);contentLayout.addView(statusBarView, 0
);}// 内容布局不是 LinearLayout 时,设置padding topif (!(contentLayout
instanceof LinearLayout) && contentLayout.getChildAt(1) !=
null) {contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0
);}// 设置属性ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1
);drawerLayout.setFitsSystemWindows(false);contentLayout.setFitsSystemWindows(false);contentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);}}/*** 为 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,
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
);}// 设置属性ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1
);drawerLayout.setFitsSystemWindows(false);contentLayout.setFitsSystemWindows(false);contentLayout.setClipToPadding(true);drawer.setFitsSystemWindows(false);}/*** 为 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,
int statusBarAlpha, View needOffsetView) {if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.KITKAT) {return;}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);if (activity
instanceof TabActivity){activity.getWindow()//兼容TabActivity
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}} else {activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}addTranslucentView(activity, statusBarAlpha);if (needOffsetView !=
null) {ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();if (layoutParams !=
null) {layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0
);}}}public static void setMargin(Activity activity, View needOffsetView) {if (needOffsetView !=
null) {ViewGroup.MarginLayoutParams layoutParams =
(ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();if (layoutParams !=
null) {layoutParams.setMargins(0, getStatusBarHeight(activity), 0, 0
);}}}/*** 为 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,
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);}}@TargetApi(Build.VERSION_CODES.KITKAT)private static void clearPreviousSetting(Activity activity) {ViewGroup decorView =
(ViewGroup) activity.getWindow().getDecorView();int count =
decorView.getChildCount();if (count > 0 && decorView.getChildAt(count - 1)
instanceof StatusBarView) {decorView.removeViewAt(count - 1
);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,
int statusBarAlpha) {ViewGroup contentView =
(ViewGroup) activity.findViewById(android.R.id.content);if (contentView.getChildCount() > 1
) {contentView.getChildAt(1).setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0
));} else {contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha));}}/*** 生成一个和状态栏大小相同的彩色矩形条** @param activity 需要设置的 activity* @param color 状态栏颜色值* @return 状态栏矩形条*/private static StatusBarView createStatusBarView(Activity activity, @ColorInt
int color) {// 绘制一个和状态栏一样高的矩形StatusBarView statusBarView =
new StatusBarView(activity);LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(color);return statusBarView;}/*** 生成一个和状态栏大小相同的半透明矩形条** @param activity 需要设置的activity* @param color 状态栏颜色值* @param alpha 透明值* @return 状态栏矩形条*/private static StatusBarView createStatusBarView(Activity activity, @ColorInt
int color,
int alpha) {// 绘制一个和状态栏一样高的矩形StatusBarView statusBarView =
new StatusBarView(activity);LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));return statusBarView;}/*** 设置根布局参数*/private static void setRootView(Activity activity) {ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0
);rootView.setFitsSystemWindows(true);rootView.setClipToPadding(true);}/*** 使状态栏透明*/@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 StatusBarView createTranslucentStatusBarView(Activity activity,
int alpha) {// 绘制一个和状态栏一样高的矩形StatusBarView statusBarView =
new StatusBarView(activity);LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));statusBarView.setLayoutParams(params);statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0
));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) {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;}} View Code - 简单用法:根据自己需求在需要的地方,StatusBarUtil.set相关的方法即可。
11.常见的item点击的效果样式文件。
长按或者点击一个TextView之后,背景颜色更改,一般都是初始为白色,长按后显示灰色。颜色值也是很讲究的。
有点类似微信页面每一个item点击的效果,一直调不出那样的效果。主要是找不到合适的颜色。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_enabled="false"><shape><solid android:color="#F1F3F4" /></shape></item><item android:state_pressed="true"><shape><solid android:color="#d9d9d9" /></shape></item><item android:state_pressed="false"><shape><solid android:color="#F1F3F4" /></shape></item></selector>
12.用SharePerferences文件来记录全局配置。
可以写一个通用类,用来封装不同数据类型写入文件,从文件中获取数据的方法。
public class SPUtils {private static final String CONFIG = "config"
;/*** 获取SharedPreferences实例对象** @param fileName*/private static SharedPreferences getSharedPreference(String fileName) {return MyApplication.getInstance().getSharedPreferences(fileName, Context.MODE_PRIVATE);}/*** 保存一个String类型的值!*/public static void putString(String key, String value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putString(key, value).apply();}/*** 获取String的value*/public static String getString(String key, String defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getString(key, defValue);}/*** 保存一个Boolean类型的值!*/public static void putBoolean(String key, Boolean value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putBoolean(key, value).apply();}/*** 获取boolean的value*/public static boolean getBoolean(String key, Boolean defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getBoolean(key, defValue);}/*** 保存一个int类型的值!*/public static void putInt(String key,
int value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putInt(key, value).apply();}/*** 获取int的value*/public static int getInt(String key,
int defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getInt(key, defValue);}/*** 保存一个float类型的值!*/public static void putFloat(String fileName, String key,
float value) {SharedPreferences.Editor editor =
getSharedPreference(fileName).edit();editor.putFloat(key, value).apply();}/*** 获取float的value*/public static float getFloat(String key, Float defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getFloat(key, defValue);}/*** 保存一个long类型的值!*/public static void putLong(String key,
long value) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.putLong(key, value).apply();}/*** 获取long的value*/public static long getLong(String key,
long defValue) {SharedPreferences sharedPreference =
getSharedPreference(CONFIG);return sharedPreference.getLong(key, defValue);}/*** 取出List<String>** @param key List<String> 对应的key* @return List<String>*/public static List<String>
getStrListValue(String key) {List<String> strList =
new ArrayList<String>
();int size = getInt(key + "size", 0
);//Log.d("sp", "" + size);for (
int i = 0; i < size; i++
) {strList.add(getString(key + i,
null));}return strList;}/*** 存储List<String>* @param key List<String>对应的key* @param strList 对应需要存储的List<String>*/public static void putStrListValue(String key, List<String>
strList) {if (
null ==
strList) {return;}// 保存之前先清理已经存在的数据,保证数据的唯一性
removeStrList(key);int size =
strList.size();putInt(key + "size"
, size);for (
int i = 0; i < size; i++
) {putString(key +
i, strList.get(i));}}/*** 清空List<String>所有数据** @param key List<String>对应的key*/public static void removeStrList(String key) {int size = getInt(key + "size", 0
);if (0 ==
size) {return;}remove(key + "size"
);for (
int i = 0; i < size; i++
) {remove(key +
i);}}/*** 清空对应key数据*/public static void remove(String key) {SharedPreferences.Editor editor =
getSharedPreference(CONFIG).edit();editor.remove(key).apply();}} View Code 另外,可以根据自己的需求,灵活地在文件中增加一些具体的方法。比如存储APP的夜间模式或者日间模式,是否登录等。
13.利用Glide画圆角图。
- 首先在module的build.gradle中添加依赖,前面第4点有讲过。
- 然后再自定义一个转换器,类似于下面这样。
public class GlideCircleTransform
extends BitmapTransformation {public GlideCircleTransform(Context context) {super(context);}@Overrideprotected Bitmap transform(BitmapPool pool, Bitmap toTransform,
int outWidth,
int outHeight) {return circleCrop(pool, toTransform);}private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {if (source ==
null)
return null;int size =
Math.min(source.getWidth(), source.getHeight());int x = (source.getWidth() - size) / 2
;int y = (source.getHeight() - size) / 2
;// TODO this could be acquired from the pool tooBitmap squared =
Bitmap.createBitmap(source, x, y, size, size);Bitmap result =
pool.get(size, size, Bitmap.Config.ARGB_8888);if (result ==
null) {result =
Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);}Canvas canvas =
new Canvas(result);Paint paint =
new Paint();paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));paint.setAntiAlias(true);float r = size /
2f;canvas.drawCircle(r, r, r, paint);return result;}@Overridepublic String getId() {return getClass().getName();}
} View Code - 然后就是Glide调用这个转换器了,类似于下面这样。
public static void displayCircle(ImageView imageView, String imageUrl) {Glide.with(imageView.getContext()).load(imageUrl).crossFade(500
).error(R.drawable.ic_avatar_default).transform(new GlideCircleTransform(imageView.getContext())).into(imageView);}
14.学会添加library。
这里添加library不是在build.gradle中添加依赖,也不是在工程中添加libs文件。而是导入一个Module。
其实是一个主项目需要实现某些功能,然后将这部分功能划分开来,最后将这部分功能集成到主项目中,也是是模块的划分,所以这里称为module。
这样的好处是:可以再主module中任何地方引用子module的图片资源,代码资源等等。
现在有一个地方不是特别理解,就是如何将多个library统一放在一个文件夹下面,这样方便管理,不然都不知道哪个是主项目了。
添加单个library参考文章:https://blog.csdn.net/u014772414/article/details/51194952
15.学会定义BaseFragment。
主要抓住一下要点吧。
- 如果网络异常,则页面应该怎么显示。
- 如果正在加载中,则页面应该怎么显示。
- 如何动态添加自己想要的布局。
所以在布局方面我们可以这样布局。
<
RelativeLayoutandroid:id=
"@+id/container"android:layout_width=
"match_parent"android:layout_height=
"match_parent"><!--加载失败--><
LinearLayoutandroid:id=
"@+id/ll_error_refresh"android:layout_width=
"match_parent"android:layout_height=
"match_parent"android:gravity=
"center"android:orientation=
"vertical"android:visibility=
"gone"><
ImageViewandroid:id=
"@+id/img_err"android:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:src=
"@drawable/load_err" /><
TextViewandroid:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:layout_marginTop=
"15dp"android:text=
"加载失败,点击重试"android:textSize=
"14sp" /></LinearLayout><!--加载中..--><
LinearLayoutandroid:id=
"@+id/ll_progress_bar"android:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:layout_centerHorizontal=
"true"android:layout_marginTop=
"80dp"android:gravity=
"center_vertical"><
ImageViewandroid:id=
"@+id/img_progress"android:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:src=
"@drawable/yun_anim" /><
TextViewandroid:layout_width=
"wrap_content"android:layout_height=
"wrap_content"android:layout_marginLeft=
"10dp"android:text=
"努力加载中..."android:textColor=
"@color/colorTabText"android:textSize=
"14sp" /></LinearLayout></RelativeLayout>
View Code 然后再BaseFragment中,这样决定怎么隐藏怎么显示。
public abstract class BaseFragment<SV
extends ViewDataBinding>
extends Fragment {protected SV bindingView;
//布局viewprotected boolean mIsVisible=
false;
//fragment是否显示了private LinearLayout mLlProgressBar;
//加载中private LinearLayout mRefresh;
//加载失败protected RelativeLayout mContainer;
//内容布局private AnimationDrawable mAnimationDrawable;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View ll = inflater.inflate(R.layout.fragment_base,
null);bindingView =
DataBindingUtil.inflate(getActivity().getLayoutInflater(),setContent(), null,
false);RelativeLayout.LayoutParams params =
new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);bindingView.getRoot().setLayoutParams(params);mContainer =
ll.findViewById(R.id.container);mContainer.addView(bindingView.getRoot());//动态替换成自己想要的布局return ll;}@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);mLlProgressBar =
getView(R.id.ll_progress_bar);ImageView img =
getView(R.id.img_progress);// 加载动画mAnimationDrawable =
(AnimationDrawable) img.getDrawable();// 默认进入页面就开启动画if (!
mAnimationDrawable.isRunning()) {mAnimationDrawable.start();}mRefresh =
getView(R.id.ll_error_refresh);// 点击加载失败布局mRefresh.setOnClickListener(
new PerfectClickListener() {@Overrideprotected void onNoDoubleClick(View v) {showLoading();onRefresh();}});bindingView.getRoot().setVisibility(View.GONE);}protected <T
extends View> T getView(
int id) {return (T) getView().findViewById(id);}/*** 加载失败后点击后的操作*/protected void onRefresh() {}/*** 显示加载中状态*/protected void showLoading() {if (mLlProgressBar.getVisibility() !=
View.VISIBLE) {mLlProgressBar.setVisibility(View.VISIBLE);}// 开始动画if (!
mAnimationDrawable.isRunning()) {mAnimationDrawable.start();}if (bindingView.getRoot().getVisibility() !=
View.GONE) {bindingView.getRoot().setVisibility(View.GONE);}if (mRefresh.getVisibility() !=
View.GONE) {mRefresh.setVisibility(View.GONE);}}/*** 加载完成的状态*/protected void showContentView() {if (mLlProgressBar.getVisibility() !=
View.GONE) {mLlProgressBar.setVisibility(View.GONE);}// 停止动画if (mAnimationDrawable.isRunning()) {mAnimationDrawable.stop();}if (mRefresh.getVisibility() !=
View.GONE) {mRefresh.setVisibility(View.GONE);}if (bindingView.getRoot().getVisibility() !=
View.VISIBLE) {bindingView.getRoot().setVisibility(View.VISIBLE);}}/*** 加载失败点击重新加载的状态*/protected void showError() {if (mLlProgressBar.getVisibility() !=
View.GONE) {mLlProgressBar.setVisibility(View.GONE);}// 停止动画if (mAnimationDrawable.isRunning()) {mAnimationDrawable.stop();}if (mRefresh.getVisibility() !=
View.VISIBLE) {mRefresh.setVisibility(View.VISIBLE);}if (bindingView.getRoot().getVisibility() !=
View.GONE) {bindingView.getRoot().setVisibility(View.GONE);}}/*** 布局*/public abstract int setContent();/*** 在这里实现Fragment数据的缓加载.*/@Overridepublic void setUserVisibleHint(
boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);if (getUserVisibleHint()) {mIsVisible =
true;loadData();} else {mIsVisible =
false;}}/*** 显示时加载数据,需要这样的使用* 注意声明 isPrepared,先初始化* 生命周期会先执行 setUserVisibleHint 再执行onActivityCreated* 在 onActivityCreated 之后第一次显示加载数据,只加载一次*/protected void loadData() {}@Overridepublic void onDestroy() {super.onDestroy();}
} View Code
16.ViewModel使用方法。
- 作用:为Activity或Fragment管理,请求数据,但是具体的逻辑不在ViewModel中写。
- 参考文章:https://blog.csdn.net/qq_24442769/article/details/79426609
- build.gradle配置:
implementation 'android.arch.lifecycle:extensions:1.1.0'
implementation 'android.arch.lifecycle:reactivestreams:1.1.0'
17.RxJava+RxAndroid的学习。
作用:RxJava是一个Android中的响应式实现,RxAndroid将异步UI事件封装起来。
参考文章:https://www.cnblogs.com/zhaoyanjun/p/5175502.html
github地址:https://github.com/ReactiveX/RxJava 和 https://github.com/ReactiveX/RxAndroid
18.首页功能完成。
只要研究好一个页面,基本上其他页面都不在话下了。
因为这个页面,进行了网络请求,视图绑定,BaseFragment定义,接口API请求等,基本上能做的事情,这个页面都会做。
除此之外,还有一些通用的工具包,首页Bean类型的定义,首页的布局文件,抽屉的布局,只不过没有处理点击事件。
预览页面如下:
然后仅仅完成首页之后,我进行打包,方便以后使用。
百度云链接:https://pan.baidu.com/s/1VCiuVy21RLMOHflN9MP19A
密码:hmci
之后的过程应该就是举一反三了!
这里我总结一下过程:(想到哪写到哪)
1.首先定义了一个抽象类BaseFragment,让这个每日推荐的Fragment来继承它。不单单继承,还要求传入一个泛型类,这个泛型类继承ViewDataBinding。
这里就是为了方便处理数据的,因为用到MVVM模式,所有声明了layout的视图都生成一个视图绑定类,进而可以获取到数据。个人觉得就是为了简化
从视图中findViewById的过程,其实在用的时候,还是有一次赋值的过程,不过就不用以前那么麻烦地看视图id了。
言归正传,这个BaseFragment同样继承了Fragment,注意是v4的Fragment,这里是一坑。
然后这个BaseFragment主要干什么?
布局很简单,最外层LinearLayout,里面一个RelativeLayout,再里面就是两个LinearLayout,分别是正在加载和加载失败的图片及文字的显示。
所以啊,布局中的RelativeLayout其实就是显示内容的,要么这个内容为正在加载,要么为加载失败,要么就是内容了,就这三种情况。
值得一提的是,使用MVVM模式是如何将内容添加到这个RelativeLayout中呢?
这里其实是用了DataBindingUtil.inflate方法,动态生成一个泛型类(继承于ViewDataBinding),然后用RelativeLayout.addView方法添加即可。
回想一下Fragment的生命周期~
首先onAttach->onCreateView->onCreate->onActivityCreate->onStart->onResume,到这里视图才能真正展示。。。
所以在onActivityCreate中可以做什么呢?
答案当然就是:开启动画啦。然后就是设置失败刷新的点击事件。反正这里就是需要干嘛就干嘛。
然后还有一些方法,比如显示加载中状态,加载完成状态,加载失败状态,主要处理视图的隐藏,这里还是简单。主要方便继承者调用嘛。
重要点1:然后就是实现Fragment数据的缓加载,其实就是判断fragment是否可见,可见才进行请求。
重要点2:然后需要添加一条消息,就是你数据在一个子线程里请求请求请求...完成之后,要通过一条消息发送给主线程,主线程来处理数据。
注意在onDestroy中,将这条消息取消掉,否则内存泄漏就惨咯,然后自己也写一个移除方法。
2.好了,现在有BaseFragment了。那么现在还需要对当前页面单独设置一个接口,用来实现仅仅这个页面才会做的一些方法。
对于主页来说,我需要显示轮播图,显示主页列表,显示主页错误页面,显示旋转动画,取主页缓存。
3.然后就是EverydayFragment的具体实现了,原来在这里面才真正开启动画的,所以这样的话会更加灵活处理各种页面动画。
原来这里不是用的BaseFragment中的动画,他是自己单独写的一个动画。所以在它自己的布局中有一个独特的动画图。
这里的需要定义一个类似于之前MVP模式中的P(presenter),这里因为用到MVVM模式,所以效果是一样的,不过这里名字为viewModel了。
4.来看一下这个处理器吧。(我习惯称处理网络请求的东西叫做处理器,不过这里进行了两次封装,还有一个我就叫具体网络请求器吧)
这里还包装了一层,将真正的网络请求封装了。
首先看一下第一层处理器。
在构造器里面建立了一个具体网络请求器的实例。这个可以叫做创建型模式吧。
简单理一下网络数据加载过程:
在处理器中调用了网络请求器的同名的方法,用了一个接口作为回调3种情况,1个是成功,1个是加载失败,1个是添加消息给basefragment。
如果加载成功,则显示数据,然后将缓存清理,重新添加最新的缓存。如果没有数据列表,则从缓存中拿,缓存也没有,则重新请求。
如果加载失败,就尝试从缓存中读取,如果缓存有数据,就显示列表数据。如果缓存也没数据,则显示BaseFragment中定义的错误界面。
然后看一些这个具体网络请求器干了些什么?
首先是具体请求轮播图。调用的过程真的太讲究了,得好好学学别人的封装过程。
首先是定义了一个同名的请求方法后,里面传入了一个接口,主要是处理具体的回调。
网络封装过程:
首先HttpClient是一个网络请求类,这是一个接口。因为每个接口会有一个BaseUrl。
这个HttpClient里面有一个Builder类,里面处理各种请求,返回的都是HttpClient对象。
为什么会返回HttpClient对象呢?因为这里还用了一个BuildFactory类,来封装Retrofit请求,设置一些请求参数等。
最后还是要回到HttpClient,然后这里面还有请求的具体参数写的API请求,返回Observable<T>类型数据。
5.获取到网络数据后该怎么显示呢?
答案就是适配器了。
在哪里设置适配器?
这个就得看着自己了,在继承BaseFragment的类中设置。在这个继承BaseFragment中有很多实例,如绑定Header布局对象,绑定Footer布局对象,
当前Fragment的适配器,当前Fragment的ViewModel。
这个Header布局对象就是当前Fragment页面最上方的布局,一个banner,一个4个图标入口。使用RecycleView.addHeaderView方法即可。
如果RecyclerView滑动不流畅,需设置recylerView.setNestedScrollingEnabled(false);
注意在onPause中停止全部图片请求,在onResume继续图片请求。
这个适配器就是处理数据显示的一个关键类了。
首先他继承了BaseRecyclerViewAdapter,无赖,只能硬着头皮先看BaseRecyclerViewAdapter了。
这个BaseRecyclerViewAdapter也是继承了RecyclerView.Adapter<BaseRecyclerViewHolder>,无赖,还得先了解BaseRecyclerViewHolder啦
BaseRecyclerViewHolder主要就是一个视图持有者,竟然定义了两个泛型,T是我们的数据类型,D是一个视图绑定类。
因为继承了RecyclerView.ViewHolder,那么它的作用也就是容纳视图的作用了。所以这个BaseRecyclerVIewHolder的作用就是执行
ViewDataBinding的一个executePendingBindings方法而已。
BaseRecyclerViewAdapter所以就该实现父类中定义的抽象方法onBindViewHolder了。其他就是一些数据的增加删除获取了。
EverydayAdapter正是继承了BaseRecyclerViewAdapter了,然后具体实现了两个必须实现的方法,getItemViewType,onCreateViewHolder了
19.对于适配器中的getItemViewType的理解。
现在才真正理解适配器中的函数。
其实里面的position,并不是一开始就加载完。而是加载到手机屏幕高度,就是说position只会填充完当前手机屏幕。
然后滑动手机屏幕后,这个position才会做相应的改变。
20.学会了自定义处理WebView的活动(通用)。
现在的APP基本都会用到webView,特别是类似于微信那种自带进度条的WebView很常见。
所以现在新建了一个专门处理webView的活动,可能只是这个项目通用吧,不同项目根据自己需求酌情修改即可。
这个网页可以处理很多东西,拨打电话,发送短信,上传图片,播放视频,循环显示网页标题,进度条,可以说比较通用吧。
参考文章:https://github.com/youlookwhat/WebViewStudy
21.以后再补充。
转载于:https://www.cnblogs.com/Jason-Jan/p/8886805.html
总结
以上是生活随笔为你收集整理的从Github开源项目《云阅》所学到的知识的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。