2018-07-06 新手引导实现(新手引导悬浮层) GuideTipView

整理下以前项目中的一个功能实现

1.需求:

应用升级后或者本身功能或者跳转提示,高亮一个区域,添加一个图片或者文字引导,用户点击高亮区域或者其他区域后消失

1.1UI设计效果

image.png
image.png

2实现

高亮一个区域,矩形或者圆形;
点击高亮区消失|点击图片消失|点击文字消失|点击随便哪儿都消失|不可以点击消失而定时自动消失

2.1demo

在Activity中使用


请忽略其中的英语提示,英语是体育老师教的.gif

在viewGroup中使用


请忽略其中的英语提示,英语是体育老师教的.gif

使用场景:Activity或者 context不是Activity也可以的ViewGroup中(比如键盘)

2.2关键类( 实际上就这个类就行了)

/**
 * 类说明: 新手引导
*
*  指定一个控件高亮,其余区域半透明,可以添加引导图片或者文字
*
* @author qing
* @time 2018/5/28 11:47
*
* @param mContext         上下文
* @param targetView       设置的目标控件
*/
 class GuideTipView(private val mContext: Context, private val targetView: View) : RelativeLayout(mContext) {

/**
 * : test 用设置引导是否只显示一次  mIsDebug 显示多次 ; 默认false只显示一次
 */
var mIsDebug = false
//高亮控件的中心点(相对外层容器 不一定是屏幕)
private var mHighlightAreaCenterPoint = Point()
//高亮的半径
private var mHighlightAreaRadius = 0
// 屏幕尺寸
private var mScreenWidth: Int = 0
private var mScreenHeight: Int = 0
//半透明背景颜色
private var bgColor: Int = Color.parseColor("#66000000")
//================= 指引图片 =============
//指引图片
private var mGuideIv = ImageView(context)
//指引图片和高亮目标距离
var mGuideIvDiffY = 0
var mGuideIvDiffX = 0
//引导图片的资源
var mGuideIvRes: Int = 0
//================= 指引文字 =============
//指引文字
var mGuideTv: TextView = LayoutInflater.from(context).inflate(com.qing.guidetipview.R.layout.tip_text, null, false) as TextView
//指引文字和高亮目标距离
var mGuideTvDiffY = 0
var mGuideTvDiffX = 0
//是否显示指引文字
var mShowGuideTv = false
//被高亮了的背景bitmap
private var mBgBitmap: Bitmap? = null
//容器画板 用于生成带高亮区域的背景bitmap
private var mTempCanvas: Canvas? = null
//背景画笔
private var mBgPaint: Paint = Paint()
private var isMeasured: Boolean = false
//获取的目标控件的位置
private var mTargetViewLeft: Int = 0
private var mTargetViewTop: Int = 0
// 是否已经显示过了
private var mHasShow: Boolean = false
//是否自动消失
var mAutoDismiss = true
//自动消失时间
private var mDismissDelayTime: Long = 4000L //默认4秒消失
//点击消失的类型 
var mClickClickDismissType: ClickDismissType = ClickDismissType.DISMISS_IN_HIGH_LIGHT_AREA
//蒙版区域(指定不是整个Activity显示引导)
var mContainerForGuide: ViewGroup? = null
//显示圆形高亮区
var mShowCircleGuide = true
//是否高亮边缘点划线
var mShowDashPath = true
//消失监听
var mDismissListener: DismissListener? = null
//引导图片的动画
var mGuideIvAnimator: Animator? = null

//设置高亮区点划线
var mDashStrokeWidth = 5f
var mDashWidth = 20f
var mDashGap = 10f
var mDashOffset = mDashStrokeWidth
//bg alpha
var mBgAlpha=150

// 控件显示状态存储SP的ID
private var mGuideInInSp = ""

companion object {
    const val SHOW_GUIDE = 101
    const val DISMISS_GUIDE = 102
    //SP中保存是否已经显示
    const val SP_NAME = "GUIDE_VIEWS"
    //SP前缀
    const val SP_NAME_ID_PREFIX = "GUIDE_VIEW_KEY"

    /**
     * 检测是否已经显示过了
     * @return ture 标识没有显示过改控件的引导
     */
    fun hasNotShowThisGuide(context: Context, id: Int): Boolean {
        val guideId = "$id"
        return if (TextUtils.isEmpty(guideId)) {
            false
        } else {
            !context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE).getBoolean("$SP_NAME_ID_PREFIX$guideId", false)
        }
    }

    //防止测试时多次点击添加
    var nowShowingTargetId = -1
}

