飞飞的AI实验室

用AI放大灵感,把想法变成作品。

实现思路:

使用相对布局,先写一个 TextView,然后自定义一个 EraseView,写一个同样大小的 EraseView 覆盖在 TextView 上面即可。

先看下效果图:

在这里插入图片描述

代码也比较简单,我就直接贴上了:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class EraseView extends View {

private boolean isMove = false;
private Bitmap bitmap = null;
private Bitmap frontBitmap = null;
private Path path;
private Canvas mCanvas;
private Paint paint;

public EraseView(Context context) {
this(context, null);
}

public EraseView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public EraseView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCanvas == null) {
createEraseBitmap();
}
canvas.drawBitmap(bitmap, 0, 0, null);
mCanvas.drawPath(path, paint);
}

public void createEraseBitmap() {
bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_4444);
frontBitmap = createBitmap(Color.GRAY, getWidth(), getHeight());

paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(60);

path = new Path();

mCanvas = new Canvas(bitmap);
mCanvas.drawBitmap(frontBitmap, 0, 0, null);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
float ax = event.getX();
float ay = event.getY();

if (event.getAction() == MotionEvent.ACTION_DOWN) {
isMove = false;
path.reset();
path.moveTo(ax, ay);
invalidate();
return true;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
isMove = true;
path.lineTo(ax, ay);
invalidate();
return true;
}
return super.onTouchEvent(event);
}

public Bitmap createBitmap(int color, int width, int height) {
int[] rgb = new int[width * height];

for (int i = 0; i < rgb.length; i++) {
rgb[i] = color;
}

return Bitmap.createBitmap(rgb, width, height, Config.ARGB_8888);
}

}

好了,今天的分享就到这里了。

最后,我这边有个技术交流群,平常我会分享一些学习资源到群里,还可以和大家一起交流学习,需要的朋友可以扫描下面的二维码加我微信并备注「加群」,拉你进入技术交流群!

