您的位置:首页 > 其它

利用DecorView实现播放视频开灯/关灯效果和仿QQ消息提示框

2018-02-05 23:33 639 查看


2017再见,2018你好

写这篇文章前得先来了解一下
activity.getWindow().getDecorView()
获取到的
DecorView
视图

首先我们通过强大的
Android Studio
开发神器,获取我们当前app界面的布局层次(结构)来帮助

我们了解
DecorView
是个什么?

依次点击菜单
Tools——>Android——>Layout Inspector
然后选择一个app所在的进程就可以抓取界面的布局结构了一览无余。



分析界面的布局结构



通过上面截图我们发现最顶层的视图就是
PhoneWindow中的DecorView
了,也就是这篇文章使用到的重点。

DecorView
是一个
FrameLayout
不信你
instanceof
一下

我们每次通过Activity的
setContentView()
函数加载布局时,都是加在图中的
content(ContentFrameLayout)


咳咳咳废话不多说了,直接进入今天要说的仿QQ消息提示框
ActionBarToast
(名字瞎起的不要在意),楼下放效果图



实现的思路:

首先获取DecorView根视图

初始化展示的视图 高度为顶部状态栏的高度加上ActionBar(标题栏)的高度

让显示的视图在ActionBar中垂直居中(布局放一个高度为状态栏高度的View占位即可)

往DecorView根视图 添加/移除 我们要展示的视图(使用属性动画)

1.首先获取DecorView根视图

//仅需一行代码即可获取
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
1
2
[/code]

2. 初始化展示的视图

我们要展示的布局
item_toast.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white">

<!--用来占位(状态栏高度)
这样让显示的内容正好在actionBar的中间-->
<View
android:id="@+id/status_bar"
android:layout_width="match_parent"
android:layout_height="1dp"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginLeft="16dp"
android:src="@drawable/icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/status_bar" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="每天最多给她点10个赞哦。"
android:textColor="#131313"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/icon"
app:layout_constraintTop_toBottomOf="@+id/status_bar" />
</android.support.constraint.ConstraintLayout>
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
[/code]

获取状态栏和ActionBar的高度

/**
* 获取状态栏 + 标题栏 的高度
*/
private int getHeight(Context context, View v) {
//标题栏
TypedArray values = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
int actionBarHeight = values.getDimensionPixelSize(0, 0);
values.recycle();
//状态栏
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
//设置布局 占位视图的高度
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
v.setLayoutParams(new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT, statusBarHeight));
}
return actionBarHeight + statusBarHeight;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[/code]

初始化好要展示的视图

View view = LayoutInflater.from(activity).inflate(R.layout.item_toast, null);
//设置View的高度
int totalHeight = getHeight(activity, view.findViewById(R.id.status_bar));
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, totalHeight));
1
2
3
4
5
[/code]

3. 往DecorView根视图 添加/移除 我们要展示的视图(使用属性动画)

/**
* 显示
*
* @param duration 显示时长
*/
public void showToast(@Duration int duration) {
if (!showing) {
showing = true;
decorView.addView(view);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", -totalHeight, 0f);
animator.setDuration(animationDuration);
animator.start();
if (duration != LENGTH_SHOW) {
//一段时间后隐藏
view.postDelayed(runnable, duration);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[/code]

/**
* 延时执行取消操作
*/
private Runnable runnable = new Runnable() {
@Override
public void run() {
cancel();
}
};
1
2
3
4
5
6
7
8
9
[/code]

重要的操作也就上面这些,主要还是实现的思路。

ActionBarToast完整源码(使用方法可以下载文末Demo查看)

同样使用
DecorView
实现一个观看视频时 开灯/关灯 的效果,如下图:



实现的思路:

往DecorView添加自定义的视图(黑色蒙版)

获取我们不加蒙版View在屏幕上的坐标(左上角)

使用path进行绘制蒙版区域

移除蒙版

添加我们自定义的View蒙版,设置大小充满屏幕,获取View在屏幕上的坐标。

public class MaskView extends View {

/**
* activity 根视图
*/
private ViewGroup decorView;
/**
* 不加遮罩的视图
*/
private View brightView;
/**
* 遮罩画笔
*/
private Paint paint;
/**
* 不遮罩的view
*/
private RectF brightRectF;
/**
* 遮罩的路径
*/
private Path path;

/**
* 是否已经添加了
*/
private boolean showing = false;

public MaskView(Context context) {
super(context);
init(context);
}

private void init(Context context) {
//获取activity顶层视图
decorView = (ViewGroup) ((Activity) context).getWindow().getDecorView();
//初始化蒙版视图,充满全屏幕
setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
paint = new Paint();
//设置为黑色 在加点透明度
paint.setColor(Color.argb(230, 0, 0, 0));
path = new Path();

}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (decorView == null || brightView == null) {
return;
}
//路径恢复
path.reset();
//A
path.moveTo(brightRectF.left, brightRectF.top);
//B
path.lineTo(brightRectF.right, brightRectF.top);
//C
path.lineTo(brightRectF.right, brightRectF.bottom);
//D
path.lineTo(brightRectF.left, brightRectF.bottom);
//A
path.lineTo(brightRectF.left, brightRectF.top);
//E
path.lineTo(0, brightRectF.top);
//F
path.lineTo(0, decorView.getHeight());
//F
path.lineTo(decorView.getWidth(), decorView.getHeight());
//H
path.lineTo(decorView.getWidth(), 0);
//I
path.lineTo(0, 0);
//E
path.lineTo(0, brightRectF.top);
//A
path.lineTo(brightRectF.left, brightRectF.top);
//闭合曲线
path.close();
canvas.drawPath(path, paint);
}

/**
* 添加不加蒙版的View
*
* @param view 视图
*/
public void attachView(View view) {
if (!showing) {
this.brightView = view;
//没有添加,才可以添加
initMask();
}
}

/**
* 移除遮罩
*/
public void removeMask() {
if (!showing) {
//没有添加
return;
}
//使用动画消失
AlphaAnimation animation = new AlphaAnimation(1f, 0f);
animation.setDuration(500);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}

@Override
public void onAnimationEnd(Animation animation) {
ViewParent parent = MaskView.this.getParent();
if (parent != null && parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(MaskView.this);
showing = false;
}
}

@Override
public void onAnimationRepeat(Animation animation) {

}
});
startAnimation(animation);

}

/**
* 初始化不加遮罩view的位置
*/
private void initMask() {
int[] location = new int[2];
//获取view在屏幕上的坐标
brightView.getLocationOnScreen(location);
brightRectF = new RectF(location[0], location[1], location[0] + brightView.getWidth(),
location[1] + brightView.getHeight());
//添加蒙版到Activity之上
decorView.addView(this);
invalidate();
showing = true;
}

/**
* 是否已经添加
*
* @return
*/
public boolean isShowing() {
return showing;
}
}
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
[/code]

使用当然也是很简单的了

MaskView maskView = new MaskView(this);
maskView.attachView(view);
//maskView.removeMask();
1
2
3
[/code]

为了更好理解
onDraw()
里的每个点,这里做了张图来辅助理解:



本篇文章源码下载

通过这篇文章相信你肯定对
DecorView
这个东西有了更清楚的了解不在感动陌生,相信只要发挥你的想象还可以作出更多好玩、好看的效果。

转自:http://blog.csdn.net/a_zhon/article/details/78988653
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: