神奇的水滴效果导航栏-BezierIndicator

本文介绍了如何实现一个神奇的水滴效果导航栏,使用贝塞尔曲线绘制,包括分析贝塞尔小球的绘制过程、控件宽高的计算、子控件位置的确定、点击切换动画的实现、与ViewPager的联动等步骤,提供了完整的代码实现。
摘要由CSDN通过智能技术生成

晨鸣的博客–神奇的水滴效果导航栏-BezierIndicator

很早之前就看见过这样一个特效

心怡很久,却一直恐于自定义View这座大山。最近在突击自定义View的技能,学习贝塞尔曲线的绘制,前面搞了个很简单的MagicButton,甚是兴奋�� 所以斗胆来试试看实现这个特效。

分析

找了半天终于找到当初看见的这个特效的原博客 –三次贝塞尔曲线练习之弹性的圆

另外在评论中发现竟然有人已经实现了这个自定义View了–自定义View之炫酷的水滴ViewPageIndicator,效果很不错,借鉴之��

关于最核心的贝塞尔小球动效的绘制,博主进行了很详细的解析及描述,并且提供了一个demo,万分感谢��

这里简单回顾一下这个小球的绘制过程:

为了控制小球的不同形态,我们这里使用三阶贝塞尔曲线cubicTo来绘制小球。

贝塞尔小球

而小球一共可以分成5个状态来绘制

状态1

状态1

状态2

状态2

状态3

状态3

状态4

状态4

状态5

状态5

绘制

计算控件宽高

作为一个导航控件,我暂时不考虑宽度设置为warp_content的状态,设置wrap_content一律计算为屏幕的最大宽高.

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        WindowManager wm = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);
        /**
         * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
         */
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY) {
            width = sizeWidth;
        } else {
            width = wm.getDefaultDisplay().getWidth();
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = sizeHeight;
        } else {
            height = wm.getDefaultDisplay().getHeight();
        }

        if (getChildCount() != 0) {
            childSideLength = (width - getPaddingRight() - getPaddingLeft()) / getChildCount() > height - getPaddingBottom() - getPaddingTop() ? height - getPaddingBottom() - getPaddingTop() : (width - getPaddingLeft() - getPaddingRight()) / getChildCount();
//        //计算出所有的ChildView的宽和高
//            measureChildren(widthMeasureSpec, heightMeasureSpec);
            bezierCircular = new BezierCircular(childSideLength / 2);
        }

        setMeasuredDimension(width, height);
    }

计算子控件的位置

为了方便管理,子View的大小统一计算为一个正方形区域,设置一个子View的padding值childPadding,可以通过childPadding值控制我们添加的子view呈现出的大小,也就是效果图中小图标在白色圆环中的大小。

 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int childCount = getChildCount();
        if (childCount == 0) {
            return;
        }
        //相邻两个子View中心点的间距
        float childDis = (width - getPaddingLeft() - getPaddingRight() - 2 * defaultLeftRightGap - childSideLength) / (childCount - 1);
        float cWidth = childSideLength - 2 * childPadding;
        float cHeight = cWidth;

        anchorList.clear();
        //计算子控件的位置,强制将子View控制绘制在均分的几个锚点上
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            PointF anchorPoint = new PointF((childDis * i + defaultLeftRightGap + childSideLength / 2 + getPaddingLeft()), getPaddingTop() + childSideLength / 2);
            anchorList.add(anchorPoint);
            childView.layout((int) (anchorPoint.x - cWidth / 2), (int) (anchorPoint.y - cHeight / 2), (int) (anchorPoint.x + cWidth / 2), (int) (anchorPoint.y + cHeight / 2));
        }
        PointF pointF = anchorList.get(0);
        bezierCircular.setCenter(pointF.x, pointF.y);
        bezierCircular.initControlPoint();
    }

绘制贝塞尔小球

将贝塞尔小球的一些参数及计算封装成一个对象BezierCircular,因为刚开始只是看了原博客的思路就动手了,绘制贝塞尔小球使用了最原始的方法,定义了4个数据点和8个控制点,在进行五个状态的绘制计算的时候太麻烦了,后面看了博客中的Demo,发现自己的计算太原始笨重了,博客中的demo中关于小球的绘制更加面向对象,更加简洁。不过既然是原创,还是要贴出自己的代码,仅供参考��

public class BezierCircular {
    private static final String TAG = "BezierCircular";

    private static final float C = 0.551915024494f; //常量


    //圆中心坐标
    float centerX;
    float centerY;

    //圆半径
    float radius;