/**
 * 显示和移除提示的handler
 */
private var mHandler = @SuppressLint("HandlerLeak")
object : Handler() {
    override fun handleMessage(msg: Message?) {
        if (msg == null) {
            return
        }
        when (msg.what) {
        //显示新手引导
            SHOW_GUIDE -> {
                showGuide()
                //默认自动消失
                if (mAutoDismiss) {
                    val obtain = Message.obtain()
                    obtain.what = DISMISS_GUIDE
                    sendMessageDelayed(obtain, mDismissDelayTime)
                }
                if (BuildConfig.DEBUG) {
                    Log.i("GuideTipView", "SHOW_GUIDE==== $mGuideInInSp   ===$mDismissDelayTime")
                }
            }
        //移除新手引导
            DISMISS_GUIDE -> {
                if (BuildConfig.DEBUG) {
                    Log.i("GuideTipView", "DISMISS_GUIDE==== $mGuideInInSp")
                }
                dismiss(ClickDismissType.AUTO_DISMISS)
            }
        }
    }
}


/**
 * 点击哪个区域可以消失
 */
enum class ClickDismissType {
    //点击高亮消失
    DISMISS_IN_HIGH_LIGHT_AREA,
    //点击指引图片消失
    DISMISS_IN_GUIDE_IV,
    //点击指引文字消失
    DISMISS_IN_GUIDE_TV,
    //点击所有区域消失
    DISMISS_IN_ALL_AREA,
    //自动消失
    AUTO_DISMISS
}

init {
    this.visibility = View.GONE
    this.setBackgroundColor(Color.TRANSPARENT)
    mGuideInInSp = "${targetView.id}"
    // QTIP: 2018/5/28  设置Z方向位置,避免被系统状态栏遮住
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        elevation = 10F
    }
}


override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    super.onSizeChanged(w, h, oldw, oldh)
    mScreenWidth = w
    mScreenHeight = h
}


/**
 * 添加指引图片
 */
private fun addGuideIv() {
    //设置要显示的引导图片
    mGuideIv.setImageDrawable(ContextCompat.getDrawable(context, mGuideIvRes))
    val guideBitmap = BitmapFactory.decodeResource(resources, mGuideIvRes)
    guideBitmap?.let {
        //qtip 设置 引导图片显示的位置参数
        val params: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT)
        val topMargin = mHighlightAreaCenterPoint.y - targetView.height / 2 + if (mGuideIvDiffY == 0) targetView.height + 50 else mGuideIvDiffY
        params.topMargin = topMargin
        //Log.e(TAG, "params.topMargin===" + params.topMargin);
        // 判断设置图片在左边还是右边显示
        if (mTargetViewLeft < mScreenWidth / 4) {// 在目标右边设置guideView
            params.leftMargin = mHighlightAreaCenterPoint.x - targetView.width / 2 + if (mGuideIvDiffX == 0) 2 * targetView.width / 3 else mGuideIvDiffX
            //  Log.e(TAG, "params.leftMargin===" + params.leftMargin);
        } else {// 在目标左边设置guideView
            params.leftMargin = mHighlightAreaCenterPoint.x - targetView.width / 2 + if (mGuideIvDiffX == 0) -it.width + targetView.width / 3 else mGuideIvDiffX
            //  Log.e(TAG, "在目标左边设置guideView------params.leftMargin===" + params.leftMargin);
        }
        mGuideIv.layoutParams = params
        //this.removeAllViews()
        //qtip 添加显示引导图片
        this.addView(mGuideIv)
    }
    // ===========    初始化指引图片  end  ================ //
}


