开发蒙版向导的一些领悟

本文介绍了一种使用自定义Dialog实现界面元素高亮显示的方法,通过复制目标View并覆盖以实现局部高亮效果,同时展示如何添加提示图标增强用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先上效果图!


奉上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);
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值