软件开发时常常须要对某些按钮进行说明,引导用户该怎么去操做软件。这种状况大部分是一个透明的遮罩层,遮罩层上有一部分高亮区显示要说明的按钮,按钮的某个方向上加上一个图片进行操做说明。好比说小面的效果。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