Giter VIP home page Giter VIP logo

highlight's Introduction

Highlight

Download

一个用于app指向性功能高亮的库。

有任何意见,欢迎提issue。新建dev分支,欢迎pull request。

效果图

竖屏:

横屏:

引入

下载代码,然后:

dependencies {
       compile project(':highlight')
}

或者

    compile 'com.isanwenyu.highlight:highlight:1.8.0'

再或者

<dependency>
  <groupId>com.isanwenyu.highlight</groupId>
  <artifactId>highlight</artifactId>
  <version>1.8.0</version>
  <type>pom</type>
</dependency>

用法

最新用法详情:https://isanwenyu.github.io/2016/11/23/HighLight-latest-usage/

Next Mode 下一步模式

Enable next mode and invoke show() method then invoke next() method in HighLight to display tip view in order till remove itself 调用enableNext()开启next模式并显示,然后调用next()方法显示下一个提示布局 直到删除自己

1. 开启next模式并显示

    /**
     * 显示 next模式 我知道了提示高亮布局
     * @param view id为R.id.iv_known的控件
     * @author [email protected]
     */
    public  void showNextKnownTipView(View view)
    {
        mHightLight = new HighLight(MainActivity.this)//
                .autoRemove(false)//设置背景点击高亮布局自动移除为false 默认为true
//                .intercept(false)//设置拦截属性为false 高亮布局不影响后面布局的滑动效果
                .intercept(true)//拦截属性默认为true 使下方ClickCallback生效
                .enableNext()//开启next模式并通过show方法显示 然后通过调用next()方法切换到下一个提示布局,直到移除自身
//                .setClickCallback(new HighLight.OnClickCallback() {
//                    @Override
//                    public void onClick() {
//                        Toast.makeText(MainActivity.this, "clicked and remove HightLight view by yourself", Toast.LENGTH_SHORT).show();
//                        remove(null);
//                    }
//                })
                .anchor(findViewById(R.id.id_container))//如果是Activity上增加引导层,不需要设置anchor
                .addHighLight(R.id.btn_rightLight,R.layout.info_known,new OnLeftPosCallback(45),new RectLightShape(0,0,15,0,0))//矩形去除圆角
                .addHighLight(R.id.btn_light,R.layout.info_known,new OnRightPosCallback(5),new BaseLightShape(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,5,getResources().getDisplayMetrics()), TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,5,getResources().getDisplayMetrics()),0) {
                    @Override
                    protected void resetRectF4Shape(RectF viewPosInfoRectF, float dx, float dy) {
                        //缩小高亮控件范围
                        viewPosInfoRectF.inset(dx,dy);
                    }

                    @Override
                    protected void drawShape(Bitmap bitmap, HighLight.ViewPosInfo viewPosInfo) {
                        //custom your hight light shape 自定义高亮形状
                        Canvas canvas = new Canvas(bitmap);
                        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
                        paint.setDither(true);
                        paint.setAntiAlias(true);
                        //blurRadius必须大于0
                        if(blurRadius>0){
                            paint.setMaskFilter(new BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.SOLID));
                        }
                        RectF rectF = viewPosInfo.rectF;
                        canvas.drawOval(rectF, paint);
                    }
                })
                .addHighLight(R.id.btn_bottomLight,R.layout.info_known,new OnTopPosCallback(),new CircleLightShape())
                .addHighLight(view,R.layout.info_known,new OnBottomPosCallback(10),new OvalLightShape(5,5,20))
                .setOnRemoveCallback(new HighLightInterface.OnRemoveCallback() {//监听移除回调 
                    @Override
                    public void onRemove() {
                        Toast.makeText(MainActivity.this, "The HightLight view has been removed", Toast.LENGTH_SHORT).show();

                    }
                })
                .setOnShowCallback(new HighLightInterface.OnShowCallback() {//监听显示回调
                    @Override
                    public void onShow(HightLightView hightLightView) {
                        Toast.makeText(MainActivity.this, "The HightLight view has been shown", Toast.LENGTH_SHORT).show();
                    }
                }).setOnNextCallback(new HighLightInterface.OnNextCallback() {
                    @Override
                    public void onNext(HightLightView hightLightView, View targetView, View tipView) {
                        // targetView 目标按钮 tipView添加的提示布局 可以直接找到'我知道了'按钮添加监听事件等处理
                        Toast.makeText(MainActivity.this, "The HightLight show next TipView,targetViewID:"+(targetView==null?null:targetView.getId())+",tipViewID:"+(tipView==null?null:tipView.getId()), Toast.LENGTH_SHORT).show();
                    }
                });
        mHightLight.show();
    }

2. 调用next()方法依次显示之前添加到提示布局 最后自动移除

/**
     * 响应所有R.id.iv_known的控件的点击事件
     * <p>
     *  移除高亮布局
     * </p>
     *
     * @param view
     */
    public void clickKnown(View view)
    {
        if(mHightLight.isShowing() && mHightLight.isNext())//如果开启next模式
        {
            mHightLight.next();
        }else
        {
            remove(null);
        }
    }

