荏苒追寻个人博客

做一个有追求的青年


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

Android 杂谈——Interview Prepare

发表于 2022-02-22 | 分类于 Android

https://rainmonth.github.io/posts/A220222.html

Android Daily Interview,备用地址

Java方面

Java基础部分

抽象类与接口的区别别

  • 抽象类,采用 修饰符 + abstract class来定义,和普通类一样,可以定义常量、方法、抽象方法、一般方法和构造方法,是类共性的提取
  • 接口, 采用 修饰符 + interface来定义,可以定义常量(默认就是public static的),可以声明方法(java 8以后方法可以有默认实现,不过需要加上default关键字),是一种规范,实现接口的类必须提供接口中定义方法的实现。
  • Java是单继承,多实现的
阅读全文 »

Android 系统架构——Android系统架构说明

发表于 2022-02-20 | 分类于 Android , 系统架构

https://rainmonth.github.io/posts/A220220.html

C++ Basic

发表于 2021-09-23 | 分类于 C++

https://rainmonth.github.io/posts/f8869076.html

C++中 void*的使用与理解

Android 学不过来系列

发表于 2021-09-07 | 分类于 Android , 新技术

https://rainmonth.github.io/posts/A210907.html

本文记录一些指的自己学习的Android 相关文章,让自己:

Keep Learning

新技术

SVGA

Android 适配——Notification适配指南

发表于 2021-09-06 | 分类于 Android , 适配

https://rainmonth.github.io/posts/A210906.html

Android Notification适配

背景

最近项目App 在Android 8.0上默认不显示通知,查了一下原因,是因为通知设置的级别太低。同时由于Android 8.0以上会列出相应的渠道名称,发现我们的渠道名称居然设置成了发起通知对应的Service的名称,借此契机,就来写一下通知相关的东西,主要从以下几个方面着手:

  • 通知的使用;
    • 基本使用方法
    • 扩展使用方法
  • 通知适配要注意的问题;
  • 通知系统其它组件结合使用;
  • 通知常用的一些操作;
阅读全文 »

Java 反射

发表于 2021-09-03 | 分类于 Java , 反射

https://rainmonth.github.io/posts/J210903.html

在Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称之外,还要求必须拥有一个与原方法不同的特征签名;

特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里面是无法仅仅依靠返回值的不同来对一个已有方法进行重载。

但在Class文件格式之中,特征签名的范围更大一些,只要描述符不是完全一致的两个方法也可以共存。

也就是说,如果两个方法有相同的名称和特征签名,但返回值不同,那么也是可以合法存于同一个Class文件中的。

以下两个方法被认为是方法的重载

1
2
3
4
5
6
7
8
9
public void setNameAndId(String name, long id) {
this.name = name;
this.id = id;
}

public void setNameAndId(String name, Long id) {
this.name = name;
this.id = id;
}

说明:传入的参数会自动被转换为Long(自动装箱),但通过Method.getParameterTypes()获取到的参数类型却是 方法declare时的类型,所以如果直接通过传入参数的类型跟Method.getParameterTypes()获取到的类型比较,是获取不到相应的Method的

Class.getDeclaredMethods() 获取的是类自己定义的所有方法(不包含其父类或接口的定义的方法)或类重写或实现的父类或接口的方法。

Android 控件——放大镜实现

发表于 2021-08-23 | 分类于 Android , 控件

本文链接:https://rainmonth.github.io/posts/A210823.html

背景

