Android 快速实现APP新手蒙层引导

        最近在项目开发中,产品设计出了新功能的用户引导,非启动页面左右滑动类型的引导,而是在APP功能界面上,直接上层弹出蒙层形式展示,其实在过往的开发中,也有过相同的功能,但是都比较少,就基本上都采用简单粗暴的直接在XML中布局,然后控制Visible,然后添加点击事件就行。最近公司APP中,产品提出来的有点多,而且部分引导区域需要做到事件的穿透,部分不穿透,甚至出现了异形的镂空,再简单粗暴的去布局,对于后期XML布局的维护存在一定的成本。于是自己决心花费周末的一个时间,写一个公共的库。这个库一定要简单,不能再在XML中布局,并且容易理解。先来几张样例图(网图,和实现功能一样)

废话不多说,先下载Demo 看效果,非常小



网图:


上面是网络上的图片,下面是我的实现图片(图大,就用三张)

自己在线PS做的图,有点丑,将就看。

话不多说,源码:SmartGuideView 到Github去下载吧。

引用方式:

dependencies {

            implementation fileTree(include: ['*.jar'], dir: 'libs')

           implementation 'aiven.guide.view:library:1.0.1'

          //或者使用api

          // api 'aiven.guide.view:library:1.0.1'

}


还是说一下代码把,这里只是说一下怎么用,API的含义,具体有兴趣的朋友可以直接看源码。

类入口:SmartGuide

创建蒙层。

    SmartGuide.newGuide(this)  这里的this 需要传入一个Activity或者fragment。需要注意的是,fragment一定要在attach执行完毕之后才能去调用,因为归根接地用的还是activity。

第一个初始化方法:initBaseColor(颜色值)  初始化引导蒙层的默认背景颜色,一般都是半透明的黑色,所以默认我采用了50%透明度的黑色,可以之定义,如果不知道,就是默认值。

SmartGuide.newGuide(this) .initBaseColor(颜色值)   这样就构建完成了一个没有引导的蒙层。接下来在蒙层中创建一个用户引导。这里我将引导命名为层Layer。一个引导(Layer)包含一个蒙层中的镂空区域和图片介绍信息,具体例子就是上面蚂蚁花呗那张图中,白色蚂蚁花呗的横条就是镂空区域,下面一行白色文字和手就是图片介绍信息。

所以我将这两个分别命名为:Clip 和Panel。

newLayer(Tag) //创建一个引导,并给这个引导指定一个Tag,相当于Id。

所以一个Layer = Clip + Panel 。当然,都不是必须的。这就是接下来的API

buildViewRectClip、buildCustomClip、buildIntroPanel  

不是说两个嘛?怎么冒出了三个函数?

这里ViewRectClip和CustomeClip 都是Clip,两种镂空区域。

ViewRectClip 是一个根据界面上的一个View 在屏幕上的位置,尺寸计算出镂空区域和大小,CustomeClip 是一个绝对位置自定义的裁剪区域,以根布局左上角为坐标定点,自己设置区域和偏移,当然这里我增加了Align,这个就不解释了,程序员做布局基本上都懂。通过buildViewRectClip和buildCustomeClip来设置,参数都是一个Build内部接口,然后返回响应的Clip接口,具体Clip的构建参数见下面的例子中说明。这里不再详细说,或者直接到github上看。clip 有个API需要特殊说明:asIrregularShape(Bitmap)  这个是传入一个异形图片,以图片形状作为镂空形状,上图中有个实例,一个老鼠的造型。

Panel:也通过build方式内部接口回调,Panel也有一个Align,这里的Align和CustomClip的Align 有点区别,这里的Align是Panel 现对于Clip而言。也就是Pannel 局clip的,上、下、做、右。然后设置offsetX,offsetY设置偏移即可。


点击事件:OnGuidClickListener  接口,有三个回调函数,

emptyErrorClicked   点击了非Panel和clip区域,返回值:true 蒙层直接退出,false不退出

clipClicked    点击了clip镂空区域,这里要说明的是,Clip有个接口是API是setEventPassThrough(boolean),这个是标明是否镂空区域点击事件要穿透到APP自身功能布局中的UI响应。

introClicked   点击了Panel 图片介绍区域。

创建完成Layer后可以直接show() 显示,如果一个蒙层同时要显示多个Layer,则要先调用over()方法结束上一个layer的参数设置功能,直接newLayer即可,具体见github的多Layer展示方案。


代码之前,再附带一下传送门:SmartView

https://github.com/aiven163/SmartGuideView


代码实例一:

SmartGuide.newGuide(this)

.initBaseColor(0X80000000)//设置引蒙层背景颜色

            //新建一个引导

            .newLayer(TAG_USER_HEADER)