private fun addGuideTv() {
    if (!mShowGuideTv) {
        return
    }
    val diffX = mGuideTvDiffX
    val diffY = mGuideTvDiffY
    val params: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT)
    params.topMargin = mHighlightAreaCenterPoint.y - targetView.height / 2 + if (diffY == 0) targetView.height + 50 else diffY
    // 判断设置图片在左边还是右边显示
    if (mTargetViewLeft < mScreenWidth / 4) {// 在目标右边设置guideView
        params.leftMargin = mHighlightAreaCenterPoint.x - targetView.width / 2 + if (diffX == 0) 2 * targetView.width / 3 else diffX
    } else {// 在目标左边设置guideView
        params.leftMargin = mHighlightAreaCenterPoint.x - targetView.width / 2 + if (diffX == 0) -mGuideTv!!.width + targetView.width / 3 else diffX
    }
    mGuideTv.layoutParams = params
    this.addView(mGuideTv)
}


/**
 * 获取需要高亮的控件相对于屏幕或者外层容器的left和top
 */
private fun getTargetViewLocation() {
    // QTIP: 2018/5/29  如果是在activity中 获取的是在屏幕上的位置 ,如果是在指定父容器中显示获取的是在父容器中的位置
    if (mContainerForGuide == null) {
        val location = IntArray(2)
        targetView.getLocationOnScreen(location)
        mTargetViewLeft = location[0]
        mTargetViewTop = location[1]
    } else {
        mTargetViewLeft = targetView.left
        mTargetViewTop = targetView.top
    }
}


/**
 * 显示引导
 */
fun show(showDelayTime: Long = 500L, dismissDelayTime: Long = -1) {

    if (nowShowingTargetId == targetView.id) {
        return
    }
    nowShowingTargetId = targetView.id
    if (dismissDelayTime != -1L) {
        this.mDismissDelayTime = dismissDelayTime
    }
    mGuideIvAnimator?.let {
        it.setTarget(mGuideIv)
        it.start()
    }
    mHandler.sendEmptyMessageDelayed(SHOW_GUIDE, showDelayTime)
}

/**
 * 显示新手引导
 */
private fun showGuide() {
    //提示显示的布局
    val rootViewShowGuide: ViewGroup? = mContainerForGuide
            ?: if (mContext is Activity) {
                mContext.window.decorView as ViewGroup
            } else null
    rootViewShowGuide?.let {
        // 判断是否已经显示过
        mHasShow = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE).getBoolean("$SP_NAME_ID_PREFIX$mGuideInInSp", false)
        //已经显示过了就点击消失
        if (!mIsDebug && mHasShow) {
            visibility = View.GONE
            try {
                it.removeView(this@GuideTipView)
            } catch (e: Exception) {
            }
            return
        }

        // 点击后存储显示与否的状态
        val sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
        sp.edit().putBoolean("$SP_NAME_ID_PREFIX$mGuideInInSp", true).apply()

        it.removeView(this@GuideTipView)
        // QTIP: 2018/5/29  LayoutParams MATCH_PARENT
        val params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        it.addView(this@GuideTipView, params)
        this@GuideTipView.visibility = View.VISIBLE
        val fadeInAnimation = AnimationUtils.loadAnimation(context, R.anim.fade_in)
        this@GuideTipView.startAnimation(fadeInAnimation)

        targetView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                if (isMeasured) return
                if (targetView.height > 0 && targetView.width > 0) {
                    isMeasured = true
                }
                getTargetViewLocation()
                val centerX: Int = mTargetViewLeft + targetView.width / 2
                val centerY: Int = mTargetViewTop + targetView.height / 2
                mHighlightAreaRadius = targetView.width / 2
                mHighlightAreaCenterPoint.x = centerX
                mHighlightAreaCenterPoint.y = centerY
                addGuideIv()
                addGuideTv()
                invalidate()
                targetView.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
    }
}


