? ? ? ? 最近在項目開發(fā)中,產品設計出了新功能的用戶引導,非啟動頁面左右滑動類型的引導,而是在APP功能界面上,直接上層彈出蒙層形式展示,其實在過往的開發(fā)中,也有過相同的功能,但是都比較少,就基本上都采用簡單粗暴的直接在XML中布局,然后控制Visible,然后添加點擊事件就行。最近公司APP中,產品提出來的有點多,而且部分引導區(qū)域需要做到事件的穿透,部分不穿透,甚至出現了異形的鏤空,再簡單粗暴的去布局,對于后期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
創(chuàng)建蒙層。
? ??SmartGuide.newGuide(this)? 這里的this 需要傳入一個Activity或者fragment。需要注意的是,fragment一定要在attach執(zhí)行完畢之后才能去調用,因為歸根接地用的還是activity。
第一個初始化方法:initBaseColor(顏色值)? 初始化引導蒙層的默認背景顏色,一般都是半透明的黑色,所以默認我采用了50%透明度的黑色,可以之定義,如果不知道,就是默認值。
SmartGuide.newGuide(this) .initBaseColor(顏色值)? ?這樣就構建完成了一個沒有引導的蒙層。接下來在蒙層中創(chuàng)建一個用戶引導。這里我將引導命名為層Layer。一個引導(Layer)包含一個蒙層中的鏤空區(qū)域和圖片介紹信息,具體例子就是上面螞蟻花唄那張圖中,白色螞蟻花唄的橫條就是鏤空區(qū)域,下面一行白色文字和手就是圖片介紹信息。
所以我將這兩個分別命名為:Clip 和Panel。
newLayer(Tag) //創(chuàng)建一個引導,并給這個引導指定一個Tag,相當于Id。
所以一個Layer = Clip + Panel 。當然,都不是必須的。這就是接下來的API
buildViewRectClip、buildCustomClip、buildIntroPanel??
不是說兩個嘛?怎么冒出了三個函數?
這里ViewRectClip和CustomeClip 都是Clip,兩種鏤空區(qū)域。
ViewRectClip 是一個根據界面上的一個View 在屏幕上的位置,尺寸計算出鏤空區(qū)域和大小,CustomeClip 是一個絕對位置自定義的裁剪區(qū)域,以根布局左上角為坐標定點,自己設置區(qū)域和偏移,當然這里我增加了Align,這個就不解釋了,程序員做布局基本上都懂。通過buildViewRectClip和buildCustomeClip來設置,參數都是一個Build內部接口,然后返回響應的Clip接口,具體Clip的構建參數見下面的例子中說明。這里不再詳細說,或者直接到github上看。clip 有個API需要特殊說明:asIrregularShape(Bitmap)? 這個是傳入一個異形圖片,以圖片形狀作為鏤空形狀,上圖中有個實例,一個老鼠的造型。
Panel:也通過build方式內部接口回調,Panel也有一個Align,這里的Align和CustomClip的Align 有點區(qū)別,這里的Align是Panel 現對于Clip而言。也就是Pannel 局clip的,上、下、做、右。然后設置offsetX,offsetY設置偏移即可。
點擊事件:OnGuidClickListener? 接口,有三個回調函數,
emptyErrorClicked? ?點擊了非Panel和clip區(qū)域,返回值:true 蒙層直接退出,false不退出
clipClicked? ? 點擊了clip鏤空區(qū)域,這里要說明的是,Clip有個接口是API是setEventPassThrough(boolean),這個是標明是否鏤空區(qū)域點擊事件要穿透到APP自身功能布局中的UI響應。
introClicked? ?點擊了Panel 圖片介紹區(qū)域。
創(chuàng)建完成Layer后可以直接show() 顯示,如果一個蒙層同時要顯示多個Layer,則要先調用over()方法結束上一個layer的參數設置功能,直接newLayer即可,具體見github的多Layer展示方案。
代碼之前,再附帶一下傳送門:SmartView
https://github.com/aiven163/SmartGuideView
代碼實例一:
SmartGuide.newGuide(this)
.initBaseColor(0X80000000)//設置引蒙層背景顏色
? ? ? ? ? ? //新建一個引導
? ? ? ? ? ? .newLayer(TAG_USER_HEADER)
//創(chuàng)建一個鏤空區(qū)域
? ? ? ? ? ? .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
@Override
? ? ? ? ? ? ? ? public CustomClipbuildTarget() {
//構建鏤空區(qū)域圖形,支持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))
//設置鏤空裁剪區(qū)域尺寸
? ? ? ? ? ? ? ? ? ? ? ? ? ? .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)
//創(chuàng)建一個鏤空區(qū)域
? ? ? ? ? ? .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(), "點擊了左上角頭像裁剪區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }else if(TAG_MUSIC_IMG.equals(tag)){
Toast.makeText(getApplicationContext(), "點擊了紫色音樂圖標裁剪區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }
}
@Override
? ? ? ? ? ? ? ? public void introClicked(SmartGuide guide, GuidView view, String tag) {
if (TAG_USER_HEADER.equals(tag)) {
Toast.makeText(getApplicationContext(), "點擊了左上角頭像圖片介紹區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }else if(TAG_MUSIC_IMG.equals(tag)){
Toast.makeText(getApplicationContext(), "點擊了紫色音樂圖片介紹區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }
}
})
.show();
}
代碼實例二:
/**
* 根據View 自身位置定位
* @param view
*/
public void showViewPosLayer(View view){
//構建引導
? ? SmartGuide.newGuide(this)
.initBaseColor(0X80000000)//設置引蒙層背景顏色
? ? ? ? ? ? //新建一個引導
? ? ? ? ? ? .newLayer(TAG_MUSIC_IMG)
//創(chuàng)建一個鏤空區(qū)域
? ? ? ? ? ? .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(), "點擊了紫色音樂圖標裁剪區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? }
@Override
? ? ? ? ? ? ? ? public void introClicked(SmartGuide guide, GuidView view, String tag) {
Toast.makeText(getApplicationContext(), "點擊了紫色音樂圖標介紹圖片區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? }
})
.show();
}
最后多個Layer代碼實例:
/**
* 單屏顯示多個layer
* @param view
*/
public void showMultLayer(View view){
SmartGuide.newGuide(this)
.initBaseColor(0X80000000)//設置引蒙層背景顏色
? ? ? ? ? ? //新建一個引導
? ? ? ? ? ? .newLayer(TAG_USER_HEADER)
//創(chuàng)建一個鏤空區(qū)域
? ? ? ? ? ? .buildCustomClip(new SmartGuide.ClipPositionBuilder() {
@Override
? ? ? ? ? ? ? ? public CustomClipbuildTarget() {
//構建鏤空區(qū)域圖形,支持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))
//設置鏤空裁剪區(qū)域尺寸
? ? ? ? ? ? ? ? ? ? ? ? ? ? .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)
//創(chuàng)建一個鏤空區(qū)域
? ? ? ? ? ? .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(), "點擊了左上角頭像裁剪區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }else if(TAG_MUSIC_IMG.equals(tag)){
Toast.makeText(getApplicationContext(), "點擊了紫色音樂圖標裁剪區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }
}
@Override
? ? ? ? ? ? ? ? public void introClicked(SmartGuide guide, GuidView view, String tag) {
if (TAG_USER_HEADER.equals(tag)) {
Toast.makeText(getApplicationContext(), "點擊了左上角頭像圖片介紹區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }else if(TAG_MUSIC_IMG.equals(tag)){
Toast.makeText(getApplicationContext(), "點擊了紫色音樂圖片介紹區(qū)域", Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? ? ? }
}
})
.show();
}