Android开发技巧——高亮的用户操作指南
2015-12-15补记:
发现使用PopupWindow进行遮罩层的显示,在华为P7上会有问题。具体表现为:画出来的高亮部分会偏下。原因为:通过view.getRootView获取到DecorView,把其作为PopupWindow的anchorView来显示,然而在华为P7上依然是显示在status bar下面,而我们计算高亮时获取view的高度,是从decorView开始计算的,导致之间的距离相差了一个状态栏的高度。参考张鸿洋大神的做法对实现进行了修改,相关修改见文末。
一不小心成了博客之星的候选人,还有许多朋友帮我投票,无以回报,只好发篇博客以表谢意。
前面四篇写了关于自定义控件的一些基础知识。在我的理解中,其实做Android开发久了,在项目领域无非是更熟悉业务流程,而在Android的技术领域,基本上是走向两个方向,或是两个方向都走。
一是做界面上的开发,比如各种下拉刷新,酷炫的对话框,各种动效等,这其中有的是为了界面的更好实现,有些是为了设计的更好表现。
第二个方向则是非界面上的开发,比如热修复,事件总线,网络请求库,图片加载库等,侧重于解决某一层面的问题,提高效率,降低风险,或是其他原因。
我自己目前对第二个方向所涉较少,而对于第一个方向,接触比较多的就是自定义控件了。除了与属性相关的设置以及控件本身的方法的调用,自定义控件的表现主要还是在onDraw里的绘制,而onDraw里的绘制,基本上也是你能想到多远,加上所需的数学知识和相关API,就能做到多远。
需求
说得有点远了,这一篇主要分享的是操作指南的一种实现,聚光灯高亮某个控件。
如图所示,所谓高亮,其实只是把屏幕全屏用半透明的遮罩层挡住,而只留下其中某一控件不遮住,并在控件的附近加上具体的文字提示,而让用户的焦点都聚集到这一控件上,并看到提示的文字。然后点击圆圈里面的控件,有对应的响应事件。按返回键不能退出这个界面。
上面的例子还是相对比较好实现的,因为它没有QQ的提示里面的箭头,只是一个圆,并且在下面一行文字。本文也只讨论这种实现,有其他需求的可以自己扩展。
思路
全屏遮罩(不包括显示的系统状态栏),首先我所想到的就是PopupWindow
了。在PopupWindow
中显示一个View
,View
的背景为半透明黑色,并且中间抠出一个圆,然后下面再绘制一行文字就可以了。抠出一个圆的实现可以通过对Paint
调用setXfermode
,setXfermode
的参数可以参考《setXfermode》这篇博客。
那么思路如下:
1. 继承View,在onDraw(Canvas canvas)
方法中绘制一层半透明背景
2. 设置setXfermode(xfermode)
为PorterDuff.Mode.DST_OUT,计算圆所在坐标,并画出
3. 在圆的下方画出文字
4. 提供接口showTipForView(final View view, String tip, OnClickListener listener)
供外部调用,参数为:需要高亮的控件,文字提示,点击之后的回调。
5. 计算需要响应的点击区域,回调listener。
实现
下面是具体的实现过程。
聚光灯高亮
首先先实现高亮的效果,关于点击下一步再说。从上面的分析我们可以知道至少需要以下变量:
public class HighLightView extends View {
private static final float RADIUS_RATIO = 1f / 3;
private static final PorterDuffXfermode MODE_DST_OUT = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
private final Paint mPaint;
private int mOverlayColor;//遮罩层颜色
private int mRadius; // 圆半径
private int mCenterX; // 圆心横坐标
private int mCenterY; // 圆心纵坐标
private String mTip; // 提示文字
private float mTipX; // 文字横坐标
private float mTipY; // 文字纵坐标
private View mAnchorView; // 高亮的View
private final PopupWindow mPopupWindow;
}
然后在构造方法中,对mPopupWindow和mPaint进行初始化。由于我这边遮罩颜色是固定的,所以我也在这边一起初始化了。代码如下:
public HighLightView(Context context) {
super(context);
mPopupWindow = new PopupWindow(this, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, true);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
int textSize = getResources().getDimensionPixelSize(R.dimen.text_32pt);
mPaint.setTextSize(textSize);
mPaint.setColor(Color.WHITE);
mOverlayColor = getResources().getColor(R.color.guide_overlay);
}
然后我们实现在onDraw(Canvas canvas)
里的绘制,绘制代码很简单,画个遮罩层,设置Xfermode,画个圆,去掉Xfermode,画个文字。
@Override
protected void onDraw(Canvas canv