![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltYWdlLngtc2lyLmNvbS9Gb2lFem5ZRjVlU1FTOFdNTjYyTnpVUTRnazQw?x-oss-process=image/format,png# =337x448)

飞哥带你去装逼,一直装逼到天黑!

阅读全文 »


题图:来自飞哥的图片工厂

音乐推荐:背叛
文丨IT大飞说
预计阅读时间:3.2 分钟

哈喽,朋友们,今天我们来学习下如何使用 Swagger2。

什么是 Swagger?

Swagger 是一款 RESTFUL 接口的、基于YAML、JSON语言的文档在线自动生成、代码自动生成的工具。

如何集成?

打开 pom.xml 文件,添加如下依赖:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

注:编写本文时使用的最新版本是 2.9.2,当你看到这篇文章的时候可能会有更新,最新的版本可到 https://mvnrepository.com 查看。

新建一个 Swagger2 类,完整代码如下:

阅读全文 »


题图:来自飞哥的图片工厂

音乐推荐:后来
文丨IT大飞说
预计阅读时间:2.3 分钟

哈喽,朋友们,之前我们学习了一些 RxJava2.x 的常用操作符,今天我们来继续学习一下 RxJava 的 compose 操作符。

compose 操作符能够从数据流中得到原始的被观察者,当创建被观察者时,compose 操作符会立即执行,而不像其他的操作符需要在 onNext() 调用后才能执行。

使用场景一

我们可以用 compose 操作符来进行线程的切换,一般用在网络请求的地方。

原始的写法为:

1
2
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

我们可以将上面的操作封装成一个简单的工具类来使用,我这里提供了 Java 版和 Kotlin 版本:

Java 版本:

阅读全文 »

  • All:判断 Observable 发射的所有的数据项是否都满足某个条件;
  • Amb:给定多个 Observable,只让第一个发射数据的 Observable 发射全部数据;
  • And/Then/When:通过模式(And条件)和计划(Then次序)组合两个或多个 Observable 发射的数据集;
  • Average:计算 Observable发射的数据序列的平均值,然后发射这个结果;
  • Buffer:缓存,可以简单理解为缓存,它定期从 Observable 收集数据到一个集合,然后把这些数据集合打包发射,而不是一次发射一个;
  • Catch:捕获,继续序列操作,将错误替换为正常的数据,从 onError 通知中恢复;
  • CombineLatest:当两个 Observables 中的任何一个发射了一个数据时,通过一个指定的函数组合每个 Observable 发射的最新数据(一共两个数据),然后发射这个函数的结果;
  • Concat:不交错地连接多个 Observable 的数据;
  • Connect:指示一个可连接的 Observable 开始发射数据给订阅者;
  • Contains:判断 Observable 是否会发射一个指定的数据项;
  • Count:计算 Observable 发射的数据个数,然后发射这个结果;
  • Create:通过调用观察者的方法从头创建一个 Observable;
  • Debounce:只有在空闲了一段时间后才发射数据,简单来说,就是如果一段时间没有操作,就执行一次操作;
  • DefaultIfEmpty:发射来自原始 Observable 的数据,如果原始 Observable 没有发射数据,就发射一个默认数据;
  • Defer:在观察者订阅之前不创建这个 Observable,为每一个观察者创建一个新的 Observable;
  • Delay:延迟一段时间发射结果数据;
  • Distinct:去重,过滤掉重复数据项;
  • Do:注册一个动作占用一些 Observable 的生命周期事件,相当于 Mock 某个操作;
  • Materialize/Dematerialize:将发射的数据和通知都当作数据发射,或者反过来;
  • ElementAt:取值,取特定位置的数据项;
  • Empty/Never/Throw:创建行为受限的特殊 Observable;
  • Filter:过滤,过滤掉没有通过谓词测试的数据项,只发射通过测试的
  • First:首项,只发射满足条件的第一条数据;
  • flatMap:扁平映射,将 Observable 发射的数据转换为 Observables 集合,然后将这些 Observable 发射的数据平坦化地放进一个单独的 Observable,可以认为是一个将嵌套的数据结构展开的过程;
  • From:将其他对象或数据结构转换为 Observable;
  • GroupBy:分组,将原来的 Observable 拆分为 Observable 集合,将原始 Observable 发射的数据按 Key 分组,每一个 Observable 发射一组不同的数据;
  • IgnoreElements:忽略所有的数据,只保留终止通知(onError 或 onCompleted);
  • Interval:创建一个定时发射整数序列的 Observable;
  • Join:无论何时,如果一个 Observable 发射了一个数据项,只要在另一个 Observable 发射的数据项定义的时间窗口内,就将两个 Observable 发射的数据合并发射;
  • Just:将对象或者对象集合转换为一个会发射这些对象的 Observable;
  • Last:末项,只发射最后一条数据;
  • Map:映射,对序列的每一项都应用一个函数变换 Observable 发射的数据,实质是对序列中的每一项执行一个函数,函数的参数就是这个数据项;
  • Max:计算并发射数据序列的最大值;
  • Merge:将两个 Observable 发射的数据组合并成一个;
  • Min:计算并发射数据序列的最小值;
  • ObserveOn:指定观察者观察 Observable 的调度程序(工作线程);
  • Publish:将一个普通的 Observable 转换为可连接的;
  • Range:创建发射指定范围的整数序列的 Observable;
  • Reduce:按顺序对数据序列的每一项数据应用某个函数,然后返回这个值;
  • RefCount:使一个可连接的 Observable 表现得像一个普通的 Observable;
  • Repeat:创建重复发射特定的数据或数据序列的 Observable;
  • Replay:确保所有的观察者收到同样的数据序列,即使他们在 Observable 开始发射数据之后才订阅;
  • Retry:重试,如果 Observable 发射了一个错误通知,重新订阅它,期待它正常终止辅助操作;
  • Sample:取样,定期发射最新的数据,等同于数据抽样,有的实现中叫作 ThrottleFirst;
  • Scan:扫描,对 Observable 发射的每一项数据应用一个函数,然后按顺序依次发射这些值;
  • SequenceEqual:判断两个 Observable 是否按相同的数据序列;
  • Serialize:强制 Observable 按次序发射数据并且功能是有效的;
  • Skip:跳过前面的若干项数据;
  • SkipLast:跳过后面的若干项数据;
  • SkipUntil:丢弃原始 Observable 发射的数据,直到第二个 Observable 发射了一个数据,然后发射原始 Observable 的剩余数据;
  • SkipWhile:丢弃原始Observable发射的数据,直到一个特定的条件为假,然后发射原始 Observable 剩余的数据;
  • Start:创建发射一个函数返回值的 Observable;
  • StartWith:在发射原来的 Observable 的数据序列之前,先发射一个指定的数据序列或数据项;
  • Subscribe:收到 Observable 发射的数据和通知后执行的操作;
  • SubscribeOn:指定 Observable 应该在哪个调度程序上执行;
  • Sum:计算并发射数据序列的和;
  • Switch:将一个发射 Observable 序列的 Observable 转换为这样一个 Observable,即它逐个发射那些 Observable 最近发射的数据;
  • Take:只保留前面的若干项数据;
  • TakeLast:只保留后面的若干项数据;
  • TakeUntil:发射来自原始 Observable 的数据,直到第二个 Observable 发射了一个数据或一个通知;
  • TakeWhile:发射原始 Observable 的数据,直到一个特定的条件为真,然后跳过剩余的数据;
  • TimeInterval:将一个 Observable 转换为发射两个数据之间所耗费时间的 Observable;
  • Timeout:添加超时机制,如果过了指定的一段时间没有发射数据,就发射一个错误通知;
  • Timer:创建在一个指定的延迟之后发射单个数据的 Observable;
  • Timestamp:给 Observable 发射的每个数据项添加一个时间戳;
  • To:将 Observable 转换为其他对象或数据结构;
  • Using:创建一个只在 Observable 生命周期内存在的一次性资源;
  • Window:窗口,定期将来自 Observable 的数据拆分成一些 Observable 窗口,然后发射这些窗口,而不是每次发射一项;类似于 Buffer,但 Buffer 发射的是数据,Window 发射的是 Observable,每一个 Observable 发射原始 Observable 数据的一个子集;
  • Zip:打包,使用一个指定的函数将多个 Observable 发射的数据组合在一起,然后将这个函数的结果作为单项数据发射;
阅读全文 »


题图:来自飞哥的图片工厂

音乐推荐:你的姑娘
文丨IT大飞说
预计阅读时间:1.2 分钟

哈喽,朋友们,之前我们学习了一些 RxJava2.x 的常用操作符,今天我们来继续学习一下RxJava 的并行编程。

随着手机 CPU 的高速发展,性能越来越强劲,核心数越来越多,我们要充分、高效地利用这些 CPU 资源,来提高程序运行的效率,解决复杂的业务问题,这将变得越来越重要。

1.什么是并行编程?

对于并发我们可能比较清楚,那么并行是什么呢?它们的区别是什么?并发(concurrency)是指一个处理器同时处理多个任务,并行(parallelism)是多个处理器或者是多核处理器同时处理多个不同的任务,并行是同时发生的多个并发事件,具有并发的含义,而并发不一定是并行。

在 Java 8 中有个并行流(parallelStream),有的同学可能用过,我们想使用并行流的方式打印出 1-100 之间的整数,来看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
private void parallelismWithJava8() {
List<Integer> list = new ArrayList<>();

for (int i = 1; i <= 100; i++) {
list.add(i);
}

list.parallelStream()
.map(Object::toString)
.forEach(s -> LogUtil.i(TAG, "s=" + s + ",Current Thread Name=" + Thread.currentThread().getName()));
}

上面的结果会交错输出 1-100 之间的整数,因为并行的缘故所以每个输出执行的时间可能不一样,所以会交错输出,其实上面的代码是 Java 8 借助了 JDK 的 fork/join 框架来实现并行编程的。

2.使用 RxJava 的 flatMap 实现并行编程

阅读全文 »

方式1

将 aar 包放入 library module 的 libs 目录下,然后在工程的 build.gradle 文件中加入如下代码:

1
flatDir { dirs 'libs', '../moduleName/libs' }

注:将 moduleName 替换为你自己的 library module 的 name.

然后在 module 的 build.gradle 文件中添加依赖:

1
implementation(name: 'aar包名', ext: 'aar')

方式2

这种方式的思路是,先把 aar 包放入一个单独的文件夹中,然后我们再依赖这个文件夹就可以了,这个文件夹你也可以理解成一个特殊的 module。

先在工程目录下新建一个文件夹(和app同级),然后将你的 aar 包放入 这个文件夹,然后再新建一个 build.gradle 文件,在这个文件中添加下面两句:

1
2
configurations.maybeCreate("default")
artifacts.add("default", file('aar包名.aar'))
阅读全文 »


题图:Pixabay License

哈喽,朋友们,上一篇文章我们学习了 filter 操作符,今天我们来学习 RxJava 中比较重要的两个操作符 flatMap 和 concatMap。

1.flatMap

我们知道 flat 是平的意思,这个翻译还是有点生硬和抽象,我们还是不太理解,你就可以先理解成平铺的意思吧。

flatMap 是变换操作符的一种,它将一个发射数据的 Observable 变换为多个 Observable,然后将他们发射的数据合并后放入一个单独的 Observable。

举个简单的例子吧,假设上游有个数据源,数据源是学生对象的集合,学生有姓名和课程的属性,每个学生的课程可以有多个,需求是打印出所有学生的课程名称。

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
Student student1 = new Student();

List<Student.Course> list1 = new ArrayList<>();
list1.add(new Student.Course("语文1"));
list1.add(new Student.Course("数学1"));
list1.add(new Student.Course("英语1"));
list1.add(new Student.Course("物理1"));
list1.add(new Student.Course("化学1"));

student1.setName("张三");
student1.setCourses(list1);

Student student2 = new Student();

List<Student.Course> list2 = new ArrayList<>();
list2.add(new Student.Course("语文2"));
list2.add(new Student.Course("数学2"));
list2.add(new Student.Course("英语2"));

student2.setName("李四");
student2.setCourses(list2);

List<Student> studentList = new ArrayList<>();
studentList.add(student1);
studentList.add(student2);

Observable.just(studentList)
.flatMap((Function<List<Student>, ObservableSource<Student>>) students -> Observable.fromIterable(students))
.flatMap((Function<Student, ObservableSource<Student.Course>>) student -> {
LogUtil.i(TAG, "student name===" + student.getName());
return Observable.fromIterable(student.getCourses());
})
.subscribe(course -> LogUtil.i(TAG, "course===" + course.getCourseName()));

执行结果如下:

1
2
3
4
5
6
7
8
9
10
I/RxJavaOperatorActivity: student name===张三
I/RxJavaOperatorActivity: course===语文1
I/RxJavaOperatorActivity: course===数学1
I/RxJavaOperatorActivity: course===英语1
I/RxJavaOperatorActivity: course===物理1
I/RxJavaOperatorActivity: course===化学1
I/RxJavaOperatorActivity: student name===李四
I/RxJavaOperatorActivity: course===语文2
I/RxJavaOperatorActivity: course===数学2
I/RxJavaOperatorActivity: course===英语2

我们看到上面的代码没有 for 循环,却遍历打印了集合中的数据,RxJava 就是为了简化或者是取消 for 循环种方式,使用操作符来解决遍历循环的问题,从而是代码更加扁平化,使代码更加清晰和便于理解。

阅读全文 »


题图:Pixabay License

哈喽,朋友们,上一篇文章我们学习了 repeate 操作符,今天我们继续来学习 filter 操作符。

我们都知道 filter 是过滤的意思,也就是说这个操作符就是帮助我们来过滤掉一些我们不需要的数据。

举个简单的例子吧,假设上游有个数据源,顺序向下游发送 0-9 十个数字,我们只想打印大于 5 的数字,我们就可以这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Observable.interval(1, TimeUnit.SECONDS)
.take(10)
.filter(new Func1<Long, Boolean>() {
@Override
public Boolean call(Long aLong) {
return aLong > 5;
}
})
.subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
LogUtil.i("aLong===" + aLong);
}
});