最近要做一个看图找字功能的游戏化场景,决定采用原生实现,具体就是利用放大镜找到图片中的汉字,识别当前区域和目标区域,在松手后给出判断。要实现这个demo需要解决如下问题:

  1. 得到要进行放大操作的目标位图(Bitmap)注意 Bitmap 的加载优化,避免OOM;
  2. 像素放大的实现方式(目前可采用ShaderDrawable和Path;
  3. 放大的像素区域和添加的放大镜图片的整合(因为不同的识别结果需要切换成不同的资源图片)
  4. 目标区域的转换,考虑到目标区域在不同的设备上会有表现,所以要注意兼容性处理;
  5. 放大识别判定(如何判定放大后的像素区域是目标区域),并添加识别回调;
  6. 边界优化,放大到边界怎么处理;
  7. UI细节上的处理
  8. 利用合适的设计模式封装使用;

实现方式

基本原理都是将要放大区域的目标像素按一定的比例通过矩阵变化来进行放大处理。

  1. 利用ShapeDrawable来确定放大的区域,然后采用BitmapShader 来实现区域的放大。(BitmapShader,Shader的一种,作用是将Bitmap以纹理的方式绘制出来)
  2. 采用Path,利用clipPath获取要放大的区域,然后在放大的区域中绘制放大后的像素;
  3. 采用系统的Magnifier组件来实现(API 28以上,即Android 9系统以上系统才有系统级支持,API 29以上支持Builder配置);

ShapeDrawable+BitmapShader方式

这种方式主要利用ShapeDrawable来确定位置,利用BitmapShader来处理放大效果,具体实现方式也分一下几个步骤:

  1. 获取源bitmap,即获取要放大处理的bitmap;

  2. 设置放大区域,并根据放大区域来创建相应的ShapeDrawable(ShapeDrawable支持不同的形状,具体可以参考ShapeDrawable的API);

  3. 根据放大区域,从源bitmap裁剪出对应范围的cutBitmap,然后对cutBitmap进行放大处理得到scaledBitmap(调用Bitmap.createScaledBitmap方法);

  4. 这时候的scaledBitmap就有放大效果了,只需要将scaledBitmap设置到BitmapShader中去即可(BitmapShader支持纹理的绘制方式,具体参见TileMode;

  5. 将得到的BitmapShader设置到ShapeDrawable的Paint中;

  6. 绘制出ShapeDrawable;

clipPath + Matrix方式

这种方式主要采用Path来确定位置,然后利用Matrix来实现放大、平移操作,关于Matrix相关说明,见底部知识储备Matrix相关。

具体实现步骤:

  1. 根据需要的放大配置(如放大倍数factor,放大位置,确定Path)

  2. 通过clipPath裁剪出要放大的区域;

  3. 根据放大倍数,通过Matrix的setScale方法来设置放大系数

  4. 通过Matrix的postTranslate来修正放大中心

    1
    2
    3
    float dx = -curX*(factor-1);
    float dy = -curY*(facgtor-1);
    matrix.postTranslate(dy, dy);
  5. 获取放大后的bitmap

  6. 调用canvas 包含matrix的drawBitmap方法,进行bitmap绘制;

Magnifier方式

根据文档的说法,这个放大效果可以应用到任何View上。这个组件有几个关键的内部类

  • Magnifier.Builder,放大镜效果的配置参数都在这个Builder对象里;

  • InternalPopupWindow,内部实现的一个PopupWindow

  • SurfaceInfo,放大镜效果用到的Surface和与Surface相关的信息;

Magnifier类在被加载的时候就启动了一个 sPixelCopyHandlerThread 的线程,即像素拷贝线程,由此可见Magnifier的实现方式应该和像素拷贝有关,后面会分析到

下面结合Magnifier的使用,来对Magnifier的源码进行分析。

Magnifier的创建

API 28 之前,通过 Magnifier的构造函数来创建:

1
2
3
public Magnifier(@NonNull View view) {
...
}

API 29 之前,可以通过 Magnifier.Builder来构建:

1
2
3
4
5
6
7
Magnifier magnifier = new Magnifier.Builder(targetView)
.setSize(width, height)// 设置放大区域的宽、高
.setInitialZoom(2)//设置初始放大倍数,和 Magnifier的setZoom效果一样,都设置了以后者为准
.setCornerRadius(150)// 设置放大区域的圆角
.setOverlay(overlayDrawable)// 设置覆盖物
.setClippingEnabled(false)// false 时放大区域可以超出屏幕,true 时放大区域不能超出屏幕
.build();

Magnifier的显示

调用Magnifier的show方法可以显示出放大镜,有两个show方法,分别是

  • public void show(float sourceCenterX, float sourceCenterY)

  • public void show(float sourceCenterX, float sourceCenterY, float magnifierCenterX, float magnifierCenterY)

参数的意义分别表示要放大内容的中心x坐标、要放大内容的中心y坐标,放大镜的中心x坐标、放大镜的中心y坐标。

下面分析一下show方法

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
public void show(@FloatRange(from = 0) float sourceCenterX,
@FloatRange(from = 0) float sourceCenterY,
float magnifierCenterX, float magnifierCenterY) {
// 获取surface
obtainSurfaces();
// 获取内容坐标信息
obtainContentCoordinates(sourceCenterX, sourceCenterY);

int startX = mClampedCenterZoomCoords.x - mSourceWidth / 2;
final int startY = mClampedCenterZoomCoords.y - mSourceHeight / 2;
// 鱼眼效果相关配置
if (mIsFishEyeStyle) {
...
}
// 获取Window坐标信息
obtainWindowCoordinates(magnifierCenterX, magnifierCenterY);

if (sourceCenterX != mPrevShowSourceCoords.x || sourceCenterY != mPrevShowSourceCoords.y
|| mDirtyState) {
if (mWindow == null) {
synchronized (mLock) {
mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, mZoom,
mRamp, mWindowElevation, mWindowCornerRadius,
mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
mCallback, mIsFishEyeStyle);
}
}
// 进行像素拷贝
performPixelCopy(startX, startY, true /* update window position */);
} else if (magnifierCenterX != mPrevShowWindowCoords.x
|| magnifierCenterY != mPrevShowWindowCoords.y) {
final Point windowCoords = getCurrentClampedWindowCoordinates();
final InternalPopupWindow currentWindowInstance = mWindow;
sPixelCopyHandlerThread.getThreadHandler().post(() -> {
synchronized (mLock) {
if (mWindow != currentWindowInstance) {
// The magnifier was dismissed (and maybe shown again) in the meantime.
return;
}
mWindow.setContentPositionForNextDraw(windowCoords.x, windowCoords.y);
}
});
}
mPrevShowSourceCoords.x = sourceCenterX;
mPrevShowSourceCoords.y = sourceCenterY;
mPrevShowWindowCoords.x = magnifierCenterX;
mPrevShowWindowCoords.y = magnifierCenterY;
}

在performPixelCopy像素拷贝成功后,会调用mWindow.updateContent(),这个方法内部会进行时图内容的更新

知识储备

Bitmap获取

获取bitmap的方式这里不赘述,根据不同的业务场景,有不同的获取方式:

  1. BitmapFactory.decodeXXX方法,支持从文件、drawable资源、byte[]、InputStream中获取Bitmap

  2. Fresco图片加载框架获取最终展示的bitmap方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    new BaseControllerListener<Object>() {
    @Override
    public void onFinalImageSet(String id, @Nullable Object imageInfo, @Nullable Animatable animatable) {
    super.onFinalImageSet(id, imageInfo, animatable);
    if (imageInfo instanceof CloseableStaticBitmap) {
    CloseableStaticBitmap staticBitmap = (CloseableStaticBitmap) imageInfo;
    // 获取到最终展示的bitmap
    Bitmap bitmap = staticBitmap.getUnderlyingBitmap();
    }
    }
    ...
    }
  3. View 的setDrawingCacheEnabled(boolean enabled)方法

Matrix相关

Matrix相关知识详解 android matrix 最全方法详解

Shader相关

Shader 是一个基类对象,在绘制时会返回一个水平跨越的颜色对象,主要功能是在绘制时通过setShader方法设置着色器的子类对象之后,任何对象(除了位图之外)都可从着色器中得到它的想要的颜色。系统提供的子类有:

  • BitmapShader,位图的图像渲染器,上述就利用了Bitmap Shader来实现了放大镜效果;

  • ComposeShader ,组合渲染器;

  • LinearGradient,线性渲染器;

  • SweepGradient,梯度渲染器(即扫描渲染),可以使用该渲染器实现,如:微信等雷达扫描效果、手机卫士垃圾扫描。

  • RadialGradient,环形渲染器,一般的水波纹效果,充电水波纹扩散效果、调色板都可以使用该渲染器实现。

RenderNode

RecordingCanvas

小白学理财——可转债

发表于 2021-08-12 | 分类于 理财 , 可转债

https://rainmonth.github.io/posts/c1f07320.html

可转债定义

  可转换公司债券(简称可转换债券)是指发行人依照法定程序发行,在一定期限内依据约定的条件可以转换为股份的公司债券。这种债券兼具债权和认股权双重属性。

可转债每个交易日9:15开始交易。

  债券现货采用竞价交易方式,交易时间为:

  每个交易日9:15-9:25为开盘集合竞价时间;9:30-11:30,13:00-14:57为连续竞价时间;14:57-15:00为收盘集合竞价时间。

内盘与外盘

  • 内盘是指主动卖出成交的数量,即卖方主动以低于或等于当前买一、买二、买三等价格下单卖出股票时成交的数量,用绿色显示。
  • 外盘是指以主动买入成交的数量,即买方主动以高于或等于当前卖一、卖二、卖三等价格下单买入股票时成交的数量,用红色显示。

博客导航——Android篇

发表于 2021-08-07 | 分类于 Android

本文链接:https://rainmonth.github.io/posts/D210807.html

Android部分,就在下面罗列

Android基础

  • [ ] Android 基础——View 知识点
  • [ ] Android 基础——Canvas
  • [ ] Android 基础——Kotlin基础语法

Android工具命令网站

  • [x] Android 工具命令——常用的网站
  • [x] Android 工具命令——发布项目到JitPack
  • [x] Android 工具命令——发布项目到Maven
  • [x] Android 工具命令——使用Android快速制作.9
  • [x] Android 工具命令——adb学习
  • [x] Android 工具命令——Gradle学习
  • [x] Android 工具命令——Gradle配置问题
  • [x] Android 工具命令——Repo命令简介
  • [x] Android 工具命令——Android Studio 升级到3.0运行项目指北
  • [x] Android 工具命令——Gradle学习day1基本的gradle命令
  • [x] Android 工具命令——Gradle学习day2 Android项目里面的gradle配置解析

Android 进程保活

  • [ ] Android 进程保活——守护进程

Android开源项目分析系列

主要罗列开源库的文章,分析的进度

  • [ ] Android开源库分析——基于ExoPlayer封装的音视频播放器
  • [ ] Andorid开源库分析——理解EXOPlayer的Timeline
  • [ ] Android开源库分析——浅谈Retrofit封装
  • [ ] Android开源库分析——图片加载库的比较与封装
  • [x] Andorid开源库分析——ARouter使用及源码分析
  • [x] Andorid开源库分析——Dagger2 简介
  • [ ] Andorid开源库分析——ExoPlayer使用与分析
  • [x] Andorid开源库分析——greenDAO使用
  • [ ] Andorid开源库分析——IjkPlayer简介
  • [x] Andorid开源库分析——KLog源码分析
  • [x] Andorid开源库分析——LeakCanary分析
  • [x] Android开源库分析——OkHttp3
  • [ ] Andorid开源库分析——Retrofit源码分析
  • [ ] Andorid开源库分析——RxJava深入理解篇
  • [ ] Andorid开源库分析——RxJava源码分析
  • [x] Android开源库分析——Fresco

Android控件

  • [x] Android控件——动画的深入分析
  • [ ] Android 控件——放大镜实现
  • [x] Android 时间选择相关系统控件
  • [x] Android 是时候了解一波MD库控件了
  • [x] Android 控件——自定义键盘
  • [x] Android SurfaceView详解
  • [x] Android 控件——View的事件体系
  • [x] Android 控件——ViewPager的无限循环与自动滚动实现

Android适配

  • [ ] Android 适配——动态权限管理
  • [ ] Android 适配——屏幕适配方案研究
  • [ ] Android 适配——全局悬浮窗的实现
  • [ ] Android 适配——原生与H5交互
  • [ ] Android 适配——状态栏攻略
  • [ ] Android 适配——Notification适配指南

Android四大组件

  • [x] Android Activity启动模式详解
  • [ ] Android 四大组件——Activity与Fragment的通信方式
  • [ ] Android 四大组件——ActivityRecord分析
  • [ ] Android 四大组件——Android开发艺术探索读书笔记
  • [ ] Android 四大组件之BroadcastRecord
  • [ ] Android 四大组件之ContentProviderRecord)
  • [ ] Android 四大组件——Fragment详解
  • [ ] Android 四大组件——Service详解
  • [ ] Android 四大组件之ServiceRecord

