美文网首页
自定义波纹特效的TabLayout

自定义波纹特效的TabLayout

作者: 0a2868f025e0 | 来源:发表于2017-05-17 19:36 被阅读317次

    1 引言

    1.1 背景。

    在以往的开发项目VR助手中有一个交互,就是利用波纹的指示器来实现Tab的切换。

    2 交互方式

    Paste_Image.png

    1)排行向全部过度时,有一个波纹走向:从初始点往高点移动。

    2)全部向排行过度,由高点向低点移动,到排行这个按钮时时为最高峰。

    从上图可以看到,当排行向全部的Tab划过时,需要有一个波纹的走向,以现有的Metrial Design提供的TabLayout是没法实现该效果的。

    3 问题

    1.TabLayout中的指示器是如何做到过渡的效果?

    2.根据波纹的走向要使用什么数学模型?

    4 解决思路

    根据第一个问题,利用手机上的开发者模式中,打开显示Layout布局方式,从中看到,TabView是多个,而指示器应该是一个。为了验证整个问题,决定从源码上分析整个TabLayout的实现方式,来验证自己的判断,并将原生的部分源码COPY出来,改造一个新的TabLayout。

    Paste_Image.png

    从上图可以看出,TabLayout分为几个模块:

    1.SlidingTabStrip:TabView的容器类,用于承载TabView以及TabView滑动过程中,绘制的指示器。

    2.TabView:一个Tab的View控件,支持自定义Tab,设置Tab的icon,text等,并且可以做特殊的特效控件。

    3.Tab:TabView的参数类。

    按照项目协作的方式理解就是:

    1.RippleTabLayout就是一个领导,领导所要做的事有:

    1)指派员工处理某件事(addTab,removeTab,selectTab,传递外部人员给与的参数)。

    2)跟外部人员对接本项目(ViewPager,暴露监听回调。

    2.SlidingTabStrip:程序员

    1)努力实现领导下达的任务(onDraw):根据领导下发的外部对接参数,来绘制指示器。

    2)由于SlidingTabStrip继承了LinearLayout,因此,也具备了add、remove、select的操作。

    这时候,外部人员(ViewPager)传入参数,告知领导RippleTabLayout(positionOffset),程序员为了保证动态的效果,加入了动画(ValueAnimator)。当每接收到一次参数时,重新绘制一次指示器。

    经验证发现,真正能实现波纹动效方面的是程序员(SlidingTabStrip)而非领导,那么我们要如何才能实现这种动态效果呢。当然还是让程序员来实现最好。

    于是,我们在SlidingTabStrip中引入的高斯函数,为其绘制出动态效果。

    Paste_Image.png

    高斯函数,广泛用于计算机图形学中,在起初进行开发前,数学模型采用的是其它线性模型,经过对交互的揣摩,后面选择了高斯滤波。在这里感谢谢炜斌、曾承航和已离职的陈鹏同学,大家针对高斯函数的方式进行了优化,并给予了算法。

    5 程序改造

    既然有了思路,就要着手解决这个交互。

    1)提取原TbaLayout中的AnimationUtils、TabView、ViewUtils、ViewUtilsLopLipop进行部分代码改造。

    2)重写SlidingTabStrip,并在SlidingTabStrip的onDraw方法中加入优化后的高斯滤波算法。核心代码如下:

    //绘制高斯函数,根据线性大小计算值
    private double getGussion(double lineX, double GussionHight, double GussionMu)
    {  
        double result = 0;   
        result = -GussionHight * ( Math.pow(Math.E, -(lineX - GussionMu) * (lineX - GussionMu) / (2 *   dpToPx(8) * dpToPx(8)))) + getHeight() - 3 ;   
        return result;
    }
    

    3)每一次的移动,手没放开,都在不断的onDraw

    protected void onDraw(Canvas canvas) 
    { 
        super.onDraw(canvas);    
        Log.i("draw", "mGussionHeightOffset start= " );
        Path path = new Path();
        path.moveTo(0, getHeight());
        for (int i = 0; i < getWidth(); i++){
            double y1 = getGussion(i, dpToPx(15) * Math.abs(mGussionHeightOffset - 0.5),   (mIndicatorRight + mIndicatorLeft) / 2);        
            double y2 = getGussion(i + 1, dpToPx(15) * Math.abs(mGussionHeightOffset - 0.5), (mIndicatorRight + mIndicatorLeft) / 2);      
            path.quadTo(i, (float) y1, i + 1 , (float)y2);  
        }   
        canvas.drawPath(path, mSelectedIndicatorPaint);  
        Log.i("draw", "mGussionHeightOffset end= " );  
    }
    

    6 总结

    代码能力编写再强,也要学会如何用数学模型思维来解决问题。

    以下是该控件实现的相关源码:
    https://github.com/RichsJeson/Android-UI/tree/master/AndroidUI/RippleLayout

    相关文章

      网友评论

          本文标题:自定义波纹特效的TabLayout

          本文链接:https://www.haomeiwen.com/subject/xffjxxtx.html