引导功能GuideView
GuideView源码仅有300余行代码;GuideView使用起来只需要4行代码。
效果
GuideView源码
```
importandroid.animation.ValueAnimator;
importandroid.app.Activity;
importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.graphics.PorterDuff;
importandroid.graphics.PorterDuffXfermode;
importandroid.graphics.Rect;
importandroid.text.TextUtils;
importandroid.util.DisplayMetrics;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.view.animation.DecelerateInterpolator;
import staticandroid.animation.ValueAnimator.REVERSE;
/**
* 引导图层
* Created by ZSC on 2017/7/11.
*/
public classGuideViewextendsView {
publicViewguideParent;
privateContextcontext;
private floatdensity;
publicGuideView(Context context) {
super(context);
this.context= context;
initSourcePaint();
initBgPaint();
initGuidePaint();
initTextPaint();
setLayerType(View.LAYER_TYPE_SOFTWARE, null);//用到Xfermode时必要的设置
}
private intbgWidth;//背景宽
private intbgHeight;//背景高
private intguideX;//需要引导的view的x坐标
private intguideY;//需要引导的view的y坐标
private intguideWidth;//需要引导的view的宽
private intguideHeight;//需要引导的view的高
private voidinitSize() {
if(contextinstanceofActivity) {
DisplayMetrics dm =newDisplayMetrics();
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
bgWidth= dm.widthPixels;//设置背景宽为屏幕宽
bgHeight= dm.heightPixels;//设置背景高为屏幕高
int[] location =new int[2];
guideParent.getLocationOnScreen(location);//得到需要引导的view在整个屏幕中的坐标
guideX= location[0];
guideY= location[1];
guideWidth=guideParent.getWidth();
guideHeight=guideParent.getHeight();
density= dm.density;//获取像素密度 2.0,2.5,3.0
distance=10*density;//箭头与需要引导的view之间的距离
}
}
public voidsetGuideParent(View guideParent) {
this.guideParent= guideParent;
initSize();
invalidate();
}
privatePaintsourcePaint;
privatePaintbgPaint;
privatePaintguidePaint;
privatePainttextPaint;
/**
* 绘制需要引导的view的区域的画笔
*/
private voidinitSourcePaint() {
sourcePaint=newPaint();
sourcePaint.setColor(Color.BLUE);
sourcePaint.setStyle(Paint.Style.FILL);
sourcePaint.setAntiAlias(true);
sourcePaint.setAlpha(255);
}
/**
* 半透明背景的画笔
*/
private voidinitBgPaint() {
bgPaint=newPaint();
bgPaint.setColor(Color.BLACK);
bgPaint.setStyle(Paint.Style.FILL);
bgPaint.setAntiAlias(true);
bgPaint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_OUT));//遮盖效果详细信息可以百度搜索PorterDuffXfermode
bgPaint.setAlpha(225);
}
/**
* 引导箭头的画笔
*/
private voidinitGuidePaint() {
guidePaint=newPaint();
guidePaint.setColor(Color.WHITE);
guidePaint.setStyle(Paint.Style.FILL);
guidePaint.setAntiAlias(true);
guidePaint.setAlpha(255);
}
/**
* 文字的画笔
*/
private voidinitTextPaint() {
textPaint=newPaint();
textPaint.setTextAlign(Paint.Align.LEFT);
textPaint.setColor(Color.WHITE);
textPaint.setStrokeWidth(density*2);
}
@Override
protected voidonDraw(Canvas canvas) {
if(bgPaint==null) {
return;
}
if(sourcePaint==null) {
return;
}
intlayerID = canvas.saveLayer(0,0,bgWidth,bgHeight,bgPaint,Canvas.ALL_SAVE_FLAG);
canvas.drawRect(guideX,guideY,guideX+guideWidth,guideY+guideHeight,sourcePaint);
canvas.drawRect(0,0,bgWidth,bgHeight,bgPaint);
intdirection;
if(guideY
direction =TOP;
drawArrow(direction,canvas,guideX+guideWidth/2,guideY+guideHeight+distance);
}else if(guideY>bgHeight/4*3) {
direction =BOTTOM;
drawArrow(direction,canvas,guideX+guideWidth/2,guideY-distance);
}else if(guideX
direction =LEFT;
drawArrow(direction,canvas,guideX+guideWidth+distance,guideY+guideHeight/2);
}else if(guideX>bgWidth/4*3) {
direction =RIGHT;
drawArrow(direction,canvas,guideX-distance,guideY+guideHeight/2);
}
drawGuideText(canvas);
canvas.restoreToCount(layerID);
}
@Override
protected voidonMeasure(intwidthMeasureSpec, intheightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
privateStringguideStr;
public voidsetGuideStr(String guideStr) {
this.guideStr= guideStr;
invalidate();
}
private voiddrawGuideText(Canvas canvas) {
if(!TextUtils.isEmpty(guideStr)) {
Rect bounds =newRect();
floattextSize =density*20.0f;
textPaint.setTextSize(textSize);
textPaint.getTextBounds(guideStr,0,guideStr.length(),bounds);
canvas.drawText(guideStr,bgWidth/2- bounds.width() /2,bgHeight/2+ bounds.height() /2,textPaint);
}
}
private final intTOP=0x01;
private final intBOTTOM=0x02;
private final intLEFT=0x03;
private final intRIGHT=0x04;
private floatdistance;
/**
* 画箭头
*
*@paramdirection上或者下方向的箭头
*@paramcanvas画布
*@paramx箭头顶点x坐标
*@paramy箭头顶点y坐标
*/
private voiddrawArrow(intdirection,Canvas canvas, floatx, floaty) {
final floattriangleWidth =20*density;
final floattriangleHeight =20*density;
final floatrectWidth =10*density;
final floatrectHeight =35*density;
floatvertexOneX = x;
floatvertexOneY = y;
floatvertexTwoX = x;
floatvertexTwoY = y;
floatrectOneX = x;
floatrectOneY = y;
floatrectTwoX = x;
floatrectTwoY = y;
floatrectThreeX = x;
floatrectThreeY = y;
floatrectFourX = x;
floatrectFourY = y;
switch(direction) {
caseTOP:
vertexOneX = x - triangleWidth /2;
vertexOneY = y + triangleHeight;
vertexTwoX = x + triangleWidth /2;
vertexTwoY = y + triangleHeight;
rectOneX = x - rectWidth /2;
rectOneY = y + triangleHeight;
rectTwoX = x + rectWidth /2;
rectTwoY = y + triangleHeight;
rectThreeX = x + rectWidth /2;
rectThreeY = y + triangleHeight + rectHeight;
rectFourX = x - rectWidth /2;
rectFourY = y + triangleHeight + rectHeight;
break;
caseBOTTOM:
vertexOneX = x + triangleWidth /2;
vertexOneY = y - triangleHeight;
vertexTwoX = x - triangleWidth /2;
vertexTwoY = y - triangleHeight;
rectOneX = x + rectWidth /2;
rectOneY = y - triangleHeight;
rectTwoX = x - rectWidth /2;
rectTwoY = y - triangleHeight;
rectThreeX = x - rectWidth /2;
rectThreeY = y - triangleHeight - rectHeight;
rectFourX = x + rectWidth /2;
rectFourY = y - triangleHeight - rectHeight;
break;
caseLEFT:
vertexOneX = x + triangleHeight;
vertexOneY = y + triangleWidth /2;
vertexTwoX = x + triangleHeight;
vertexTwoY = y - triangleWidth /2;
rectOneX = x + triangleHeight;
rectOneY = y + rectWidth /2;
rectTwoX = x + triangleHeight;
rectTwoY = y - rectWidth /2;
rectThreeX = x + triangleHeight + rectHeight;
rectThreeY = y - rectWidth /2;
rectFourX = x + triangleHeight + rectHeight;
rectFourY = y + rectWidth /2;
break;
caseRIGHT:
vertexOneX = x - triangleHeight;
vertexOneY = y - triangleWidth /2;
vertexTwoX = x - triangleHeight;
vertexTwoY = y + triangleWidth /2;
rectOneX = x - triangleHeight;
rectOneY = y - rectWidth /2;
rectTwoX = x - triangleHeight;
rectTwoY = y + rectWidth /2;
rectThreeX = x - triangleHeight - rectHeight;
rectThreeY = y + rectWidth /2;
rectFourX = x - triangleHeight - rectHeight;
rectFourY = y - rectWidth /2;
break;
}
Path trianglePath =newPath();
trianglePath.moveTo(x,y);
trianglePath.lineTo(vertexOneX,vertexOneY);
trianglePath.lineTo(vertexTwoX,vertexTwoY);
trianglePath.close();
canvas.drawPath(trianglePath,guidePaint);
Path rectPath =newPath();
rectPath.moveTo(rectOneX,rectOneY);
rectPath.lineTo(rectTwoX,rectTwoY);
rectPath.lineTo(rectThreeX,rectThreeY);
rectPath.lineTo(rectFourX,rectFourY);
rectPath.close();
canvas.drawPath(rectPath,guidePaint);
}
/**
* 重写onTouchEvent,实现触摸之后就销毁GuideView的功能
* 因为重写了onTouchEvent 所有View 的OnClickListener等将不能正常使用
*@paramevent触摸
*@returnboolean true-触摸被自己消耗掉,false-触摸传到下一层
*/
@Override
public booleanonTouchEvent(MotionEvent event) {
if(added){
((ViewGroup) ((Activity)context).getWindow().getDecorView()).removeView(this);
added=false;
}
if(animator!=null){
if(animator.isRunning()){
animator.end();
}
}
return true;
}
privateValueAnimatoranimator;
private voidinitAnim() {
animator= ValueAnimator.ofFloat(0.0f,1.0f);
animator.addUpdateListener(newValueAnimator.AnimatorUpdateListener() {
@Override
public voidonAnimationUpdate(ValueAnimator animation) {
floatcValue = (float) animation.getAnimatedValue();
distance=density*10* cValue;
invalidate();
}
});
animator.setDuration(350);
animator.setInterpolator(newDecelerateInterpolator());
animator.setRepeatMode(REVERSE);
animator.setRepeatCount(-1);
}
private booleanadded=false;
public voidstartAnim() {
if(animator==null) {
initAnim();
}
if(!added) {
//将guideView直接添加到window中,获取DecorView并把guideView作为child添加到其中,从而实现全屏显示
setLayoutParams(newViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
((ViewGroup) ((Activity)context).getWindow().getDecorView()).addView(this);
added=true;
}
if(!animator.isRunning()) {
animator.start();
}
}
}
```
GuideView调用
GuideViewguideView=newGuideView(this);
guideView.setGuideParent(tvFunctionTitle);//设置需要引导的view
guideView.setGuideStr("点击这里创建一个新的旅途相册");//设置提示文字
guideView.startAnim();