/**
 * 移除新手引导
 */
private fun dismiss(clickDismissType: GuideTipView.ClickDismissType) {
    mHandler.removeCallbacksAndMessages(null)
    mDismissListener?.onDismiss(clickDismissType)
    try {
        (parent as ViewGroup).removeView(this)
    } catch (e: Exception) {

    }
    nowShowingTargetId = -1
}


/**
 * 监听触摸
 */
override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {

            val downX = event.x
            val downY = event.y
            //是够在可点击区
            var inClickArea = true

            when (mClickClickDismissType) {
            //点击所有区域消失
                ClickDismissType.DISMISS_IN_ALL_AREA -> {
                    inClickArea = true
                }
            //店家高亮区消失
                ClickDismissType.DISMISS_IN_HIGH_LIGHT_AREA -> {
                    //判断点击区域是否在高亮区域
                    val inClickAreaWidth = downX < mHighlightAreaCenterPoint.x + targetView.width / 2 && downX > mHighlightAreaCenterPoint.x - targetView.width / 2
                    val inClickAreaHeight = downY < mHighlightAreaCenterPoint.y + targetView.height / 2 && downY > mHighlightAreaCenterPoint.y - targetView.height / 2
                    inClickArea = inClickAreaWidth && inClickAreaHeight

                }
            //点击图片消失
                ClickDismissType.DISMISS_IN_GUIDE_IV -> {
                    //判断是否点击引导图片
                    val inClickAreaWidth = downX < mGuideIv.right && downX > mGuideIv.left
                    val inClickAreaHeight = downY < mGuideIv.bottom && downY > mGuideIv.top
                    inClickArea = inClickAreaWidth && inClickAreaHeight

                }
            //点击文字消失
                ClickDismissType.DISMISS_IN_GUIDE_TV -> {
                    //判断是否点击引导文字
                    val inClickAreaWidth = downX < mGuideTv.right && downX > mGuideTv.left
                    val inClickAreaHeight = downY < mGuideTv.bottom && downY > mGuideTv.top
                    inClickArea = inClickAreaWidth && inClickAreaHeight
                }
            //DISMISS_FORBID_CLICK
                else -> {
                    inClickArea = false
                }
            }

            if (inClickArea) {
                dismiss(mClickClickDismissType)
            }

        }
    }
    return true
}


