android遮罩层引导页showCaseView的扩展及解析
前言:
首先感谢showcase引导页的这个lib,本blog对此lib进行了扩展,可以输入自定义layout或者通过代码对引导层中的布局属性进行设置;
快捷入口为: MaterialShowcaseView
内容:
效果图
先来回顾下原lib中的亮点:
- 提供targetview的三种显示策略,
CircleShape,NoShape,RectangleShape;
- 提供多个引导页的延迟展示;
Queue<ShowcaseView> mShowcaseQueue;
- 提供两种引导动画,
Fade和CircularReveal;
扩展功能及使用
去除了 CustomShowcaseView 中的固定布局,采用了设置自定义layout来设置多个引导页面;可使用自定义xml的layout,增加灵活性;
调用方式与原lib一致:
//显示引导页;
private void presentShowcaseSequenceByCustomStyle() {
List<CustomShowcaseView> caseViews = new ArrayList<>();
//添加不同特征的实现类;
caseViews.add(FeedDetailActHelper.createMaterialShowcaseViewNeo(this, likeCustomView, 0));
caseViews.add(FeedDetailActHelper.createMaterialShowcaseViewNeo(this, commentCustomView, 1));
caseViews.add(FeedDetailActHelper.createMaterialShowcaseViewNeo(this, shareCustomView, 2));
caseViews.add(FeedDetailActHelper.createMaterialShowcaseViewNeo(this, layoutFavorite, 3));
CustomMaterialShowcaseSequence sequence = new CustomMaterialShowcaseSequence(this, SHOWCASE_ID_BOTTOM);
//添加至队列的容器内;
for (CustomShowcaseView view : caseViews) {
sequence.addSequenceItem(view);
}
//显示对应的showcase;
sequence.start();
}
//具体的showcase构造类
public static CustomShowcaseView createMaterialShowcaseViewNeo(Activity activity, View tartgetView,int mode) {
return new CustomShowcaseView.Builder(activity)
.setLayoutShowCase(createStrategyByType(mode))
.setTarget(tartgetView)
.setDismissOnTouch(true)
.setMaskColour(BPlusApplication.mContext.getResources().getColor(R.color.show_case_color))
.singleUse(SHOWCASE_ID_BOTTOM) // provide a unique ID used to ensure it is only shown once
.withRectangleShape(false)
.build();
}
//对不同的case,显示不同layout的不同实现类;
public static CustomShowcaseView.ShowcaseDraw createStrategyByType(int mode) {
CustomShowcaseView.ShowcaseDraw mShowcaseDrawImpl = null;
switch (mode) {
case 0:
mShowcaseDrawImpl = new ShowcaseDrawFeedImpl();
break;
case 1:
mShowcaseDrawImpl = new ShowcaseDrawFeedImpl.ShowcaseDrawFeed2Impl();
break;
case 2:
mShowcaseDrawImpl = new ShowcaseDrawFeedImpl.ShowcaseDrawFeed3Impl();
break;
case 3:
mShowcaseDrawImpl = new ShowcaseDrawFeedImpl.ShowcaseDrawFeed4Impl();
break;
}
return mShowcaseDrawImpl;
}
原理解析:
CustomShowcaseView
自定义viewgroup,用于显示不同布局的view;
设置不同的布局,将子实现的布局和绘制抽象出去,同一控制显示;
- 将不同的引导布局作为一个单独的策略;
/**
* 子实现的布局及绘制接口;
*/
public interface ShowcaseDraw {
//当前showcase的layout布局id;
int getLayoutId();
//初始化layout;
void initView(CustomShowcaseView contentView);
//在布局改变时监听,以mTarget view的包装类为锚,可自定义设置view的精确布局;
void customLayout(Target mTarget);
}
可自定义设置layout显示;
getLayout返回一个layout id;
initview 初始化layout;
customlayout 设置其中某些view的相对于targetview的位置;
eg:
@Override public void customLayout(Target mTarget) { mImgArrow.setTranslationX(mTarget.getPoint().x - mImgArrow.getMeasuredWidth()/2); }
-
getViewTreeObserver().addOnGlobalLayoutListener 添加对布局改变事件的监听
监听布局改变,设置mTargetview,并且此事件中view的高度宽度可以获取到; 可通过ShowcaseDraw
设置不同实现的具体布局或者变换属性; -
onDraw 具体绘制方法
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//判断是否需要绘制;
// don't bother drawing if we're not ready
if (!mShouldRender) return;
// get current dimensions
final int width = getMeasuredWidth();
final int height = getMeasuredHeight();
// don't bother drawing if there is nothing to draw on
if (width <= 0 || height <= 0) return;
//建立装载画布,用于相交模式下对mTarget的形状显示;
// build a new canvas if needed i.e first pass or new dimensions
if (mBitmap == null || mCanvas == null || mOldHeight != height || mOldWidth != width) {
if (mBitmap != null) mBitmap.recycle();
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
// save our 'old' dimensions
mOldWidth = width;
mOldHeight = height;
// clear canvas
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// draw solid background
mCanvas.drawColor(mMaskColour);
// Prepare eraser Paint if needed
if (mEraser == null) {
mEraser = new Paint();
mEraser.setColor(0xFFFFFFFF);
mEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mEraser.setFlags(Paint.ANTI_ALIAS_FLAG);
}
//将具体的绘制交给不同的实现类,可实现对targetview的各种不同形状的显示;
// draw (erase) shape
mShape.draw(mCanvas, mEraser, mXPosition, mYPosition, mShapePadding);
//绘制装载画布;
// Draw the bitmap on our views canvas.
canvas.drawBitmap(mBitmap, 0, 0, null);
}
- 加入到当前activity和从activity中移除
show:
((ViewGroup) activity.getWindow().getDecorView()).addView(this);
添加至activity中,设置重绘,添加动画;
hide:
//从activity中移除,并回收相关的资源操作;
if (getParent() != null && getParent() instanceof ViewGroup) {
((ViewGroup) getParent()).removeView(this);
}
onDetachedFromWindow
回调detach方法,设置PrefsManager的记录值,回调给mDetachedListener,如果是多个layout
的显示,detachListener 通过showNextItem
重新显示下一个layout;
- 动画策略
//显示layout的动画;
void animateInView(View target, Point point, long duration, AnimationStartListener listener);
//隐藏layout的动画;
void animateOutView(View target, Point point, long duration, AnimationEndListener listener);
提供两个动画
ViewAnimationUtils.createCircularReveal() 水波纹动画;
ObjectAnimator.ofFloat 透明度属性动画;
- targetview的相交形状策略
public interface Shape {
/**
* Draw shape on the canvas with the center at (x, y) using Paint object provided.
*/
void draw(Canvas canvas, Paint paint, int x, int y, int padding);
/**
* Get width of the shape.
*/
int getWidth();
/**
* Get height of the shape.
*/
int getHeight();
/**
* Update shape bounds if necessary
*/
void updateTarget(Target target);
}
提供了3种策略,
CircleShape: 显示targetview圆形区域;
NoShape: 不做任何处理;
RectangleShape: targetview的方形区域;
本地sharepreference的存储;
设置两种状态 start 和 finish 状态记录多种layout点击一次回到start状态,detach时设置finish状态;
配置类和构建者
建立ShowcaseConfig保存可设置的属性,用于便捷统一设置showcase的属性; 构建者用于快速设置属性;
###后记
详细见代码,希望与大家共同进步;