GuideView的封装实现app功能引导页

本文实例为大家分享了GuideView的封装实现app功能引导页的具体代码,供大家参考,具体内容如下

package oschina.comxianbing100.yindao;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;

import java.util.List;

/**
 * author : Majunbao
 * date : 2019/3/4 16:32
 * description :App第一次打开功能蒙版 引导
 */
public class GuideView extends RelativeLayout implements ViewTreeObserver.OnGlobalLayoutListener {
 private final String TAG = getClass().getSimpleName();
 private Context mContent;
 private List<View> mViews;
 private boolean first = true;
 /**
  * targetView前缀。SHOW_GUIDE_PREFIX + targetView.getId()作为保存在SP文件的key。
  */
 private static final String SHOW_GUIDE_PREFIX = "show_guide_on_view_";
 /**
  * GuideView 偏移量
  */
 private int offsetX, offsetY;
 /**
  * targetView 的外切圆半径
  */
 private int radius;
 /**
  * 需要显示提示信息的View
  */
 private View targetView;
 /**
  * 自定义View
  */
 private View customGuideView;
 /**
  * 透明圆形画笔
  */
 private Paint mCirclePaint;
 /**
  * 背景色画笔
  */
 private Paint mBackgroundPaint;
 /**
  * targetView是否已测量
  */
 private boolean isMeasured;
 /**
  * targetView圆心
  */
 private int[] center;
 /**
  * 绘图层叠模式
  */
 private PorterDuffXfermode porterDuffXfermode;
 /**
  * 绘制前景bitmap
  */
 private Bitmap bitmap;
 /**
  * 背景色和透明度,格式 #aarrggbb
  */
 private int backgroundColor;
 /**
  * Canvas,绘制bitmap
  */
 private Canvas temp;
 /**
  * 相对于targetView的位置.在target的那个方向
  */
 private Direction direction;

 /**
  * 形状
  */
 private MyShape myShape;
 /**
  * targetView左上角坐标
  */
 private int[] location;
 private boolean onClickExit;
 private OnClickCallback onclickListener;
 private RelativeLayout guideViewLayout;

 public void restoreState() {
  Log.v(TAG, "restoreState");
  offsetX = offsetY = 0;
  radius = 0;
  mCirclePaint = null;
  mBackgroundPaint = null;
  isMeasured = false;
  center = null;
  porterDuffXfermode = null;
  bitmap = null;
  needDraw = true;
  //  backgroundColor = Color.parseColor("#00000000");
  temp = null;
  //  direction = null;

 }

 public int[] getLocation() {
  return location;
 }

 public void setLocation(int[] location) {
  this.location = location;
 }

 public GuideView(Context context) {
  super(context);
  this.mContent = context;
  init();
 }

 public int getRadius() {
  return radius;
 }

 public void setRadius(int radius) {
  this.radius = radius;
 }

 public void setOffsetX(int offsetX) {
  this.offsetX = offsetX;
 }

 public void setOffsetY(int offsetY) {
  this.offsetY = offsetY;
 }

 public void setDirection(Direction direction) {
  this.direction = direction;
 }

 public void setShape(MyShape shape) {
  this.myShape = shape;
 }

 public void setCustomGuideView(View customGuideView) {
  this.customGuideView = customGuideView;
  if (!first) {
   restoreState();
  }
 }

 public void setBgColor(int background_color) {
  this.backgroundColor = background_color;
 }

 public View getTargetView() {
  return targetView;
 }

 public void setTargetView(View targetView) {
  this.targetView = targetView;
  //  restoreState();
  if (!first) {
   //   guideViewLayout.removeAllViews();
  }
 }

 private void init() {
 }

 public void showOnce() {
  if (targetView != null) {
   mContent.getSharedPreferences(TAG, Context.MODE_PRIVATE).edit().putBoolean(generateUniqId(targetView), true).commit();
  }
 }

 private boolean hasShown() {
  if (targetView == null)
   return true;
  return mContent.getSharedPreferences(TAG, Context.MODE_PRIVATE).getBoolean(generateUniqId(targetView), false);
 }

 private String generateUniqId(View v) {
  return SHOW_GUIDE_PREFIX + v.getId();
 }

 public int[] getCenter() {
  return center;
 }

 public void setCenter(int[] center) {
  this.center = center;
 }

 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
 public void hide() {
  Log.v(TAG, "hide");
  if (customGuideView != null) {
   targetView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
   this.removeAllViews();
   ((FrameLayout) ((Activity) mContent).getWindow().getDecorView()).removeView(this);
   restoreState();
  }
 }

