軟件開發時常常須要對某些按鈕進行說明,引導用戶該怎麼去操做軟件。這種狀況大部分是一個透明的遮罩層,遮罩層上有一部分高亮區顯示要說明的按鈕,按鈕的某個方向上加上一個圖片進行操做說明。好比說小面的效果。android
編寫一個自定義view,繼承自view,下面直接上代碼,看代碼中的註釋便可理解。git
public class GuideView extends View {
private Paint paint;
//編寫一個GuideBean,裏面存放控件說明的信息,好比圖片,要說明的控件等
private GuideBean guideBean;
public GuideView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//若是集合中不存在控件說明,直接隱藏該view。
if (guideBeans == null || guideBeans.size() == 0){
this.setVisibility(GONE);
return;
}
//Config是此view的配置類,下面會提到。這裏的OPENMORE指是否在一個屏幕內顯示多個控件說明
if (Config.OPENMORE){
//一屏顯示多個控件說明
canvas.drawBitmap(createDstBitmap(width,height), 0, 0, paint);
for (int i=0; i<guideBeans.size(); i++){
drawBuyView(canvas,guideBeans.get(i));
}
}else {
//沒點擊一次,就播放下一個控件說明
drawBuyView(canvas,guideBean);
}
}
//將要添加的控件說明集合設置進此view中,等待顯示
public void setGuideBeans(List<GuideBean> guideBeans){
this.guideBeans = guideBeans;
invalidate();
}
//當數據設置好後調用此方法顯示控件說明
public void showGuide(){
//將隱藏中的該view設置爲顯示狀態
this.setVisibility(VISIBLE);
if (guideBeans != null && guideBeans.size() !=0){
//將當前的說明設置爲集合中的第一個對象。
guideBean = guideBeans.get(0);
}
invalidate();
}
}
複製代碼
繪製要說明控件的圖片canvas
/**
* 繪製高亮區及說明圖片
* @param canvas
* @param guideBean
*/
public void drawBuyView(Canvas canvas,GuideBean guideBean){
if (guideBean == null){
return;
}
//若是是一屏顯示多個控件說明,遮罩層在循環前繪製一邊便可
if (!Config.OPENMORE){
canvas.drawBitmap(createDstBitmap(width,height), 0, 0, paint);
}
//繪製view的圖像,即將view自己繪製成高亮區
//須要注意的是,要說明的控件背景不可設置爲透明背景,否在獲取控件圖片時背景也
//是透明的,這樣控件的顏色與遮罩層的顏色就一致了,起不到高亮的效果
if (guideBean.getViewBitmap() != null){
canvas.drawBitmap(guideBean.getViewBitmap(),guideBean.getRect().left,guideBean.getRect().top,paint);
}
//說明控件的中線座標,用於計算說明圖片的在x軸上的起始距離(距離屏幕左邊距離)
int centerLine = guideBean.getRect().left+(guideBean.getRect().right-guideBean.getRect().left)/2;
//計算說明圖片x軸上的起始位置
int targetCenter = centerLine - guideBean.getBitmap().getWidth()/2;
if (guideBean.getBitmap() != null){
//執行繪製說明圖片的方法
drawShuoMingPic(canvas,guideBean,targetCenter);
}
}
/**
* 繪製控件說明
* @param canvas
* @param guideBean
*/
private void drawShuoMingPic(Canvas canvas, GuideBean guideBean,int targetCenter) {
//根據不一樣的方位狀況進行控件繪製。這裏只寫出繪製在控件底部的狀況,所有的代碼有點長,就不貼出來了。
if (guideBean.getPosition() == Config.BOTTOM){
canvas.drawBitmap(guideBean.getBitmap(),targetCenter+guideBean.getMarginLeft(),guideBean.getRect().bottom+guideBean.getMarginTop()+guideBean.getMarginBottom(),paint);
}
}
複製代碼
編寫一個配置類,用於配置該view的屬性bash
/**
* view的配置文件,務必在調用showGuide方法前配置完guideview
*/
public static class Config{
/**
* 是否打開一個界面顯示多個控件說明,默認是關閉的
*/
public static boolean OPENMORE = false;
/**
* 配置遮罩層顏色
*/
public static int COLOR = Color.parseColor("#99000000");
}
複製代碼
編寫GuideBean,這個類描述了全部的控件說明信息,好比padding,margin,控件的在屏幕上的位置等信息app
public class GuideBean{
private Rect rect;
private int img;
private Bitmap bitmap;
private Bitmap viewBitmap;
private int marginTop,marginBottom,marginLeft,marginRight;
private boolean isSimpleShape;
private int position;
private byte shape;
/**
*
* @param img 要展現的說明圖片
* @param act 活動界面
* @param view 想要出現高亮區的控件
*/
public GuideBean(int img, Activity act, View view) {
Rect rect = new Rect();
//獲取控件在屏幕中的座標位置,此座標位置包括了狀態欄的高度和標題欄的高度,
//因此後面矩陣rect中的top和bottom須要去除這兩個高度的影響。去除方法在GuideViewUtils幫助類中
view.getGlobalVisibleRect(rect);
setRectInfo(rect,act);
this.img = img;
//獲取控件說明圖片
bitmap = BitmapFactory.decodeResource(act.getResources(),img);
this.rect = rect;
//獲取控件自己的圖片
this.viewBitmap = GuideViewUtils.loadBitmapFromView(view);
}
/**
* 設置控件矩陣的頂部座標和底部座標,這兩個座標與狀態欄和標題欄高度有關
* @param rect 控件的矩陣
* @param act 活動
*/
public void setRectInfo(Rect rect,Activity act){
rect.top = rect.top - GuideViewUtils.getStatusBarHeight(act) - GuideViewUtils.getInstance().getActionBarHeight(act);
rect.bottom = rect.bottom - GuideViewUtils.getStatusBarHeight(act) - GuideViewUtils.getInstance().getActionBarHeight(act);
}
//下面時一堆get和set方法,不貼出來了。
}
複製代碼
/**
* 獲取狀態欄高度
* @param context
* @return 狀態欄高度
*/
public static int getStatusBarHeight(Activity context){
//判斷存不存在狀態欄
WindowManager.LayoutParams params = context.getWindow().getAttributes();
if ((params.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN){
//不存在狀態欄直接返回0
return 0;
}else {
//存在則獲取狀態欄高度
int height = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
height = context.getResources().getDimensionPixelSize(resourceId);
}
return height;
}
}
/**
* 獲取 標題欄高度
* @param activity
* @return
*/
public int getActionBarHeight(Activity activity){
//判斷傳進來的activity是否是Activity,繼承自Activity的活動是不帶標題欄的
//繼承自AppCompatActivity的活動是帶有標題欄的
if (activity instanceof AppCompatActivity){
//判斷活動中是否存在標題欄,存在返回高度,不存在返回0
if (((AppCompatActivity) activity).getSupportActionBar() != null){
return ((AppCompatActivity) activity).getSupportActionBar().getHeight();
}else {
return 0;
}
}else if (activity instanceof Activity){
if (activity.getActionBar() != null){
return activity.getActionBar().getHeight();
}else {
return 0;
}
}else {
return 0;
}
}
複製代碼
<com.libowu.guide.view.GuideView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/guide"/>
複製代碼
//說明一下,只有繪製view完成後才能夠設置集合,oncreate中view並無繪製完成,因此控件的高度,在屏幕中的位置沒法
//獲取到,因此放到onWindowFocusChanged中執行了。不過onWindowFocusChanged是隻要屏幕焦點變化時都會調用,不如鎖屏
//開屏,切換先後臺等都有肯能觸發onWindowFocusChanged
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
//將要說明的控件添加到集合中,讓後給guideview設置數據便可
guides = new ArrayList<>();
guides.add(new GuideBean(R.mipmap.guide,this,textView, true, GuideView.Config.OVAL));
guides.add(new GuideBean(R.mipmap.guide,this,textViewTwo,true, GuideView.Config.OVAL));
guides.add(new GuideBean(R.mipmap.guide,this,textViewThree,true, GuideView.Config.OVAL));
guide.setGuideBeans(guides);
}
複製代碼
textViewTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//想要顯示引導時,調用此方法便可顯示
guide.showGuide();
}
});
複製代碼
allprojects {
repositories {
....
maven {url "https://jitpack.io"}
}
}
複製代碼
2.app的build.gradle中加入依賴
implementation 'com.gitee.libowu:guideView:v0.0.4'
maven
這個項目只說了第一種思路,第二種思路並無說,由於代碼有點多,因此先不寫了,有興趣的能夠到碼雲上面clone項目到本地。碼雲地址。ide