/**
 * 绘制
 */
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    if (width <= 0 || height <= 0) {
        return
    }
    //1 绘制半透明背景
    // 设置透明背景
    if (mBgBitmap == null) {
        mBgBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        mTempCanvas = Canvas(mBgBitmap)
    }
    mBgPaint.color = Color.parseColor("#000000")
    // mBgPaint.color = bgColor
    mBgPaint.alpha = mBgAlpha
    mTempCanvas!!.drawRect(0f, 0f, width.toFloat(), height.toFloat(), mBgPaint)


    //2.绘制高亮区 // QTIP: 2018/5/28 从temp 上去掉了高亮
    mBgPaint.reset()
    val transparentPaint = Paint()
    transparentPaint.color = ContextCompat.getColor(mContext, R.color.transparent)
    transparentPaint.isAntiAlias = true
    transparentPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)

    //高亮目标控件的中心位置
    val centerX = mHighlightAreaCenterPoint.x
    val centerY = mHighlightAreaCenterPoint.y

    //2.1显示圆形高亮
    if (mShowCircleGuide) {
        mTempCanvas!!.drawCircle(centerX.toFloat(), centerY.toFloat(), maxOf(targetView.height / 2, targetView.width / 2).toFloat() + 10, transparentPaint)
        canvas.drawBitmap(mBgBitmap, 0f, 0f, mBgPaint)
    }
    //2.2显示矩形高亮
    else {
        //当控件尺寸小于2*150时 调整高亮区 避免高亮区太小
        if (mHighlightAreaRadius < 150) {
            val rect = RectF((centerX - mHighlightAreaRadius).toFloat(), (centerY - targetView.height / 2).toFloat(), (centerX + mHighlightAreaRadius).toFloat(), (centerY + targetView.height / 2).toFloat())
            mTempCanvas!!.drawRoundRect(rect, 15.0f, 15.0f, transparentPaint)
        } else {
            val rect = RectF((centerX - mHighlightAreaRadius + 8).toFloat(), (centerY - targetView.height / 2 + 5).toFloat(), (centerX + mHighlightAreaRadius - 8).toFloat(), (centerY + targetView.height / 2 - 5).toFloat())
            mTempCanvas!!.drawRoundRect(rect, 15.0f, 15.0f, transparentPaint)

        }
        canvas.drawBitmap(mBgBitmap, 0f, 0f, mBgPaint)
    }

    //绘制高亮区域外边的点画线
    if (mShowDashPath) {
        //设置点划线画笔
        val p = Paint(Paint.ANTI_ALIAS_FLAG)
        p.style = Paint.Style.STROKE
        p.color = Color.WHITE
        p.strokeWidth = mDashStrokeWidth
        val effects = DashPathEffect(floatArrayOf(mDashWidth, mDashGap), 1f)
        p.pathEffect = effects

        if (mShowCircleGuide) {
            canvas.drawCircle(centerX.toFloat(), centerY.toFloat(), maxOf(targetView.height / 2, targetView.width / 2).toFloat() + 10 + mDashOffset, p)
        } else {
            //当控件小于2*150时
            if (mHighlightAreaRadius < 150) {
                val rect2 = RectF((centerX - mHighlightAreaRadius - 4).toFloat(), (centerY - targetView.height / 2 - 4).toFloat(), (centerX + mHighlightAreaRadius + 4).toFloat(), (centerY + targetView.height / 2 + 4).toFloat())
                canvas.drawRoundRect(rect2, 15.0f, 15.0f, p) //画空心圆角矩形
            }
            //当控件大于2*150时
            else {
                val rect2 = RectF((centerX - mHighlightAreaRadius + 3).toFloat(), (centerY - targetView.height / 2).toFloat(), (centerX + mHighlightAreaRadius - 3).toFloat(), (centerY + targetView.height / 2).toFloat())
                canvas.drawRoundRect(rect2, 15.0f, 15.0f, p)
            }
        }
    }
}

}

2.2.1.半透明蒙版区域

        //1.半透明蒙版区域rootViewShowGuide 获取 ,就是activity的window 的根布局
        val rootViewShowGuide: ViewGroup? = mContainerForGuide
            ?: if (mContext is Activity) {
                mContext.window.decorView as ViewGroup
            } else null

2.2.2.设置高亮控件 , 监听布局完成时 高亮控件的位置(在屏幕上的位置 或者 相对于外层控件的位置i) getTargetViewLocation()

  /**
 * 获取需要高亮的控件相对于屏幕或者外层容器的left和top
 */