 public void show() {
  Log.v(TAG, "show");
  if (hasShown())
   return;

  if (targetView != null) {
   targetView.getViewTreeObserver().addOnGlobalLayoutListener(this);
  }

  this.setBackgroundResource(R.color.transparent);

  ((FrameLayout) ((Activity) mContent).getWindow().getDecorView()).addView(this);
  first = false;
 }

 /**
  * 添加提示文字,位置在targetView的下边
  * 在屏幕窗口,添加蒙层,蒙层绘制总背景和透明圆形,圆形下边绘制说明文字
  */
 private void createGuideView() {
  Log.v(TAG, "createGuideView");

  // 添加到蒙层
  //  if (guideViewLayout == null) {
  //   guideViewLayout = new RelativeLayout(mContent);
  //  }

  // Tips布局参数
  LayoutParams guideViewParams;
  guideViewParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  guideViewParams.setMargins(0, center[1] + radius + 10, 0, 0);

  if (customGuideView != null) {

   //   LayoutParams guideViewParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
   if (direction != null) {
    int width = this.getWidth();
    int height = this.getHeight();

    int left = center[0] - radius;
    int right = center[0] + radius;
    int top = center[1] - radius;
    int bottom = center[1] + radius;
    switch (direction) {
     case TOP:
      this.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
      guideViewParams.setMargins(offsetX, offsetY - height + top, -offsetX, height - top - offsetY);
      break;
     case LEFT:
      this.setGravity(Gravity.RIGHT);
      guideViewParams.setMargins(offsetX - width + left, top + offsetY, width - left - offsetX, -top - offsetY);
      break;
     case BOTTOM:
      this.setGravity(Gravity.CENTER_HORIZONTAL);
      guideViewParams.setMargins(offsetX, bottom + offsetY, -offsetX, -bottom - offsetY);
      break;
     case RIGHT:
      guideViewParams.setMargins(right + offsetX, top + offsetY, -right - offsetX, -top - offsetY);
      break;
     case LEFT_TOP:
      this.setGravity(Gravity.RIGHT | Gravity.BOTTOM);
      guideViewParams.setMargins(offsetX - width + left, offsetY - height + top, width - left - offsetX, height - top - offsetY);
      break;
     case LEFT_BOTTOM:
      this.setGravity(Gravity.RIGHT);
      guideViewParams.setMargins(offsetX - width + left, bottom + offsetY, width - left - offsetX, -bottom - offsetY);
      break;
     case RIGHT_TOP:
      this.setGravity(Gravity.BOTTOM);
      guideViewParams.setMargins(right + offsetX, offsetY - height + top, -right - offsetX, height - top - offsetY);
      break;
     case RIGHT_BOTTOM:
      guideViewParams.setMargins(right + offsetX, bottom + offsetY, -right - offsetX, -top - offsetY);
      break;
    }
   } else {
    guideViewParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    guideViewParams.setMargins(offsetX, offsetY, -offsetX, -offsetY);
   }

   //   guideViewLayout.addView(customGuideView);

   this.addView(customGuideView, guideViewParams);
  }
 }

 /**
  * 获得targetView 的宽高,如果未测量,返回{-1, -1}
  *
  * @return
  */
 private int[] getTargetViewSize() {
  int[] location = {-1, -1};
  if (isMeasured) {
   location[0] = targetView.getWidth();
   location[1] = targetView.getHeight();
  }
  return location;
 }

 /**
  * 获得targetView 的半径
  *
  * @return
  */
 private int getTargetViewRadius() {
  if (isMeasured) {
   int[] size = getTargetViewSize();
   int x = size[0];
   int y = size[1];

   return (int) (Math.sqrt(x * x + y * y) / 2);
  }
  return -1;
 }

 boolean needDraw = true;

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  Log.v(TAG, "onDraw");

  if (!isMeasured)
   return;

  if (targetView == null)
   return;

  //  if (!needDraw) return;

