先上效果图!
奉上git地址,还有一些属性和点击事件的设置可以自己封装
这次主要是使用了自定义Dialog的方式来实现这个效果
主要步骤:
1、创建一个自定义VIew的Dialog
2、复制一个需要高亮显示的View在同样的位置,并完美覆盖(重点)
3、绘制提示图片
1 创建一个自定义VIew的Dialog
guidelayout = new RelativeLayout(activity);
//创建Dialog,遮挡状态栏
guideDialog = new Dialog(activity, android.R.style.Theme_DeviceDefault_Light_DialogWhenLarge_NoActionBar);
//设置背景颜色
guideDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x66000000));
//设置自定义的布局
guideDialog.setContentView(guidelayout);
//设置布局的属性
guideDialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
//设置点击不能取消
guideDialog.setCancelable(false);
//显示Dialog
guideDialog.show();
2 复制一个需要高亮显示的View在同样的位置,并完美覆盖(重点)
2.1、获取原来的VIew的位置和宽高
//获取view的宽高
int vWidth = lightView.getMeasuredWidth();
int vHeight = lightView.getMeasuredHeight();
//如果宽高都小于等于0,再measure试下获取
if (vWidth <= 0 || vHeight <= 0) {
ViewGroup.LayoutParams mlayoutParams = lightView.getLayoutParams();
lightView.measure(mlayoutParams.width, mlayoutParams.height);
vWidth = lightView.getMeasuredWidth();
vHeight = lightView.getMeasuredHeight();
}
//获取不到宽高则返回操作
if (vWidth <= 0 || vHeight <= 0) {
Log.e("GuideHelper", "宽高都小于等于0");
return;
}
2.2 获取自定义布局在视图的位置,View的Y轴坐标减去这个位置的Y轴坐标,避免Dialog不是全屏的时候位置偏移,就可以可以实现全局覆盖了
//获取view在屏幕的位置
int[] location = new int[2];
lightView.getLocationOnScreen(location);
//获取layout在屏幕上的位置
int layoutOffset[] = new int[2];
guidelayout.getLocationOnScreen(layoutOffset);
//这里避免dialog不是全屏,导致view的绘制位置不对应
location[1] -= layoutOffset[1];
2.3 根据视图缓存获取bitmap
//开启能缓存图片信息
lightView.setDrawingCacheEnabled(true);
//获取视图缓存
lightView.buildDrawingCache();
Bitmap LightBitmap = lightView.getDrawingCache();
if (LightBitmap != null) {
//根据缓存获取Bitmap
LightBitmap = Bitmap.createBitmap(LightBitmap);
} else {
//如果获取不到,则用创建一个view宽高一样的bitmap用canvas把view绘制上去
LightBitmap = Bitmap.createBitmap(vWidth, vHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(LightBitmap);
lightView.draw(canvas);
}
//关闭能缓存图片信息
lightView.setDrawingCacheEnabled(false);
//释放缓存
lightView.destroyDrawingCache();
2.4 创建一个ImageView,设置位置,把bitmap设置为背景,添加到布局
//设置ImageView属性
ImageView newLightView = new ImageView(activity);
newLightView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
newLightView.setImageBitmap(LightBitmap);
//动态设置Viwe的id
int imageViewId = R.id.snack;
newLightView.setId(imageViewId);
//设置位置
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.leftMargin = location[0];
params.topMargin = location[1];
//添加到布局
guidelayout.addView(newLightView, params);
到这里就完成对需要高亮的View的复制了,注意的是这个view最好设置背景颜色,不然有可能没有效果。
3 显示提示图片
获取资源的Bitmap,然后创建ImageView,设Bitmap为背景,在指定位置显示
/**********显示提示图片**********/
//获取提示图片的Bitmap
Bitmap tipBitmap = BitmapFactory.decodeResource(activity.getResources(), tipsImageResourceId);
//设置大小
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int showViewHeight = tipBitmap.getHeight();
int showViewWidth = tipBitmap.getWidth();
//设置ImageView属性
ImageView newTipView = new ImageView(activity);
newTipView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
layoutParams.width = showViewWidth;
layoutParams.height = showViewHeight;
newTipView.setImageBitmap(tipBitmap);
//设置间距(可自行封装)
//layoutParams.topMargin += dipToPix(activity, 20);
//设置相对位置(可自行封装)
layoutParams.addRule(RelativeLayout.BELOW, newLightView.getId());
layoutParams.addRule(RelativeLayout.ALIGN_LEFT, newLightView.getId());
//layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
//添加到布局
guidelayout.addView(newTipView, layoutParams);
完整代码:
package com.mocn.guidehelper;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import java.util.List;
public class GuideHelper {
private List<PageData> pageDatas;
private Activity activity;
private Dialog guideDialog;
private RelativeLayout guidelayout;
public GuideHelper(Activity activity, List<PageData> pageDatas) {
this.activity = activity;
this.pageDatas = pageDatas;
}
public void show() {
if (pageDatas.size() <= 0) {
Log.e("GuideHelper", "没有数据");
return;
}
if (guidelayout == null) {
guidelayout = new RelativeLayout(activity);
//创建Dialog,不遮挡状态栏
guideDialog = new Dialog(activity, R.style.popupDialog);
guideDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
guideDialog.setContentView(guidelayout);
guideDialog.setCanceledOnTouchOutside(false);
guideDialog.setCancelable(false);
guideDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x66000000));
//设置不遮挡状态栏
WindowManager.LayoutParams lay = guideDialog.getWindow().getAttributes();
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
Rect rect = new Rect();
View view = activity.getWindow().getDecorView();//decorView是window中的最顶层view,可以从window中获取到decorView
view.getWindowVisibleDisplayFrame(rect);
lay.height = dm.heightPixels - rect.top;
lay.width = dm.widthPixels;
guideDialog.show();
// //创建Dialog,遮挡状态栏
// guideDialog = new Dialog(activity, android.R.style.Theme_DeviceDefault_Light_DialogWhenLarge_NoActionBar);
// //设置背景颜色
// guideDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x66000000));
// //设置自定义的布局
// guideDialog.setContentView(guidelayout);
// //设置布局的属性
// guideDialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
// //设置点击不能取消
// guideDialog.setCancelable(false);
// //显示Dialog
// guideDialog.show();
}
pageDatas.get(0).getLightView().post(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public void run() {
showTip(guidelayout);
}
});
}
private void showTip(final RelativeLayout guidelayout) {
View lightView = pageDatas.get(0).getLightView();
int tipsImageResourceId = pageDatas.get(0).getTipsImageResourceId();
/**********显示高亮控件**********/
//获取view的宽高
int vWidth = lightView.getMeasuredWidth();
int vHeight = lightView.getMeasuredHeight();
//如果宽高都小于等于0,再measure试下获取
if (vWidth <= 0 || vHeight <= 0) {
ViewGroup.LayoutParams mlayoutParams = lightView.getLayoutParams();
lightView.measure(mlayoutParams.width, mlayoutParams.height);
vWidth = lightView.getMeasuredWidth();
vHeight = lightView.getMeasuredHeight();
}
//获取不到宽高则返回操作
if (vWidth <= 0 || vHeight <= 0) {
Log.e("GuideHelper", "宽高都小于等于0");
return;
}
//获取view在屏幕的位置
int[] location = new int[2];
lightView.getLocationOnScreen(location);
//获取layout在屏幕上的位置
int layoutOffset[] = new int[2];
guidelayout.getLocationOnScreen(layoutOffset);
//这里避免dialog不是全屏,导致view的绘制位置不对应
location[1] -= layoutOffset[1];
//开启能缓存图片信息
lightView.setDrawingCacheEnabled(true);
//获取视图缓存
lightView.buildDrawingCache();
Bitmap LightBitmap = lightView.getDrawingCache();
if (LightBitmap != null) {
//根据缓存获取Bitmap
LightBitmap = Bitmap.createBitmap(LightBitmap);
} else {
//如果获取不到,则用创建一个view宽高一样的bitmap用canvas把view绘制上去
LightBitmap = Bitmap.createBitmap(vWidth, vHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(LightBitmap);
lightView.draw(canvas);
}
//关闭能缓存图片信息
lightView.setDrawingCacheEnabled(false);
//释放缓存
lightView.destroyDrawingCache();
//设置ImageView属性
ImageView newLightView = new ImageView(activity);
newLightView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
newLightView.setImageBitmap(LightBitmap);
//动态设置Viwe的id
int imageViewId = R.id.snack;
newLightView.setId(imageViewId);
//设置位置
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.leftMargin = location[0];
params.topMargin = location[1];
//添加到布局
guidelayout.addView(newLightView, params);
/**********显示提示图片**********/
//获取提示图片的Bitmap
Bitmap tipBitmap = BitmapFactory.decodeResource(activity.getResources(), tipsImageResourceId);
//设置大小
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int showViewHeight = tipBitmap.getHeight();
int showViewWidth = tipBitmap.getWidth();
//设置ImageView属性
ImageView newTipView = new ImageView(activity);
newTipView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
layoutParams.width = showViewWidth;
layoutParams.height = showViewHeight;
newTipView.setImageBitmap(tipBitmap);
//设置间距(可自行封装)
//layoutParams.topMargin += dipToPix(activity, 20);
//设置相对位置(可自行封装)
layoutParams.addRule(RelativeLayout.BELOW, newLightView.getId());
layoutParams.addRule(RelativeLayout.ALIGN_LEFT, newLightView.getId());
//layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
//添加到布局
guidelayout.addView(newTipView, layoutParams);
//点击布局执行下一步动作
guidelayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//清理布局控件
guidelayout.removeAllViews();
//操作有第二步继续执行
if (pageDatas.size() >= 2) {
pageDatas.remove(0);
show();
} else {
guideDialog.dismiss();
}
}
});
}
public static class PageData {
/**
* 高亮View
*/
private View lightView;
/**
* 提示图片id
*/
private int tipsImageResourceId;
public PageData(View lightView, int tipsImageResourceId) {
this.lightView = lightView;
this.tipsImageResourceId = tipsImageResourceId;
}
public View getLightView() {
return lightView;
}
public void setLightView(View lightView) {
this.lightView = lightView;
}
public int getTipsImageResourceId() {
return tipsImageResourceId;
}
public void setTipsImageResourceId(int tipsImageResourceId) {
this.tipsImageResourceId = tipsImageResourceId;
}
}
/**
* dip转px
*/
public int dipToPix(Context context, int dip) {
int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, context.getResources().getDisplayMetrics());
return size;
}
/**
* 实例化布局
*
* @param layoutId
* @return
*/
public View inflate(int layoutId) {
LayoutInflater inflater = LayoutInflater.from(activity);
View view = inflater.inflate(layoutId, null);
return view;
}
}
使用方法:
package com.mocn.guidehelper;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class GuideHelperActivity extends AppCompatActivity {
private TextView tv_first;
private Button btn_first;
private Button btn_second;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_guide_helper);
tv_first = (TextView) findViewById(R.id.tv_first);
btn_first = (Button) findViewById(R.id.btn_first);
btn_second = (Button) findViewById(R.id.btn_second);
btn_first.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<GuideHelper.PageData> pageDatas = new ArrayList<GuideHelper.PageData>();
pageDatas.add(new GuideHelper.PageData(btn_first, R.mipmap.tip1));
pageDatas.add(new GuideHelper.PageData(tv_first, R.mipmap.tip1));
pageDatas.add(new GuideHelper.PageData(btn_second, R.mipmap.tip1));
GuideHelper guideHelper = new GuideHelper(GuideHelperActivity.this, pageDatas);
guideHelper.show();
}
});
}
}
在这个例子当中最重要的是复制一个view,而复制一个View的重点又是根据getDrawingCache()获取缓存的Bitmap,并显示在界面上
getDrawingCache()被经常用来做屏幕截图,比如说:
看下手机截图:
截图之后的效果图
可以看到,截取了除状态栏外,标题栏和内容的界面,关键代码如下:
/**
* 截取当前程序界面
*/
private void saveView() {
//DecorView只有一个子元素为LinearLayout。代表整个Window界面,包含通知栏,标题栏,内容显示栏三块区域
View decorView = getWindow().getDecorView();
//开启能缓存图片信息
decorView.setDrawingCacheEnabled(true);
//获取视图缓存
decorView.buildDrawingCache();
//根据缓存获取Bitmap
Bitmap bmp = decorView.getDrawingCache();
Rect rect = new Rect();
//getWindowVisibleDisplayFrame方法可以获取到程序显示的区域,包括标题栏,但不包括状态栏
decorView.getWindowVisibleDisplayFrame(rect);
//获取状态栏高度
int statusBarHeight = rect.top;
//获取图片宽高
int width = bmp.getWidth();
int height = bmp.getHeight();
//坐标轴和高度都减去状态栏的高度
Bitmap saveBmp = Bitmap.createBitmap(bmp, 0, statusBarHeight,
width, height - statusBarHeight, null, false);
//关闭能缓存图片信息
decorView.setDrawingCacheEnabled(false);
//释放缓存
decorView.destroyDrawingCache();
//将图片保存到SD卡
saveBitmap("ScreenShot", saveBmp);
}
有时候,我们需要截取超过屏幕外的界面,比如说listview和scrollView,就要动态计算加起来的子view的总高度了,效果图如下:
代码如下:
/**
* 截取超过程序界面的长图
*/
private void saveLongView() {
int h = 0;
// 获取listView实际高度
// for (int i = 0; i < listView.getChildCount(); i++) {
// h += listView.getChildAt(i).getHeight();
// }
// 获取scrollView实际高度
for (int i = 0; i < scrollView.getChildCount(); i++) {
h += scrollView.getChildAt(i).getHeight();
}
//如果是webView
// Picture snapShot = webView.capturePicture();
// Bitmap bmp = Bitmap.createBitmap(snapShot.getWidth(), snapShot.getHeight(), Bitmap.Config.ARGB_8888);
// Canvas canvas = new Canvas(bmp);
// snapShot.draw(canvas);
Log.i("ScreenShot", " 高度:" + scrollView.getHeight());
Log.i("ScreenShot", "实际高度:" + h);
Bitmap bitmap;
// 创建对应大小的bitmap
bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
scrollView.draw(canvas);
//将图片保存到SD卡
saveBitmap("ScreenShot", bitmap);
}