使用 Jectpack Navigation 中 SafeArg 的方式向 Activity 与 Fragment 传递数据

在自己的项目中用 Jetpack 的 Navigation 的时候发现 SafeArgs 在 destination 之间传递参数非常符合我所期望的 Kotlin 该有的方式,所以我想把 SafeArgs 中传递参数的方式应用到 Activity 与 Fragment 中,并不想像现在使用 lateinit var 然后在 onCreate 等函数中去初始化这样用 Java 的方式去写 Kotlin,于是我开始尝试仿写一个 activityArgs<T>()fragmentArgs<T>() 来在非 Navigation 的页面传递参数,所以先着手看 Jetpack Navigation SafeArgs 是如何使用代理传递参数的

阅读更多

RecyclerView ItemDecoration 均分 Item 间隔

在 RecyclerView 中使用 ItemDecoration 重写 getItemOffsets 设置每个 Item 的间隔,支持各种 LayoutManager:

SpacingItemDecoration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class SpacingItemDecoration extends RecyclerView.ItemDecoration {

private int spanCount;
private int spacing;
private boolean includeEdge;

public SpacingItemDecoration(int spacing, int spanCount, boolean includeEdge) {
this.spacing = spacing;
this.spanCount = spanCount;
this.includeEdge = includeEdge;
}

@Override
public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, RecyclerView parent,
@NotNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;

if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;

if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
} else {
outRect.left = column * spacing / spanCount;
outRect.right = spacing - (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
}

TextView 添加下划线,删除线,加粗

通过 TextView 的 setPaintFlags 方式来添加下划线,删除线,加粗,在原有的 PaintFlags 上添加新的 Flag 而不清除原有的 Flags

  • Paint.ANTI_ALIAS_FLAG 抗锯齿

  • Paint.FILTER_BITMAP_FLAG 用于对 Bitmap 进行转换(例如缩放)时对 Bitmap 进行双线性过滤

  • Paint.DITHER_FLAG 会影响对比设备精度更高的颜色进行下采样的方式

  • Paint.UNDERLINE_TEXT_FLAG 下划线

  • Paint.STRIKE_THRU_TEXT_FLAG 删除线

  • Paint.FAKE_BOLD_TEXT_FLAG 伪粗体,并不是通过选用更高 weight 的字体让文字变粗,而是通过程序在运行时把文字给「描粗」

  • Paint.LINEAR_TEXT_FLAG 会调整文本绘制操作以平滑处理缩放比例,应当与 Paint.SUBPIXEL_TEXT_FLAG 一起使用

  • Paint.SUBPIXEL_TEXT_FLAG 亚像素级的抗锯齿

TextViewUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class TextViewUtils {

public static void addUnderLine(TextView textView) {
addPaintFlag(textView, Paint.UNDERLINE_TEXT_FLAG);
}

public static void removeUnderLine(TextView textView) {
removePaintFlag(textView, Paint.UNDERLINE_TEXT_FLAG);
}

public static void addStrikeThrough(TextView textView) {
addPaintFlag(textView, Paint.STRIKE_THRU_TEXT_FLAG);
}

public static void removeStrikeThrough(TextView textView) {
removePaintFlag(textView, Paint.STRIKE_THRU_TEXT_FLAG);
}

public static void setFakeBold(TextView textView) {
addPaintFlag(textView, Paint.FAKE_BOLD_TEXT_FLAG);
}

public static void removeFakeBold(TextView textView) {
removePaintFlag(textView, Paint.FAKE_BOLD_TEXT_FLAG);
}

private static void addPaintFlag(TextView textView, int paintFlag) {
textView.setPaintFlags(textView.getPaintFlags() | paintFlag);
}

private static void removePaintFlag(TextView textView, int paintFlag) {
textView.setPaintFlags(textView.getPaintFlags() & (~paintFlag));
}
}

Android 中的 Parcelable 与 Serializable

1.作用

Serializable 的作用是为了保存对象的属性到本地文件、数据库、网络流、rmi 以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。

而 Android 的 Parcelable 的设计初衷是因为 Serializable 效率过慢。为了在程序内不同组件间以及不同 Android 程序(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable 是通过 IBinder 通信的消息的载体。

2.区别

两者最大的区别在于存储媒介的不同,Serializable 使用 I/O 读写存储在硬盘上,而 Parcelable 是直接在内存中读写。很明显,内存的读写速度通常大于 I/O 读写,所以在 Android 中传递数据优先选择 Parcelable。

Serializable 会使用反射,序列化和反序列化过程需要大量的 I/O 操作,会在序列化的时候创建许多临时对象,容易触发GC。

Parcelable 自已实现封送和解封(marshalled & unmarshalled)操作不需要用反射,数据也存放在 Native 内存中,效率要快很多。

Android 启动模式

1.Standard

这个模式是默认的启动模式,即标准模式,在不指定启动模式的前提下,系统默认使用该模式启动 Activity ,每次启动一个 Activity 都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的 Activity ,该 Activity 就属于启动它的 Activity 的任务栈中。这个 Activity 它的 onCreate()onStart()onResume() 方法都会被调用。

2.SingleTop

这个模式下,如果新的 Activity 已经位于栈顶,那么这个 Activity 不会被重写创建,同时它的 onNewIntent() 方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与 standard 模式相同。需要注意的是这个Activity它的 onCreate()onStart() 方法不会被调用,因为它并没有发生改变。

  • 当前栈中已有该 Activity 的实例并且该实例位于栈顶时,不会新建实例,而是复用栈顶的实例,并且会将 Intent 对象传入,回调 onNewIntent() 方法。
  • 当前栈中已有该 Activity 的实例但是该实例不在栈顶时,其行为和 Standard 启动模式一样,依然会创建一个新的实例。
  • 当前栈中不存在该 Activity 的实例时,其行为同 Standard 启动模式。

3.SingleInstance

该模式也是单例的,但和 SingleTask 不同, SingleTask 只是任务栈内单例,系统里是可以有多个 SingleTask Activity 实例的,而 SingleInstance Activity 在整个系统里只有一个实例,启动一 SingleInstanceActivity 时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity。

4.SingleTask

该模式的 Activity 在同一个 Task 内只有一个实例,如果 Activity 已经位于栈顶,系统不会创建新的 Activity 实例,和 SingleTop 模式一样。但 Activity已经存在但不位于栈顶时,系统就会把该 Activity 移到栈顶,并把它上面的 Activity 出栈。