  drawBackground(canvas);

 }

 private void drawBackground(Canvas canvas) {
  Log.v(TAG, "drawBackground");
  needDraw = false;
  // 先绘制bitmap,再将bitmap绘制到屏幕
  bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
  temp = new Canvas(bitmap);

  // 背景画笔
  Paint bgPaint = new Paint();
  if (backgroundColor != 0)
   bgPaint.setColor(backgroundColor);
  else
   bgPaint.setColor(getResources().getColor(R.color.shadow));

  // 绘制屏幕背景
  temp.drawRect(0, 0, temp.getWidth(), temp.getHeight(), bgPaint);

  // targetView 的透明圆形画笔
  if (mCirclePaint == null)
   mCirclePaint = new Paint();
  porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);// 或者CLEAR
  mCirclePaint.setXfermode(porterDuffXfermode);
  mCirclePaint.setAntiAlias(true);

  if (myShape != null) {
   RectF oval = new RectF();
   switch (myShape) {
    case CIRCULAR://圆形
     temp.drawCircle(center[0], center[1], radius, mCirclePaint);//绘制圆形
     break;
    case ELLIPSE://椭圆
     //RectF对象
     oval.left = center[0] - 150;        //左边
     oval.top = center[1] - 50;         //上边
     oval.right = center[0] + 150;        //右边
     oval.bottom = center[1] + 50;        //下边
     temp.drawOval(oval, mCirclePaint);     //绘制椭圆
     break;
    case RECTANGULAR://圆角矩形
     //RectF对象
     oval.left = center[0] - 150;        //左边
     oval.top = center[1] - 50;         //上边
     oval.right = center[0] + 150;        //右边
     oval.bottom = center[1] + 50;        //下边
     temp.drawRoundRect(oval, radius, radius, mCirclePaint);     //绘制圆角矩形
     break;
   }
  } else {
   temp.drawCircle(center[0], center[1], radius, mCirclePaint);//绘制圆形
  }

  // 绘制到屏幕
  canvas.drawBitmap(bitmap, 0, 0, bgPaint);
  bitmap.recycle();
 }

 public void setOnClickExit(boolean onClickExit) {
  this.onClickExit = onClickExit;
 }

 public void setOnclickListener(OnClickCallback onclickListener) {
  this.onclickListener = onclickListener;
 }

 private void setClickInfo() {
  final boolean exit = onClickExit;
  setOnClickListener(new OnClickListener() {
   @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
   @Override
   public void onClick(View v) {
    if (onclickListener != null) {
     onclickListener.onClickedGuideView();
    }
    if (exit) {
     hide();
    }
   }
  });
 }

 @Override
 public void onGlobalLayout() {
  if (isMeasured)
   return;
  if (targetView.getHeight() > 0 && targetView.getWidth() > 0) {
   isMeasured = true;
  }

  // 获取targetView的中心坐标
  if (center == null) {
   // 获取右上角坐标
   location = new int[2];
   targetView.getLocationInWindow(location);
   center = new int[2];
   // 获取中心坐标
   center[0] = location[0] + targetView.getWidth() / 2;
   center[1] = location[1] + targetView.getHeight() / 2;
  }
  // 获取targetView外切圆半径
  if (radius == 0) {
   radius = getTargetViewRadius();
  }
  // 添加GuideView
  createGuideView();
 }

 /**
  * 定义GuideView相对于targetView的方位,共八种。不设置则默认在targetView下方
  */
 enum Direction {
  LEFT, TOP, RIGHT, BOTTOM,
  LEFT_TOP, LEFT_BOTTOM,
  RIGHT_TOP, RIGHT_BOTTOM
 }

 /**
  * 定义目标控件的形状,共3种。圆形,椭圆,带圆角的矩形(可以设置圆角大小),不设置则默认是圆形
  */
 enum MyShape {
  CIRCULAR, ELLIPSE, RECTANGULAR
 }

 /**
  * GuideView点击Callback
  */
 interface OnClickCallback {
  void onClickedGuideView();
 }

 public static class Builder {
  static GuideView guiderView;
  static Builder instance = new Builder();
  Context mContext;

  private Builder() {
  }

  public Builder(Context ctx) {
   mContext = ctx;
  }

  public static Builder newInstance(Context ctx) {
   guiderView = new GuideView(ctx);
   return instance;
  }

  public Builder setTargetView(View target) {
   guiderView.setTargetView(target);
   return instance;
  }

  public Builder setBgColor(int color) {
   guiderView.setBgColor(color);
   return instance;
  }

  public Builder setDirction(Direction dir) {
   guiderView.setDirection(dir);
   return instance;
  }

  public Builder setShape(MyShape shape) {
   guiderView.setShape(shape);
   return instance;
  }

  public Builder setOffset(int x, int y) {
   guiderView.setOffsetX(x);
   guiderView.setOffsetY(y);
   return instance;
  }

  public Builder setRadius(int radius) {
   guiderView.setRadius(radius);
   return instance;
  }

  public Builder setCustomGuideView(View view) {
   guiderView.setCustomGuideView(view);
   return instance;
  }

  public Builder setCenter(int X, int Y) {
   guiderView.setCenter(new int[]{X, Y});
   return instance;
  }

  public Builder showOnce() {
   guiderView.showOnce();
   return instance;
  }

  public GuideView build() {
   guiderView.setClickInfo();
   return guiderView;
  }

  public Builder setOnclickExit(boolean onclickExit) {
   guiderView.setOnClickExit(onclickExit);
   return instance;
  }

  public Builder setOnclickListener(final OnClickCallback callback) {
   guiderView.setOnclickListener(callback);
   return instance;
  }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View实现水波纹引导动画

    一.实现效果图 关于贝塞尔曲线 二.实现代码 1.自定义view package com.czhappy.showintroduce.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.grap

  • android 引导界面的实现方法

    复制代码 代码如下: /** * 实现 * @author dujinyang * */ 顺序是: OneAcitivity  -->MainActivity -> TwoActivity 然后第2次进去就是:OneActivity -> TwoActivity 代码里都有注释的了,这里就不多说了.OneActivity的代码如下: [java] 复制代码 代码如下: package cn.djy.activity; import android.app.Activity; import

  • Android启动引导页使用ViewPager实现

    我们在安装某个APP的时候,基本都会有一个引导页的提示,他们可以打广告,或者介绍新功能的加入和使用说明等. 一般都支持滑动并且下面有几个点,显示共有多少页和当前图片的位置,在IOS上这个实现起来比较简单,但在安卓上如何实现呢. 今天就和大家一起来学习用官方v4支持包下的ViewPager来实现这个效果. 先上图: 下面是我的实现,一个xml布局,一个GuideActivity和一个GuidePageAdapter.  先上XML. <?xml version="1.0" enco

  • Android客户端首次启动引导界面

    刚做完一个比赛项目,来写点以后能用着的东西–Android客户端的首次启动页面,而且这个以后复用的几率很大,也不怎么修改,特留下为以后准备,同时为初学者提供一个帮助. 实现思路是:用SharedPreferences保存一个首次登陆的信息,默认是true,进入MainActivity后对其经行赋flase保存,把GuideActivity作为软件的启动界面,如果是第一次启动,就初始化该activity,不是的话直接跳转到应用主界面activity,这里有些不合理,启动界面如果设置成一个每次app

  • Android引导页面的简单实现

    第一次进入应用的时候,都会有一个引导页面,引导页面的实现起来也很简单,实现的方式也有很多,下面是自己写的一个引导页面的效果,大致的实现思路为: 最外层是一个FragmentActivity,里面包含4个fragment,在fragment中给ViewPager开一个循环,这样实现了图片播,其实效果就是在FragmentActivity中几个fragment滑动切换, fragment中ViewPager无限轮播和滑动切换: 下面为代码实现: public class MainActivity e

  • Android开发实战之漂亮的ViewPager引导页

    目前很多软件安装时都会出现引导页面,用户体验很好. 下面就来DIY下: 因为视频上传很麻烦,所以截图了. 首先看看效果图: 点击小点可自由切换,滑动也可以自由切换,最后一个导航页添加了点击跳转. 开始实现引导页: 一.采集需要的图片放入drawable文件里 二.初始化每个导航页的视图 import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.suppo

  • Android GuideView实现首次登陆引导

    简介:最最轻量级的新手引导库,能够快速为任何一个 View 创建一个遮罩层,支持单个页面,多个引导提示,支持为高亮区域设置不同的图形,支持引导动画,方便扩展 项目地址:binIoter/GuideView GuideView 本系统能够快速的为一个 Activity 里的任何一个 View 控件创建一个遮罩式的导航页. 工作原理 首先它需要一个目标 View 或者它的 id,我们通过 findViewById 来得到这个 View,计算它在屏幕上的区域 targetRect,通过这个区域,开始绘

  • android使用ViewPager组件实现app引导查看页面

    我们安装完app后第一次打开app,通常都会有一个翻页图片形式的app引导简介说明.下面我们来实现这个功能.ViewPager这个组件与ListView和RecyclerView在使用上有很高的相似处,所以你如果学习过以上两种列表滚动控件,可以基于它们帮助你理解ViewPager的使用. 实现步骤: 1.在左右滑动页面的界面中,通常在屏幕下方都会有一个小圆点,我们需要给这些小圆点的实现准备一些东西.在drawable目录下创建两个xml文件,一个画出未选中的圆点,一个画出选中后的圆点. 2.准备

  • Android绘制炫酷的引导界面

    先看一下我们要开发的界面(三张图片,滑到最后一个会出现开始体验的Button,下面的小红点会跟着一起滑动): 首先看一下布局文件: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.and

  • GuideView的封装实现app功能引导页

    本文实例为大家分享了GuideView的封装实现app功能引导页的具体代码,供大家参考,具体内容如下 package oschina.comxianbing100.yindao; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; impo

  • Flutter实现App功能引导页

    App功能介绍页,主要是由介绍app功能的几张图片和当前页指示符组成,如下效果 我们来一步一步实现上面的界面,左右滑动切换显示功能页,这个可以通过PageView来实现,底部的指示符半透明覆盖在PageView上,开发过Android同学知道可以用Framelayout布局来实现,Flutter上也有类似的控件Stack,我们先完成骨架代码 // An highlighted block void main() => runApp(App()); class App extends Statel

  • RxJava两步打造华丽的Android引导页

    前言 之前的一篇文章:基于RxJava实现酷炫启动页 中,我们尝试了用RxJava实现酷炫的启动页,今天我们在此基础上加入首次使用APP时的引导页功能. 效果如下图: 思路:思路其实很简单,就是在WelcomeActivity 中setContentView()之前判断是否是首次打开APP,若是,则去启动引导页(WelcomeGuideActivity)并return:若不是,则直接setContentView(),然后启动动画再启动MainActivity. 一.WelcomeActivity

  • iOS App引导页开发教程

    引导页功能简介 方式一: 判断程序是否首次启动,如果是将GuidePageViewController作为窗口的根视图控制器.GuidePageViewController有三个子控件:一个UIScrollView.一个UIPageControl.一个UIButton(默认隐藏),UIScrollView有多个UIImageView子控件,当滚动到最后一页UIButton展示,点击立即体验然后将窗口的根视图控制器设置为UITabBarController: 方式二: 也可以直接将根视图控制器设置

  • iOS App初次启动时的用户引导页制作实例分享

    应用程序APP一般都有引导页,引导页可以作为操作指南指导用户熟悉使用:也可以展现给用户,让用户了解APP的功能作用.引导页制作简单,一般只需要一组图片,再把图片组展现出来就可以了.展示图片组常用UIScrollView来分页显示,并且由UIPageControl页面控制器控制显示当前页.UIScrollView和UIPageControl搭配会更加完美地展现引导页的功能作用.下面我们来看具体的实例: 我们用NSUserDefaults类来判断程序是不是第一次启动或是否更新,在 AppDelega

  • Android开发实现的ViewPager引导页功能(动态加载指示器)详解

    本文实例讲述了Android开发实现的ViewPager引导页功能(动态加载指示器).分享给大家供大家参考,具体如下: 先看效果图咯~ 现在几乎每个App都会有引导页,是不是感觉很炫很厉害,所以就想做出来一个学习一下~让自己的App看起来更加的美观~ 现在来分析一下: 这个引导页可以分为俩部分~ 1.小红点--来提醒这是第几页了~ 2."开始体验"这个Button--可以进入主界面,但是要控制这个Button只能在最后一页出现 布局的话使用相对布局~ 那现在来看看布局吧: activi

  • Android实现过渡动画、引导页 Android判断是否第一次启动App

    目前的App在安装后,第一次打开,都会显示两秒左右的logo,然后进入引导页.如果关闭App,再重新打开,则只会显示logo,然后直接进入主页. 最近写了这个,记录一下. 首先是过渡动画,因为它不论App是否第一次启动都会显示. 这里我使用了Handler的postDelayed()方法.把过渡动画的Activity设为默认启动的Activity.在当前Activity中,执行postDelayed()方法,把延时的时长设为两秒即可. 过渡页面如下:transition_view.xml <?x

  • Android使用ViewPager完成app引导页

    本文实例为大家分享了Android使用ViewPager完成app引导页的具体代码,供大家参考,具体内容如下 public class MainActivity extends AppCompatActivity { // int[] resourceId = { // R.layout.first, // R.layout.second, // R.layout.third // }; List<View> mListView; ViewPager viewPager; ViewGroup

  • 微信小程序的引导页实现代码

    前一段时间写了一个微信小程序的项目,其中就有引导页面这一功能模块,接下来给大家说一下这一块的怎么实现的以及一个思路吧! 一.引导页 下给大家康康效果图是啥样舍的呢!! 其实就是和轮播图差不多,就是当用户滑动到最后一页的时候显示跳转页面就完事了. 二.代码分析 第一步:先找到小程序目录下面的app.json然后在"pages"配置好页面 { "pages": [ "pages/guidance/guidance", // 配置引导页面 "

随机推荐