    private PointF currentPoint;
    private PointF targetPoint;
    private float mDifference;

    private float stretchDistance;
    private float cDistance;

    private float moveDistance;

    private float[] mData = new float[8];  //顺时针记录绘制圆形的四个数据点
    private float[] mCtrl = new float[16];  //顺时针记录绘制圆形的八个控制点

    public BezierCircular(float radius) {
        this.radius = radius;
        stretchDistance = radius / 3 * 2;
        mDifference = radius * C;
        cDistance = mDifference * 0.45f;
    }

    public void setCenter(float centerX, float centerY) {
        this.centerX = centerX;
        this.centerY = centerY;

    }



    public void initControlPoint() {

        //初始化数据点
        mData[0] = centerX;
        mData[1] = centerY + radius;

      
使用bezier实现粘连效果ViewPager指示器。效果图:项目地址:https://github.com/chenupt/SpringIndicator 如何导入到工程中:导入控件源码2.导入multiplemodel依赖资源,资源地址:https://github.com/chenupt/MultipleModel 3.添加google-collect-1.0.jar依赖包jar包下载地址:http://www.java2s.com/Code/JarDownload/google-collect/google-collect-1.0.jar.zip 4.导入如下必须的文件:attrs.xml、dimens.xml、colors.xml  这三个默认参数文件是必须要ok。 有些麻烦,不过不用着急,可以点击上面的"下载源码" 按钮,这是我整理的完整的工程,包括源码和相应资源,可直接运行。另外,关于PagerModelManager类,作者没有提供,没关系,可以用PagerManager代替。如何使用1. 添加   xmlns:app="http://schemas.android.com/apk/res-auto"     添加命名空间,这是引入自定义控件时,必须添加的。   如下图:2.  接下来就是创建SpringIndicator、ScrollerViewPager实例。 SpringIndicator 就是当前我们讲的这个控件。另外,作者还提供了ScrollerViewPager 的ViewPager,来配合SpringIndicator使用,  其扩展了android.support.v4.view.ViewPager。<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      xmlns:app="http://schemas.android.com/apk/res-auto"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:orientation="vertical" >          <github.chenupt.springindicator.SpringIndicator             app:siTextSize="18sp"             app:siIndicatorColors="@array/indicator_colors"             app:siTextColor="@color/colorPrimaryDark"             app:siSelectedTextColor="@android:color/white"             app:siIndicatorColor="@color/colorPrimary"             android:id="@ id/indicator"             android:layout_width="match_parent"             android:layout_height="56dp"             android:layout_marginLeft="24dp"             android:layout_marginRight="24dp"              />     <github.chenupt.springindicator.viewpager.ScrollerViewPager             android:id="@ id/viewpager"             android:layout_width="match_parent"             android:layout_height="match_parent"             android:layout_gravity="center" />          </LinearLayout>3. 初始化ViewPager,以及将Viewpager关联到SpringIndicator:        @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); this.setContentView(R.layout.main); viewPager = (ViewPager) findViewById(R.id.viewpager);                 SpringIndicator springIndicator = (SpringIndicator) findViewById(R.id.indicator);                //页面管理器PagerManager, 该管理器负责管理指示器的文字以及ViewPager用到的所有Fragment                PagerManager manager = new PagerManager();                manager.setTitles(getTitles());//设置指示器的文字                //添加4个ViewPager页面                manager.addFragment(new PageFragment());                manager.addFragment(new PageFragment());                manager.addFragment(new PageFragment());                manager.addFragment(new PageFragment());                ModelPagerAdapter adapter = new ModelPagerAdapter(getSupportFragmentManager(), manager);                viewPager.setAdapter(adapter);        //将ViewPager关联到springIndicator                springIndicator.setViewPager(viewPager); } /** *设置指示器上的文字 **/ private List<String> getTitles(){         return Lists.newArrayList("1", "2", "3", "4");     }属性介绍:  siTextColor : 指示器上每个tab的文本颜色  siSelectedTextColor : 当前tab的字体颜色  siTextBg : 整个tab的背景颜色  siTextSize : tab上的字体大小  siIndicatorColor : 设置所有tab的统一的颜色,即圆形区域的颜色  siIndicatorColors : 和siIndicatorColor一样,也是设置tab的颜色的,只是其值是个数组,可分别设置每个tab的颜色  siRadiusMax : 当ViewPager切换时, tab产生粘连效果时的最大圆形半径  siRadiusMin :当ViewPager切换时, tab产生粘连效果时的最小圆形半径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值