private fun getTargetViewLocation() {
    // QTIP: 2018/5/29  如果是在activity中 获取的是在屏幕上的位置 ,如果是在指定父容器中显示获取的是在父容器中的位置
    if (mContainerForGuide == null) {
        val location = IntArray(2)
        targetView.getLocationOnScreen(location)
        mTargetViewLeft = location[0]
        mTargetViewTop = location[1]
    } else {
        mTargetViewLeft = targetView.left
        mTargetViewTop = targetView.top
    }
}
2.2.3.绘制透明蒙版,高亮区,指引图片,指引文字
   /**
   * 绘制
   */
  @SuppressLint("DrawAllocation")
  override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    if (width <= 0 || height <= 0) {
        return
    }
    //1 绘制半透明背景
    // 设置透明背景
    if (mBgBitmap == null) {
        mBgBitmap = Bitmap.createBitmap(width, height,         
    Bitmap.Config.ARGB_8888)
        mTempCanvas = Canvas(mBgBitmap)
    }
    mBgPaint.color = Color.parseColor("#000000")
    // mBgPaint.color = bgColor
    mBgPaint.alpha = mBgAlpha
    mTempCanvas!!.drawRect(0f, 0f, width.toFloat(), height.toFloat(), mBgPaint)


    //2.绘制高亮区 // QTIP: 2018/5/28 从temp 上去掉了高亮
    mBgPaint.reset()
    val transparentPaint = Paint()
    transparentPaint.color = ContextCompat.getColor(mContext, R.color.transparent)
    transparentPaint.isAntiAlias = true
    transparentPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)

    //高亮目标控件的中心位置
    val centerX = mHighlightAreaCenterPoint.x
    val centerY = mHighlightAreaCenterPoint.y

    //2.1显示圆形高亮
    if (mShowCircleGuide) {
        mTempCanvas!!.drawCircle(centerX.toFloat(), centerY.toFloat(), maxOf(targetView.height / 2, targetView.width / 2).toFloat() + 10, transparentPaint)
        canvas.drawBitmap(mBgBitmap, 0f, 0f, mBgPaint)
    }
    //2.2显示矩形高亮
    else {
        //当控件尺寸小于2*150时 调整高亮区 避免高亮区太小
        if (mHighlightAreaRadius < 150) {
            val rect = RectF((centerX - mHighlightAreaRadius).toFloat(), (centerY - targetView.height / 2).toFloat(), (centerX + mHighlightAreaRadius).toFloat(), (centerY + targetView.height / 2).toFloat())
            mTempCanvas!!.drawRoundRect(rect, 15.0f, 15.0f, transparentPaint)
        } else {
            val rect = RectF((centerX - mHighlightAreaRadius + 8).toFloat(), (centerY - targetView.height / 2 + 5).toFloat(), (centerX + mHighlightAreaRadius - 8).toFloat(), (centerY + targetView.height / 2 - 5).toFloat())
            mTempCanvas!!.drawRoundRect(rect, 15.0f, 15.0f, transparentPaint)

        }
        canvas.drawBitmap(mBgBitmap, 0f, 0f, mBgPaint)
    }

    //绘制高亮区域外边的点画线
    if (mShowDashPath) {
        //设置点划线画笔
        val p = Paint(Paint.ANTI_ALIAS_FLAG)
        p.style = Paint.Style.STROKE
        p.color = Color.WHITE
        p.strokeWidth = mDashStrokeWidth
        val effects = DashPathEffect(floatArrayOf(mDashWidth, mDashGap), 1f)
        p.pathEffect = effects

        if (mShowCircleGuide) {
            canvas.drawCircle(centerX.toFloat(), centerY.toFloat(), maxOf(targetView.height / 2, targetView.width / 2).toFloat() + 10 + mDashOffset, p)
        } else {
            //当控件小于2*150时
            if (mHighlightAreaRadius < 150) {
                val rect2 = RectF((centerX - mHighlightAreaRadius - 4).toFloat(), (centerY - targetView.height / 2 - 4).toFloat(), (centerX + mHighlightAreaRadius + 4).toFloat(), (centerY + targetView.height / 2 + 4).toFloat())
                canvas.drawRoundRect(rect2, 15.0f, 15.0f, p) //画空心圆角矩形
            }
            //当控件大于2*150时
            else {
                val rect2 = RectF((centerX - mHighlightAreaRadius + 3).toFloat(), (centerY - targetView.height / 2).toFloat(), (centerX + mHighlightAreaRadius - 3).toFloat(), (centerY + targetView.height / 2).toFloat())
                canvas.drawRoundRect(rect2, 15.0f, 15.0f, p)
            }
        }
    }
}

3.使用方式