执行结果如下:

1
2
3
4
I/RxJavaOperatorActivity: aLong===6
I/RxJavaOperatorActivity: aLong===7
I/RxJavaOperatorActivity: aLong===8
I/RxJavaOperatorActivity: aLong===9

结果打印出来的都是大于 5 的数字,当然你还可以设置其他的过滤条件,例如空值等!

好了,今天的学习内容就算完成了,感觉是不是非常简单?如果你学会了就赶紧动手去实践一下吧,说不定你会有新的发现哦!

最后,我和大家分享一下我学习的一些经验或者是见解吧。我们再学习的过程中,可能学的很多东西都是点状的,我们要把学习到的东西学会融会贯通,将他们连接成线即学会相互联系,再将线形成面,再形成体,这样我们学到的知识才能形成一个完整的知识体系,不要只见树叶,不见森林,希望大家在学习的过程中,多思考,多总结!

阅读全文 »


题图:Pixabay License

哈喽,朋友们,上一篇文章我们学习了 from 操作符,今天我们继续来学习 repeat 操作符。

我们都知道 repeat 是重复的意思,也就是说这个操作符就是帮助我们来操作处理一些重复的数据或者操作,repeat 有三个相关的操作符:repeat、repeatWhen、repeatUntil,从字面也很好理解,repeat 就是简单的重复操作,repeatWhen 当达到什么条件的时候重复,repeatUntil 是直到某个条件不在重复。