3. 下一步回调监听

    /**
     * 下一个回调监听 只有Next模式下生效
     */
    public static interface OnNextCallback {
        /**
         * 监听下一步动作
         *
         * @param hightLightView 高亮布局控件
         * @param targetView     高亮目标控件
         * @param tipView        高亮提示控件
         */
        void onNext(HightLightView hightLightView, View targetView, View tipView);
    }

4. mAnchor根布局完成回调监听

针对下方问题4的优化方案 在Activity或Fragment onCreated方法中构造HighLight 通过mAnchor.getViewTreeObserver().addOnGlobalLayoutListener(this)实现

    /**
     * 当界面布局完成显示next模式提示布局
     * 显示方法必须在onLayouted中调用
     * 适用于Activity及Fragment中使用
     * 可以直接在onCreated方法中调用
     * @author [email protected]
     */
    public  void showNextTipViewOnCreated(){
        mHightLight = new HighLight(MainActivity.this)//
                .anchor(findViewById(R.id.id_container))//如果是Activity上增加引导层,不需要设置anchor
                .autoRemove(false)
                .enableNext()
                .setOnLayoutCallback(new HighLightInterface.OnLayoutCallback() {
                    @Override
                    public void onLayouted() {
                        //mAnchor界面布局完成添加tipview
                        mHightLight.addHighLight(R.id.btn_rightLight,R.layout.info_gravity_left_down,new OnLeftPosCallback(45),new RectLightShape())
                                .addHighLight(R.id.btn_light,R.layout.info_gravity_left_down,new OnRightPosCallback(5),new CircleLightShape())
                                .addHighLight(R.id.btn_bottomLight,R.layout.info_gravity_left_down,new OnTopPosCallback(),new CircleLightShape());
                        //然后显示高亮布局
                        mHightLight.show();
                    }
                })
                .setClickCallback(new HighLight.OnClickCallback() {
                    @Override
                    public void onClick() {
                        Toast.makeText(MainActivity.this, "clicked and show next tip view by yourself", Toast.LENGTH_SHORT).show();
                        mHightLight.next();
                    }
                });
    }
    ```
### Nomarl Mode 普通模式

对于上面效果图中的一个需要高亮的View,需要通过下面的代码

/**
 * 显示我知道了提示高亮布局
 * @param view id为R.id.iv_known的控件
 * @author [email protected]
 */
public  void showKnownTipView(View view)
{
    mHightLight = new HighLight(MainActivity.this)//
            .autoRemove(false)//设置背景点击高亮布局自动移除为false 默认为true
            .intercept(false)//设置拦截属性为false 高亮布局不影响后面布局的滑动效果 而且使下方点击回调失效
            .setClickCallback(new HighLight.OnClickCallback() {
                @Override
                public void onClick() {
                    Toast.makeText(MainActivity.this, "clicked and remove HightLight view by yourself", Toast.LENGTH_SHORT).show();
                    remove(null);
                }
            })
            .anchor(findViewById(R.id.id_container))//如果是Activity上增加引导层,不需要设置anchor
            .addHighLight(R.id.btn_rightLight,R.layout.info_known,new OnLeftPosCallback(45),new RectLightShape())
            .addHighLight(R.id.btn_light,R.layout.info_known,new OnRightPosCallback(5),new CircleLightShape(0,0,0))
            .addHighLight(R.id.btn_bottomLight,R.layout.info_known,new OnTopPosCallback(),new CircleLightShape())
            .addHighLight(view,R.layout.info_known,new OnBottomPosCallback(10),new OvalLightShape(5,5,20));
    mHightLight.show();

// //added by [email protected] 设置监听器只有最后一个添加到HightLightView的knownView响应了事件 // //优化在布局中声明onClick方法 {@link #clickKnown(view)}响应所有R.id.iv_known的控件的点击事件 // View decorLayout = mHightLight.getHightLightView(); // ImageView knownView = (ImageView) decorLayout.findViewById(R.id.iv_known); // knownView.setOnClickListener(new View.OnClickListener() // { // @Override // public void onClick(View view) { // remove(null); // } // }); }


anchor()指你需要在哪个view上加一层透明的蒙版,如果不设置,默认为android.R.id.content。也就是说,该库支持局部范围内去高亮某些View.

addHighLight包含3个参数:

* 参数1:需要高亮view的id,这个没什么说的
* 参数2:你的tip布局的layoutId,也就是箭头和文字,你自己编写个布局,参考demo即可。
* 参数3:是个接口,接口包含一系列的位置信息,如下
	
	```xml
	/**
	 * @param rightMargin 高亮view在anchor中的右边距
	 * @param bottomMargin 高亮view在anchor中的下边距
	 * @param rectF 高亮view的l,t,r,b,w,h都有
	 * @param marginInfo 设置你的布局的位置,一般设置l,t或者r,b
	 */
	```
	哈,提供了一堆的位置信息,但是你要做的,只是去设置leftMargin和topMargin;或者rightMargin和bottomMargin。
	
	目前看起来,我觉得位置信息够了,当然如果你有想法欢迎提出。
	
	哈,是不是参数比较多,看着烦,如果你图省事,可以提供一个枚举,提供4个或者8个默认的位置,这个事呢,dota1群`@李志云`已经完成~认识的话可以去找他。
* 参数4:高亮形状  抽象类BaseLightShape(dx,dy,blurRadius)

	```xml
    /**
     * @param dx 水平方向偏移
     * @param dy 垂直方向偏移
     * @param blurRadius 模糊半径 默认15px 0不模糊
     */