Android通用功能封装

  • [ ] Android通用功能封装0——开篇
  • [x] Android通用功能封装1——通用工具类封装
  • [ ] Android 通用功能封装2——图片加载
  • [ ] Android 通用功能封装3——文件选择
  • [ ] Android 通用功能封装4——文件下载
  • [ ] Android 通用功能封装5——网络库封装
  • [ ] Android 通用功能封装6——数据库封装
  • [ ] Android 通用功能封装7——广播管理

Android系统功能

  • [ ] Android 系统功能——震动相关实现

Android系统架构

  • [ ] Android 系统架构——Android系统架说明
  • [ ] Android 系统架构——组件化开发实践
  • [ ] Andorid 系统架构——Jetpack实践
  • [ ] Android 系统架构——JVM、Dalvik和ART
  • [ ] Android 系统架构——MVP模式、MVC模式和MVVM模式

Android系统源码分析

  • [ ] Android 系统源码分析——MacOs下的源码下载及编译
  • [ ] Android 系统源码分析——Android Studio导入Android8.0源码
  • [ ] Android 系统源码分析——进程的创建过程
  • [ ] Android 系统源码分析——系统的启动
  • [ ] Android 系统源码分析——Binder线程池工作过程
  • [ ] Android 系统源码分析——init进程
  • [ ] Android 系统源码分析——IPC机制
  • [ ] Android 系统源码分析——Handler机制
  • [ ] Android 系统源码分析——Launcher3分析开篇
  • [ ] Android 系统源码分析——Launcher3分析之LauncherModel
  • [ ] Android 系统源码分析——Launcher3分析之Workspace
  • [ ] Android 系统源码分析——AMS分析
  • [ ] Android 系统源码分析——WMS分析
  • [ ] Android 系统源码分析——MotionEvent