1.repeat

举个简单的例子吧,例如我们想重复打印一下 “Hello World!”,我们就可以这么写:

1
2
3
4
5
private void repeat() {
Observable.just("Hello World!")
.repeat(3)
.subscribe(s -> LogUtil.i(TAG, s));
}

很简单吧,这个就不用我多说了吧。需要注意的是,如果不指定重复的次数,则会无限地重复下去!

2.repeatWhen

例如,我们想按顺序打印 0-3 的数字,当 2 秒后我们再重复打印一次,我们就可以这么写。

1
2
3
Observable.range(0, 4)
.repeatWhen(objectObservable -> Observable.timer(2, TimeUnit.SECONDS))
.subscribe(integer -> LogUtil.i(TAG, "integer===" + integer));
阅读全文 »


题图:Pixabay License

哈喽,朋友们,上一篇文章我们学习了 create & just 操作符,今天我们继续来学习 from 操作符。

from 操作符和 just 操作符一样,也属于创建操作符的一种,from 可将其他种类的对象和数据类型转换为 Observable。

从概念看,just 和 from 是比较相似的,那么,他们两者有什么区别呢?

just 只是简单的原样发射,它会将数组或 Iterable 当做单个数据发射出去,而 from 会将数组或 Iterable 的数据按顺序取出来,然后逐个发射出去,举个例子你就明白了。

举例,我们初始化了一个 list,分别用 just 和 fromIterable 操作符进行处理,并打印了数据的结果。

1
2
3
4
5
6
7
8
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 6; i++) {
list.add(i);
}

Observable.just(list).subscribe((Consumer<List>) list1 -> LogUtil.i(TAG, "just():" + Arrays.toString(list1.toArray())));

Observable.fromIterable(list).subscribe(integer -> LogUtil.i(TAG, "fromIterable():" + integer));

执行结果如下:

1
2
3
4
5
6
7
I/RxJavaOperatorActivity: just():[0, 1, 2, 3, 4, 5]
I/RxJavaOperatorActivity: fromIterable():0
I/RxJavaOperatorActivity: fromIterable():1
I/RxJavaOperatorActivity: fromIterable():2
I/RxJavaOperatorActivity: fromIterable():3
I/RxJavaOperatorActivity: fromIterable():4
I/RxJavaOperatorActivity: fromIterable():5

从上面打印的日志我们可以看到,just 是将整个 list 数据传递下来了,而 fromIterable 是将 list 中的数据遍历并一个一个发射出去,数组也是一样的道理,数组使用 fromArray 操作符。

阅读全文 »
0%