Skip to content

Commit e5b7431

Browse files
committed
update guide layer & add doc
1 parent d0f32fd commit e5b7431

3 files changed

Lines changed: 253 additions & 15 deletions

File tree

app/src/main/java/com/haoge/sample/easyandroid/activities/EasyGuideLayerActivity.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class EasyGuideLayerActivity: EasyDimensionActivity() {
5252

5353
private fun GuideItem.setTextLayout(message:String):GuideItem {
5454
return this.setLayout(R.layout.guide_text_layout)
55-
.setOnViewCreatedListener { view, controller ->
55+
.setOnViewAttachedListener { view, controller ->
5656
(view as TextView).text = message
5757
// 点击后。关闭自身的引导层。
5858
view.setOnClickListener { controller.getGuideLayer().removeItem(this).show() }
@@ -78,7 +78,6 @@ private fun EasyGuideLayer.addHighLightForTransform(activity: Activity): EasyGui
7878
.setOnHighLightClickListener { it.getGuideLayer().removeItem(item).show() }
7979
.setOnDrawHighLightCallback { canvas, rect, paint ->
8080
canvas.drawRoundRect(rect, 20f, 20f, paint)
81-
true
8281
}
8382
addItem(item)
8483
}

docs/EasyGuideLayer.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# EasyGuideLayer
2+
3+
EasyGuideLayer是用于进行蒙层引导实现的一个组件。
4+
5+
[Sample Activity](../app/src/main/java/com/haoge/sample/easyandroid/activities/EasyGuideLayerActivity.kt)
6+
7+
## 特性
8+
9+
- 链式调用。调用逻辑清晰直观
10+
- 支持同时设置多个引导层
11+
- 支持高亮区域的自定义绘制
12+
- 支持高亮区域点击监听
13+
- 支持指定任意View设置蒙层引导
14+
- 支持进行蒙层展示、隐藏事件监听
15+
16+
## 用法
17+
18+
用法概览
19+
20+
```
21+
// step1: 创建layer蒙层实例。并添加基础配置
22+
val layer = EasyGuideLayer.with(activity)...
23+
24+
// step2: 创建引导item实例,添加引导配置并绑定给layer:
25+
val item = GuideItem.newInstance(view, padding)...
26+
layer.addItem(item)
27+
28+
// step3: 请求进行蒙层展示:
29+
layer.show()
30+
```
31+
32+
实例说明:
33+
34+
1. `EasyGuideLayer`:为蒙层实例。为底部半透明的覆盖层。
35+
2. `GuideItem`: 为引导层实例。盖在蒙层之上,展示的`引导View`以及对应的`高亮区域`显示部分。
36+
37+
### 创建蒙层实例。
38+
39+
1. 蒙层实例的创建分为两种方式:
40+
41+
```
42+
// 1. 直接使用activity. 表示直接在content布局上展示蒙层
43+
val layer = EasyGuideLayer.with(activity)
44+
// 2. 手动指定对应的View.表示在此View布局上展示蒙层
45+
val layer = EasyGuideLayer.with(view)
46+
```
47+
48+
2. 为蒙层添加背景色:
49+
50+
```
51+
layer.setBackgroundColor(color)// 若不设置,为默认背景色:0x33000000
52+
```
53+
54+
3. 设置蒙层的展示/消失监听器:
55+
56+
```
57+
layer.setOnGuideShownListener { isShowing:Boolean -> // isShowing为true表示蒙层在展示,否则为消失 }
58+
```
59+
60+
4. 设置当点击到非预期区域(即非内部view点击以及非高亮区域点击事件)的时候,是否自动关闭蒙层
61+
62+
```
63+
layer.setDismissOnClickOutside(false)
64+
```
65+
66+
5. 设置当未添加有引导层实例GuideItem时。是否自动将蒙层关闭:
67+
68+
```
69+
layer.setDismissIfNoItems(false)
70+
```
71+
72+
6. 设置引导层实例:
73+
74+
一个蒙层的引导层可以添加多个。所以也就对应有新增、移除、清空操作:
75+
76+
```
77+
// 1. 添加指定引导层实例:
78+
layer.addItem(item:GuideItem)
79+
// 2. 移除指定引导层实例:
80+
layer.removeItem(item)
81+
// 3. 清空所有的引导层实例:
82+
layer.clearItems()
83+
```
84+
85+
7. 配置完毕。发起蒙层展示请求:
86+
87+
```
88+
layer.show()
89+
```
90+
91+
### GuideItem引导层实例
92+
93+
在上面我们有提到`引导层`的概念,一个GuideItem实例表示一个具体的`引导层`,一个引导层相对于界面展示效果来说。
94+
即是指的对应的`高亮块`以及`引导View`
95+
96+
1. 引导层实例的创建:
97+
98+
GuideItem提供三个方法进行创建。根据不同的方法创建参数将会创建出不同的`高亮块区域`进行绘制。
99+
100+
```
101+
//高亮块区域为指定的`view`的位置再边框扩充padding尺寸(单位为px)后的大小
102+
val item = GuideItem.newInstance(view:View, padding:Int)
103+
```
104+
105+
```
106+
// 直接指定具体的高亮矩形区域
107+
val item = GuideItem.newInstance(rect:Rect)
108+
```
109+
110+
```
111+
// 不指定,则将高亮块区域设置为Rect(0,0,0,0)
112+
val item = GuideItem.newInstance()
113+
```
114+
115+
2. 设置引导View
116+
117+
一个引导层`GuideItem`可以绑定一个`引导View`。此View将被添加到蒙层布局中,并展示在蒙层顶部
118+
119+
设置引导View的方式有两种:
120+
121+
- 直接指定具体的引导View的layout:
122+
123+
```
124+
item.setlayout(@LayoutRes layout)
125+
```
126+
127+
- 或者直接指定具体的图片drawable。让组件创建个adjustViewBounds为true的ImageView进行使用
128+
129+
```
130+
item.setDrawable(@DrawableRes drawable)
131+
```
132+
133+
有时候我们会需要在`引导View`被添加到布局中去的时候。进行一些额外处理。比如内部部分控件`点击监听`等。
134+
135+
```
136+
item.setOnViewAttachedListener { view:View, controller:Controller ->
137+
// view: 被创建的具体引导View实例。
138+
// controller: 提供的控制器。可使用此实例在点击后让蒙层消失或对蒙层进行更新显示
139+
// 注意:此处view刚被添加到蒙层布局中,所以不能在此获取view的具体大小尺寸等信息
140+
}
141+
```
142+
143+
3. 设置引导View的相对位置
144+
145+
```
146+
item.setGravity(gravity:Int)
147+
```
148+
149+
引导View的相对位置是相对于`高亮块区域rect`的, 支持5种相对位置关系:
150+
151+
- Gravity.LEFT: 位于高亮区域`左侧`,顶点(left, top)为`rect.left - view.width``rect.top`
152+
- Gravity.TOP: 位于高亮区域`顶部`,顶点为`rect.left``rect.top - view.height`
153+
- Gravity.RIGHT: 位于高亮区域`右侧`,顶点为`rect.right``rect.top`
154+
- Gravity.BOTTOM: 位于高亮区域`底部`,顶点为`rect.left``rect.bottom`
155+
- Gravity.NO_GRAVITY: 顶点为`rect.left``rect.top`
156+
157+
4. 对引导View的位置进行微调
158+
159+
`gravity`位置是比较单一的,所以为了满足更多的位置要求。组件也提供的对应的回调允许进行`位置偏移调整`
160+
161+
```
162+
item.setOffsetProvider {
163+
point:Point, // 顶点位置
164+
rect:RectF, // 高亮块区域
165+
view:View -> // 具体的引导View实例。此处view已被测量大小。可以直接获取width、height数据
166+
// TODO 在此计算好偏移量后。设置给point调整顶点位置即可
167+
}
168+
```
169+
170+
5. 设置高亮绘制模式
171+
172+
```
173+
item.setHighLightShape(shapeType:Int)
174+
```
175+
176+
shapeType(高亮显示效果)组件自带三种模式:
177+
178+
- GuideItem.SHAPE_NONE: 默认模式,表示不进行高亮块绘制。
179+
- GuideItem.SHAPE_RECT: 矩形模式,表示将高亮块绘制为矩形进行展示
180+
- GuideItem.SHAPE_OVAL: 椭圆模式,表示将高亮块绘制为椭圆进行展示
181+
182+
5. 自定义高亮绘制逻辑
183+
184+
自带的绘制模式永远不可能满足设计师们的要求。所以也提供了回调做`自定义高亮块绘制`
185+
186+
```
187+
item.setOnDrawHighLightCallback {
188+
canvas:Canvas, // 蒙层的画布
189+
rect:RectF, // 高亮块
190+
paint:Paint -> // 绘制的画笔。模式为PorterDuff.Mode.CLEAR。
191+
// 在此进行自定义绘制
192+
}
193+
```
194+
195+
需要注意的点:
196+
197+
- 绘制回调运行在onDraw方法中。所以请避免在此进行`对象创建操作`
198+
- 避免对提供的paint的属性进行修改,如果需要,请在外部额外创建单独的paint提供使用
199+
- 当配置了绘制回调时,则说明不再使用本身的`shapeType`模式进行高亮绘制。
200+
201+
6. 添加高亮块点击监听
202+
203+
高亮块的点击,只有在绘制模式为`非 SHAPE_NONE`时,才能被触发。
204+
205+
```
206+
item.setOnHighLightClickListener { controller:Controller ->
207+
// 进行点击后的操作
208+
}
209+
```
210+
211+
### Controller控制说明
212+
213+
Controller类用于对蒙层进行控制:
214+
215+
1. 让蒙层消失:
216+
217+
```
218+
controller.dismiss()
219+
```
220+
221+
2. 更新蒙层信息并展示:
222+
223+
```
224+
// 获取蒙层示例
225+
val layer = controller.getLayer()
226+
...// 操作蒙层示例。更新蒙层数据
227+
// 更新数据后。重新发起蒙层展示请求。更新蒙层展示
228+
layer.show()
229+
```
230+

utils/src/main/java/com/haoge/easyandroid/easy/EasyGuideLayer.kt

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import android.graphics.*
1010
import android.graphics.drawable.Drawable
1111
import android.support.annotation.DrawableRes
1212
import android.support.annotation.IntDef
13+
import android.support.annotation.LayoutRes
1314
import android.view.*
1415
import android.widget.FrameLayout
1516
import android.widget.ImageView
1617
import com.haoge.easyandroid.EasyAndroid
1718

1819
private typealias OnGuideShowListener = (Boolean) -> Unit
19-
private typealias OnGuideViewCreatedListener = (View, EasyGuideLayer.Controller) -> Unit
20-
private typealias OnDrawHighLightCallback = (canvas:Canvas, rect:RectF, paint:Paint) -> Boolean
20+
private typealias OnGuideViewAttachedListener = (View, EasyGuideLayer.Controller) -> Unit
21+
private typealias OnDrawHighLightCallback = (canvas:Canvas, rect:RectF, paint:Paint) -> Unit
2122
private typealias OnHighLightClickListener = (EasyGuideLayer.Controller) -> Unit
2223
private typealias OnGuideViewOffsetProvider = (Point, RectF, View) -> Unit
2324
class EasyGuideLayer private constructor(private val anchor: View){
@@ -132,11 +133,13 @@ class EasyGuideLayer private constructor(private val anchor: View){
132133
private var layer:EasyGuideLayer? = null
133134
private var container = mutableMapOf<RectF, GuideItem>()
134135
private val paint = Paint()
136+
private var copyPaint: Paint
135137

136138
init {
137139
paint.isAntiAlias = true
138140
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
139141
paint.maskFilter = BlurMaskFilter(10f, BlurMaskFilter.Blur.INNER)
142+
copyPaint = Paint(paint)
140143
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
141144
setWillNotDraw(true)
142145
setOnClickListener(this)
@@ -159,7 +162,7 @@ class EasyGuideLayer private constructor(private val anchor: View){
159162
val child = createItemView(item)
160163
child.setTag(GuideItem.TAG_ID, item)
161164
addView(child)
162-
item.getOnViewCreatedListener()?.invoke(child, this)
165+
item.getOnViewAttachedListener()?.invoke(child, this)
163166
}
164167
}
165168

@@ -239,12 +242,15 @@ class EasyGuideLayer private constructor(private val anchor: View){
239242
}
240243

241244
override fun onDraw(canvas: Canvas) {
242-
container.forEach {
243-
if (it.value.getOnDrawHighLightListener()?.invoke(canvas, it.key, paint) == true) return@forEach
245+
container.forEach { entry ->
246+
entry.value.getOnDrawHighLightListener()?.let {
247+
it.invoke(canvas, entry.key, copyPaint)
248+
return@forEach
249+
}
244250

245-
when(it.value.getShapeType()) {
246-
GuideItem.SHAPE_RECT -> canvas.drawRect(it.key, paint)
247-
GuideItem.SHAPE_OVAL -> canvas.drawOval(it.key, paint)
251+
when(entry.value.getShapeType()) {
252+
GuideItem.SHAPE_RECT -> canvas.drawRect(entry.key, paint)
253+
GuideItem.SHAPE_OVAL -> canvas.drawOval(entry.key, paint)
248254
}
249255
}
250256
}
@@ -295,7 +301,7 @@ class EasyGuideLayer private constructor(private val anchor: View){
295301
* */
296302
class GuideItem private constructor(val rect: RectF? = null, val view: View? = null, val padding: Int = 0){
297303
private var shapeType = SHAPE_NONE
298-
private var onViewCreated:OnGuideViewCreatedListener? = null
304+
private var onViewAttached:OnGuideViewAttachedListener? = null
299305
private var onDrawHighLight:OnDrawHighLightCallback? = null
300306
private var onHighLightClickListener:OnHighLightClickListener? = null
301307
private var offsetProvider:OnGuideViewOffsetProvider? = null
@@ -304,7 +310,7 @@ class GuideItem private constructor(val rect: RectF? = null, val view: View? = n
304310
private var gravity = Gravity.NO_GRAVITY
305311

306312
fun getShapeType() = shapeType
307-
fun getOnViewCreatedListener() = onViewCreated
313+
fun getOnViewAttachedListener() = onViewAttached
308314
fun getOnDrawHighLightListener() = onDrawHighLight
309315
fun getOnHighLightClickListenter() = onHighLightClickListener
310316
fun getDrawable() = drawable
@@ -333,8 +339,8 @@ class GuideItem private constructor(val rect: RectF? = null, val view: View? = n
333339
* 1. View:被创建的引导View。
334340
* 2. Controller: 提供给上层使用的控制器。可用于进行蒙层关闭或者蒙层引导更新等操作。
335341
*/
336-
fun setOnViewCreatedListener(onViewCreatedListener: OnGuideViewCreatedListener?): GuideItem {
337-
this.onViewCreated = onViewCreatedListener
342+
fun setOnViewAttachedListener(onViewAttachedListener: OnGuideViewAttachedListener?): GuideItem {
343+
this.onViewAttached = onViewAttachedListener
338344
return this
339345
}
340346

@@ -361,7 +367,7 @@ class GuideItem private constructor(val rect: RectF? = null, val view: View? = n
361367
/**
362368
* 设置引导层View的布局id。与[setDrawable]互斥。
363369
*/
364-
fun setLayout(layout:Int): GuideItem {
370+
fun setLayout(@LayoutRes layout:Int): GuideItem {
365371
this.layout = layout
366372
this.drawable = null
367373
return this
@@ -403,8 +409,11 @@ class GuideItem private constructor(val rect: RectF? = null, val view: View? = n
403409
const val SHAPE_OVAL = 1
404410

405411
internal const val TAG_ID = 0x0F000000
412+
/** 不指定具体的高亮绘制区域,使用默认区域Rect(0,0,0,0)*/
406413
fun newInstance() = GuideItem()
414+
/** 通过指定view与padding创建具体的高亮绘制区域*/
407415
fun newInstance(view: View, padding: Int = 0) = GuideItem(view = view, padding = padding)
416+
/** 直接指定具体的高亮绘制区域*/
408417
fun newInstance(rect: RectF) = GuideItem(rect = rect)
409418
}
410419
}

0 commit comments

Comments
 (0)