Android性能优化系列

  • [ ] Android 性能优化——布局优化
  • [ ] Android 性能优化——内存优化
  • [ ] Android 性能优化——启动优化
  • [ ] Android 性能优化——结合KaDa故事谈谈Android性能优化

Android学不过来系列

Android音视频基础

  • [x] 音视频基础0——目录
  • [x] 音视频基础1——AudioTrack、AudioRecorder、MediaRecorder音频数据采集播放处理
  • [ ] 音视频基础2——Android Camera API及Camera2 API的使用
  • [ ] 音视频基础3——Android Camera API及Camera2 采集视频数据
  • [ ] 音视频基础4——Android MediaCodec API学习
  • [ ] 音视频基础5——Android 实现rtmp推流
  • [x] 音视频基础6——交叉编译动态库
  • [ ] 音视频基础7——MacOs下编译FFMpeg4.2.2
  • [ ] 音视频基础8——音频焦点管理
  • [ ] Android 音视频基础——官方关于多媒体的说明介绍

Android音视频进阶

  • [ ] Android 音视频进阶——OpenGL基础

Android NDK开发

  • [x] Android之NDK与JNI简单介绍及NDK开发demo实现

Android杂谈

  • [x] Android 杂谈——从一个简单的弹窗说起
  • [x] Android 杂谈零散知识点(持续更新)
  • [x] Android 杂谈——SdkVersion那些事儿
  • [x] Android 杂谈——Interview Prepare
  • [x] Android 杂谈——Activity与Fragment恢复的那些事儿