两个抽象方法:

 /**
  * reset RectF for Shape by dx and dy. 根据dx,dy重置viewPosInfoRectF大小
  * @param viewPosInfoRectF
  * @param dx
  * @param dy
  */
 protected abstract void resetRectF4Shape(RectF viewPosInfoRectF, float dx, float dy);

 /**
  * draw shape into bitmap. 绘制高亮形状到传递过来的图片画布上
  * @param bitmap
  * @param viewPosInfo
  * @see zhy.com.highlight.view.HightLightView#addViewForEveryTip(HighLight.ViewPosInfo)
  * @see HightLightView#buildMask()
  */
 protected abstract void drawShape(Bitmap bitmap, HighLight.ViewPosInfo viewPosInfo);

BaseLightShape的实现类:RectLightShape(矩形)、CircleLightShape(圆形)、OvalLightShape(椭圆),具体实现请查看代码

Changelog 更改历史

操作 时间 版本 详情
A 2016/9/26 v1.2.0 添加点击高亮布局背景是否自动移除标志autoRemove
A 2016/10/5 v1.3.0 添加Next模式-按顺序依次显示添加的提示布局
&&给HighLightView添加唯一id防止重复添加
F 2016/10/8 v1.3.1 修复Next模式-HightLightView中校验mPostion问题
I 2016/10/27 v1.4.0 扩展高亮形状LightShape支持开发者自定义配置
A 2016/11/01 v1.5.0 HighLight添加接口约束
A 2016/11/01 v1.5.1 显示与隐藏监听及isShowing属性
I 2016/11/02 v1.5.2 优化HightLightView中提示布局添加与显示 兼容AutoFrameLayout
A 2016/11/16 v1.6.0 添加椭圆高亮形状&修复显示问题
A 2016/11/25 v1.7.0 添加下一个监听 回调返回当前的目标控件及提示控件
R 2016/12/02 v1.7.1 显示及移除回调不依赖intercept属性
F 2016/12/07 v1.7.2 修复#21 在ViewPager中用户获取目标布局位置信息错误
I 2016/12/14 v1.7.3 RectLightShape构造函数添加x-radius和y-radius圆角半径属性
F 2016/12/21 v1.7.4 修复HightLightView绘制时图片回收异常
F 2017/1/3 v1.7.5 修复ViewUtils#getLocationInView中获取位置空异常
A 2017/5/25 v1.8.0 添加setOnLayoutCallback回调 支持onCreated中初始化,界面布局完成显示

Question 问题

  1. 添加的提示布局怎么在屏幕中水平居中显示(垂直居中类似)

    提示布局根布局android:layout_width="match_parent" android:gravity="center_horizontal"

    自定义定位参数rightMargin=0

  2. 怎么针对同一个高亮控件添加多个提示布局

    addHighLight 多次添加 第一个参数使用同一个控件id即可

  3. 高亮布局显示后 底层布局有变化 怎么更新高亮布局

    mHighLight.getHightLightView().requestLayout() 掉用后高亮布局会重新布局及绘制

  4. 页面加载完成,自动显示,应该放在哪里调用?

     ```
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
         //界面初始化后显示高亮布局
         mHightLight.show();
     }
     
     或者:
     
     getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
             @Override
             public void onGlobalLayout() {
                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                     getWindow().getDecorView().getViewTreeObserver()
                             .removeOnGlobalLayoutListener(this);
                 }
                 
             	//界面初始化后显示高亮布局
               	initHightLight();
               	mHightLight.show();
             }
    
         });	 
     ```
    
  5. 如果使用viewpager非第一页高亮布局 有可能定位到屏幕外

    v1.7.2版本已修复 具体方案参考#21

    感谢 @liyanxi 提供的方案 会更新到 README.md

    //自定义高亮形状
    final HighLight.LightShape mLightShape = new BaseLightShape() {
        @Override
        protected void resetRectF4Shape(RectF viewPosInfoRectF, float dx, float dy) {
           //重置viewPosInfoRectF 模掉屏幕宽度 得到真实的left
            viewPosInfoRectF.offsetTo(viewPosInfoRectF.left % DeviceUtil.getScreenDispaly(getActivity())[0], viewPosInfoRectF.top);
        }
      ......
    

致谢

  • 感谢android day day dota1群,苏苏,提供的图片资源。
  • thx for 李志云@dota1的测试、修改、提议。
  • thx for @zj593743143`的测试和建议

highlight's People

Contributors

allen026 avatar hongyangandroid avatar isanwenyu avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.