3.1最简单使用

  //设置上下文 ,高亮控件
 GuideTipViewBuild(this, iv_share0)
  //添加引导图片 并设置位置
 .setGuideIvResAndLocation(R.drawable.img_guide_activity_orginfo_fresh, -ScreenUtil.dp2px(this, 220), ScreenUtil.dp2px(this, 80))
                    .build().show()

3.2 其他设置

     // customer guide textView
     val textView = TextView(this)
     textView.setTextColor(Color.WHITE)
     textView.textSize = 18f
     textView.setBackgroundResource(R.drawable.bg_tv)

            //set animation in guideIV 
            val loadAnimator = AnimatorInflater.loadAnimator(this, R.animator.guide_up_down)
            GuideTipViewBuild(this, iv_share5)
                    //set image res
                    .setGuideIvResAndLocation(R.drawable.img_click, ScreenUtil.dp2px(this, 18), ScreenUtil.dp2px(this, 40))
                    //set des text
                    .setGuideIvMsgAndLocation("click here dismiss.. ", -ScreenUtil.dp2px(this, 140), ScreenUtil.dp2px(this, 90), textView)
                    //click textView  to dismiss
                    .setClickDismissType(GuideTipView.ClickDismissType.DISMISS_IN_GUIDE_TV)
                    //set a container to show guide
                    .setContainerForGuide(rl_main)
                    //open debug
                    .openDebug(true)
                    //add animation in guide iv
                    .setGuideIvAnimator(loadAnimator)
                    //set highlight area type
                    .setTypeCircle(false)
                    //use dash line
                    .setShowDashPath(false)
                    //
                    //set translucent background alpha value
                    .setBgAlpha(200)
                    .setDashLine(3f,20f,10f,0f)
                    //dismiss listener
                    .setOnDismissListener(object : DismissListener {
                        override fun onDismiss(clickDismissType: GuideTipView.ClickDismissType) {
                            Toast.makeText(this@MainActivity, "clickDismissType = $clickDismissType", Toast.LENGTH_SHORT).show()
                        }
                    })
                    //forbid auto dismiss
                    .setAutoDismiss(false)
                    //show
                    .build().show()

20190102更新

使用自定义布局 use customLayout

截图
QQ20190102-174709.gif
private fun showCustomLayoutGuide(targetView: View) {

val alphaAnimation = AlphaAnimation(.5F, 1F)
alphaAnimation.duration = 600
alphaAnimation.repeatMode = Animation.REVERSE
alphaAnimation.repeatCount = Animation.INFINITE
alphaAnimation.interpolator = AccelerateInterpolator()
//点击整个卡片,查看详细使用情况
val guideCustomLayoutForClickItem = LayoutInflater.from(this).inflate(R.layout.guide_usage_click, null, false)
val guideTipView = GuideTipViewBuild(this, targetView)
        .setClickDismissType(GuideTipView.ClickDismissType.AUTO_DISMISS)
        .setGuideCustomLayoutAndLocation(ScreenUtil.dp2px(this, -5), ScreenUtil.dp2px(this, 45), guideCustomLayoutForClickItem)
        .openDebug(true)
        .setTypeCircle(false)
        .setShowDashPath(false)
        .setBgAlpha(100)
        .setAutoRadius(true)
        //dismiss listener
        .setOnDismissListener(object : DismissListener {
            override fun onDismiss(clickDismissType: GuideTipView.ClickDismissType) {

            }
        })
        .setAutoDismiss(false)
        //show
        .build()

guideTipView.show()
guideCustomLayoutForClickItem.iv_dot.startAnimation(alphaAnimation)
guideCustomLayoutForClickItem.btn_got_it.setOnClickListener {
    guideTipView.dismiss()
}
}

Android studio 添加依赖使用

Step 1.Add it in your root build.gradle at the end of repositories:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
  }

Step 2. Add the dependency

dependencies {
      implementation 'com.github.qingyc:GuideTipView:0.3'
}

项目地址:https://github.com/qingyc/GuideTipView.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容