//创建一个镂空区域

            .buildCustomClip(new SmartGuide.ClipPositionBuilder() {

@Override

                public CustomClipbuildTarget() {

//构建镂空区域图形,支持CustomClip 和ViewRectClip

                    return CustomClip.newClipPos()

.setAlignX(SmartGuide.AlignX.ALIGN_RIGHT)//设置定位水平定位偏向

                            .setAlignY(SmartGuide.AlignY.ALIGN_TOP)//设置定位垂直定位偏向

                            .setOffsetX(SmartUtils.dip2px(getApplicationContext(),14))//根据水平定位偏向设置偏移,如果未ALIGN_LEFT,则是距离屏幕左侧偏移量,如果是ALIGN_RIGHT 则是距离屏幕右侧偏移量

                            .setOffsetY(SmartUtils.getStatusBarHeight(getApplicationContext())+SmartUtils.dip2px(getApplicationContext(),4))

//设置镂空裁剪区域尺寸

                            .setClipSize(SmartUtils.dip2px(getApplicationContext(),48),SmartUtils.dip2px(getApplicationContext(),48))

.clipRadius(SmartUtils.dip2px(getApplicationContext(),24));

                }

})

.buildIntroPanel(new SmartGuide.IntroPanelBuilder() {

@Override

                public IntroPanelbuildFacePanel() {

return IntroPanel.newIntroPanel(getApplicationContext())

//设置介绍图片与clipInfo的对齐信息

                            .setIntroBmp(R.mipmap.test_face)

.setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_BOTTOM)

.setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),97))

.setOffset(SmartUtils.dip2px(getApplicationContext(),-20),0);

                }

})

.over()//多个newLayer必须用over作为上一个newLayer的结束连接

            .newLayer(TAG_MUSIC_IMG)

//创建一个镂空区域

            .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {

@Override

                public ViewRectClipbuildTarget() {

return ViewRectClip.newClipPos()

.setDstView(R.id.text_pos)

.setPadding(SmartUtils.dip2px(getApplicationContext(),5))

.clipRadius(SmartUtils.dip2px(getApplicationContext(),51));

                }

})

.buildIntroPanel(new SmartGuide.IntroPanelBuilder() {

@Override

                public IntroPanelbuildFacePanel() {

return IntroPanel.newIntroPanel(getApplicationContext())

//设置介绍图片与clipInfo的对齐信息

                            .setIntroBmp(R.mipmap.test_face_music)

.setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)

.setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))

.setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);

                }

})

.setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {

@Override

                public boolean emptyErrorClicked() {

return true;

                }

@Override

                public void clipClicked(SmartGuide guide, GuidView view, String tag) {

if (TAG_USER_HEADER.equals(tag)) {

Toast.makeText(getApplicationContext(), "点击了左上角头像裁剪区域", Toast.LENGTH_SHORT).show();

                    }else if(TAG_MUSIC_IMG.equals(tag)){

Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();

                    }

}

@Override

                public void introClicked(SmartGuide guide, GuidView view, String tag) {

if (TAG_USER_HEADER.equals(tag)) {

Toast.makeText(getApplicationContext(), "点击了左上角头像图片介绍区域", Toast.LENGTH_SHORT).show();

                    }else if(TAG_MUSIC_IMG.equals(tag)){

Toast.makeText(getApplicationContext(), "点击了紫色音乐图片介绍区域", Toast.LENGTH_SHORT).show();

                    }

}

})

.show();

}




代码实例二:

/**

* 根据View 自身位置定位

* @param view

*/

public void showViewPosLayer(View view){

//构建引导

    SmartGuide.newGuide(this)

.initBaseColor(0X80000000)//设置引蒙层背景颜色

            //新建一个引导

            .newLayer(TAG_MUSIC_IMG)

//创建一个镂空区域

            .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {

@Override

                public ViewRectClipbuildTarget() {

return ViewRectClip.newClipPos()

.setDstView(R.id.text_pos)

.setPadding(SmartUtils.dip2px(getApplicationContext(),5))

.clipRadius(SmartUtils.dip2px(getApplicationContext(),51));

                }

})

.buildIntroPanel(new SmartGuide.IntroPanelBuilder() {

@Override

                public IntroPanelbuildFacePanel() {

return IntroPanel.newIntroPanel(getApplicationContext())

//设置介绍图片与clipInfo的对齐信息

                            .setIntroBmp(R.mipmap.test_face_music)

.setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)

.setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))

.setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);

                }

})

.setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {

@Override

                public boolean emptyErrorClicked() {

return true;

                }

@Override

                public void clipClicked(SmartGuide guide, GuidView view, String tag) {

Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();

                }

@Override

                public void introClicked(SmartGuide guide, GuidView view, String tag) {

Toast.makeText(getApplicationContext(), "点击了紫色音乐图标介绍图片区域", Toast.LENGTH_SHORT).show();

                }

})

.show();

}





最后多个Layer代码实例:

/**

* 单屏显示多个layer

* @param view

*/

public void showMultLayer(View view){

SmartGuide.newGuide(this)

.initBaseColor(0X80000000)//设置引蒙层背景颜色

            //新建一个引导

            .newLayer(TAG_USER_HEADER)

//创建一个镂空区域

            .buildCustomClip(new SmartGuide.ClipPositionBuilder() {

@Override

                public CustomClipbuildTarget() {

//构建镂空区域图形,支持CustomClip 和ViewRectClip

                    return CustomClip.newClipPos()

.setAlignX(SmartGuide.AlignX.ALIGN_RIGHT)//设置定位水平定位偏向

                            .setAlignY(SmartGuide.AlignY.ALIGN_TOP)//设置定位垂直定位偏向

                            .setOffsetX(SmartUtils.dip2px(getApplicationContext(),14))//根据水平定位偏向设置偏移,如果未ALIGN_LEFT,则是距离屏幕左侧偏移量,如果是ALIGN_RIGHT 则是距离屏幕右侧偏移量

                            .setOffsetY(SmartUtils.getStatusBarHeight(getApplicationContext())+SmartUtils.dip2px(getApplicationContext(),4))

//设置镂空裁剪区域尺寸

                            .setClipSize(SmartUtils.dip2px(getApplicationContext(),48),SmartUtils.dip2px(getApplicationContext(),48))

.clipRadius(SmartUtils.dip2px(getApplicationContext(),24));

                }

})

.buildIntroPanel(new SmartGuide.IntroPanelBuilder() {

@Override

                public IntroPanelbuildFacePanel() {

return IntroPanel.newIntroPanel(getApplicationContext())

//设置介绍图片与clipInfo的对齐信息

                            .setIntroBmp(R.mipmap.test_face)

.setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_BOTTOM)

.setSize(SmartUtils.dip2px(getApplicationContext(),151),SmartUtils.dip2px(getApplicationContext(),97))

.setOffset(SmartUtils.dip2px(getApplicationContext(),-20),0);

                }

})

.over()//多个newLayer必须用over作为上一个newLayer的结束连接

            .newLayer(TAG_MUSIC_IMG)

//创建一个镂空区域

            .buildViewRectClip(new SmartGuide.ClipPositionBuilder() {

@Override

                public ViewRectClipbuildTarget() {

return ViewRectClip.newClipPos()

.setDstView(R.id.text_pos)

.setPadding(SmartUtils.dip2px(getApplicationContext(),5))

.clipRadius(SmartUtils.dip2px(getApplicationContext(),51));

                }

})

.buildIntroPanel(new SmartGuide.IntroPanelBuilder() {

@Override

                public IntroPanelbuildFacePanel() {

return IntroPanel.newIntroPanel(getApplicationContext())

//设置介绍图片与clipInfo的对齐信息

                            .setIntroBmp(R.mipmap.test_face_music)

.setAlign(SmartGuide.AlignX.ALIGN_LEFT,SmartGuide.AlignY.ALIGN_TOP)

.setSize(SmartUtils.dip2px(getApplicationContext(),120),SmartUtils.dip2px(getApplicationContext(),120))

.setOffset(SmartUtils.dip2px(getApplicationContext(),-100),0);

                }

})

.setOnGuidClickListener(new SmartGuide.OnGuidClickListener() {

@Override

                public boolean emptyErrorClicked() {

return true;

                }

@Override

                public void clipClicked(SmartGuide guide, GuidView view, String tag) {

if (TAG_USER_HEADER.equals(tag)) {

Toast.makeText(getApplicationContext(), "点击了左上角头像裁剪区域", Toast.LENGTH_SHORT).show();

                    }else if(TAG_MUSIC_IMG.equals(tag)){

Toast.makeText(getApplicationContext(), "点击了紫色音乐图标裁剪区域", Toast.LENGTH_SHORT).show();

                    }

}

@Override

                public void introClicked(SmartGuide guide, GuidView view, String tag) {

if (TAG_USER_HEADER.equals(tag)) {

Toast.makeText(getApplicationContext(), "点击了左上角头像图片介绍区域", Toast.LENGTH_SHORT).show();

                    }else if(TAG_MUSIC_IMG.equals(tag)){

Toast.makeText(getApplicationContext(), "点击了紫色音乐图片介绍区域", Toast.LENGTH_SHORT).show();

                    }

}

})

.show();

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容