博客导航——Java篇

发表于 2021-08-06 | 分类于 Android

本文链接:https://rainmonth.github.io/posts/D210806.html

Java部分

设计模式

  • [x] Java 设计模式00——开篇

    创建型(5种)

  • [x] Java 设计模式01——单例模式
  • [x] Java 设计模式02——工厂方法模式
  • [x] Java 设计模式03——抽象工厂模式
  • [x] Java 设计模式04——建造者模式
  • [x] Java 设计模式05——原型模式

    结构型(7种)

  • [x] Java 设计模式06——适配器模式
  • [x] Java 设计模式07——桥接模式
  • [x] Java 设计模式08——组合模式
  • [x] Java 设计模式09——装饰器模式
  • [x] Java 设计模式10——外观模式
  • [x] Java 设计模式11——享元模式
  • [x] Java 设计模式12——代理模式

    行为型(11种)

  • [x] Java 设计模式13——模板方法模式
  • [x] Java 设计模式14——命令模式
  • [x] Java 设计模式15——责任链模式
  • [x] Java 设计模式16——策略模式
  • [x] Java 设计模式17——中介者模式
  • [x] Java 设计模式18——观察者模式
  • [x] Java 设计模式19——备忘录模式
  • [x] Java 设计模式20——访问者模式
  • [x] Java 设计模式21——状态模式
  • [x] Java 设计模式22——解释器模式
  • [x] Java 设计模式23——迭代器模式

Java并发

  • [ ] Java 并发——基础
  • [ ] Java 并发——死锁
  • [ ] Java 并发——线程池
  • [ ] Java 并发——Atomic相关类
  • [ ] Java 并发——CountDownLatch
  • [ ] Java 并发——Future详解
  • [ ] Java 并发——Semaphore

Java反射

Java集合

  • [ ] Java 集合——简介
  • [ ] Java集合HashMap

Java注解

Java杂谈

  • [ ] Java杂谈——不同Java版本的特性比较说明
  • [ ] Java 杂谈——取文件夹大小的几种方式
  • [ ] Java 杂谈——AOP简介及简单使用
<i class="fa fa-angle-left" aria-label="上一页"></i>1…8910…22<i class="fa fa-angle-right" aria-label="下一页"></i>

216 日志
43 分类
43 标签
GitHub
© 2025 Randy Zhang
由 Hexo 强力驱动
|
主题 — NexT.Gemini v6.1.0