用CorePlot实现类似Air Quality的柱状图滚动效果(1/2)

也是最近1个月在项目中不断研究CorePlot实现柱状图的效果。

先来看一下Air Quality的柱状图效果:

经过1个月的研究,现在基本上已经重现了这个柱状图的功能(99%),而且还加上了刷新数据的功能。

计划通过两篇blog来记录下开发中的难点,在后一篇blog中会把所有的源码挂上去。

难点有以下几个:

1. Coreplot自带的滚动机制在我这边做起来有一些卡顿,用户体验很不好,这里需要替换掉它自带的滚动功能

2. 不用coreplot的滚动以后,需要新建一个view用于承载左侧固定的坐标轴

3. 滚动是高亮的柱子的选择以及设置高亮标签

4. 顶端时间的显示

5. 数据刷新功能

如果不清楚如何用swift添加coreplot,参见我之前的这篇blog:http://blog.csdn.net/u011156012/article/details/44061411

本篇blog首先讲解下前两个问题。进入正题:

先上一下storyboard的构建:

二级页模块bg是一个背景的ImageView;Label就是中间上部的标题label;line1px是美工切出来的1个px的分割线,放在label下面;然后ScrollView用来承载我们的Coreplot,可以看到里面还是放了一个我们的Graph hosting view;最后的view是用来承载左侧的坐标系。

接下来看如何在scrollview中搭建hosting view

/**
    关于graphView的长度计算,每个项目占10个px的长度是合适的,也就是说  150个柱子,graph的width,也就是scroll_view的contentsize的width
    是150*10= 1500 比较合适;而plot space的xMax的值 设置为 柱子个数+10
    */
    func initPlotGraphView(){
        // 禁止缩放
        graphView.allowPinchScaling = false
        // Create graph
        // 设置graph的宽,num*10 如果 width 最小值为frame.width
        var graph_width = CGFloat(num * 10)
        if graph_width < self.frame.size.width {
            // 多+1的目的是为了让scrollview能够滚动
            graph_width = self.frame.size.width + 51
            self.scrollType = 1
        }
        var graph = CPTXYGraph(frame: CGRectMake(0, 0, graph_width, graphView.frame.size.height))
        println("There are \(num) bars, graph's width : \(graph.frame.size.width),height is : \(graph.frame.size.height)")
        // Set ScrollView
        self.scroll_view.contentSize = CGSizeMake(graph_width - 50, graph.frame.size.height)
        OriginalContentOffSet_x = self.scroll_view.contentSize.width - self.frame.size.width
        self.scroll_view.contentOffset = CGPointMake(OriginalContentOffSet_x, 0)
        // 设置起始的contentoffset
        self.lastContentOffset = self.scroll_view.contentOffset.x
        println("当前的contentOffset是:\(OriginalContentOffSet_x)")
        self.scroll_view.delegate = self
        graphView.frame = graph.bounds

        graph.plotAreaFrame.masksToBorder = false
        graphView.hostedGraph = graph
        // Configure the graph
        graph.backgroundColor = UIColor.clearColor().CGColor
        // Graph 在hostedGraph中的偏移
        graph.paddingBottom = 20.0
        graph.paddingLeft = 50.0
        graph.paddingRight = 5.0
        graph.paddingTop = 15.0
        graph.plotAreaFrame.borderLineStyle = nil
        graph.plotAreaFrame.cornerRadius = 0.0

这里我用一个函数initPlotGraphView来把所有的代码写进去,因为这部分代码确实有点长。

我基本上加上了足够多的注释。有一些特别重要的部分单独拿出来强调以下。

正常情况下,我们的graphview的长度肯定会大于scrollview的长度的,因为数据足够多的话,这个长度一定是够的;当然一定会出现特殊的情况,比如我们不断向左边划屏,刷新历史的数据,总会遇到历史数据刷新过来只有几条或者十几条的情况,这样整个scrollview的contentsize的width就会太小,而导致scrollview不能滑动了,所以这里面如果graphview的width小于屏幕的宽度,就要把它+1,这样确保scrollview中的数目不多的情况下依旧可以滑屏。

这里补充以下前提:因为coreplot的数据是画上去的,在我们不适用它自带的滑屏效果后,我们需要把所有的柱子都画到scrollview里面,这样会占用不小的内存,实际测试中,画上600个柱子大概要消耗20M左右的内存,所以我们要采用滚动刷新的机制,确保每个scrollview中的柱子数目不能太多。项目中我使用的阈值是120个。

接下来设置bar plot

// set up bar plot
        theBarPlot = CPTBarPlot.tubularBarPlotWithColor(CPTColor.clearColor(), horizontalBars: false)
        // 去除每个柱子周围的黑线
        var linestyle = CPTMutableLineStyle()
        linestyle.lineWidth = 0.1
        linestyle.lineColor = CPTColor.lightGrayColor()
        theBarPlot.lineStyle = linestyle
        // setup line style
        var barLineStyle = CPTMutableLineStyle()
        barLineStyle.lineColor = CPTColor.whiteColor()
        barLineStyle.lineWidth = 1
        // set up text style
        var textLineStyle = CPTMutableTextStyle()
        textLineStyle.color = CPTColor.whiteColor()
        // set up plot space
        var xMin = Float(0)
        var xMax = Float((num > 40 ? num : 40)+10)
        var yMin = Float(0)
        var yMax = self.model.maxValue.floatValue
        var plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace
        // 允许滚动
        plotSpace.allowsUserInteraction = false

        // 设置滚动时的动画效果,这里采用默认的就好
        // plotSpace.momentumAnimationCurve = CPTAnimationCurveExponentialIn
        // plotSpace.bounceAnimationCurve = CPTAnimationCurveExponentialIn

        // 设置x,y在视图显示中大小,也就是点的个数,通过这样设置可以达到放大缩小的效果,来达到我们想要的合理视图显示
        // 这里因为我们外层添加了scrollview,来取代它自身的比较卡的滚动,所以,是1:1的关系
        // 如果想用它自己的滚动,这里的x的length应该是xMax的1/4或者1/8这样子的,因为这里的长度是一屏之内显示的数量
        plotSpace.xRange = CPTPlotRange(location: CPTDecimalFromFloat(xMin), length: CPTDecimalFromFloat(xMax))
        plotSpace.yRange = CPTPlotRange(location: CPTDecimalFromFloat(yMin), length: CPTDecimalFromFloat(yMax))

        //设置x、y轴的滚动范围,如果不设置,默认是无线长的
        plotSpace.globalXRange = CPTPlotRange(location: CPTDecimalFromFloat(xMin), length: CPTDecimalFromFloat(xMax))
        plotSpace.globalYRange = CPTPlotRange(location: CPTDecimalFromFloat(yMin), length: CPTDecimalFromFloat(yMax))

这里其实把每个柱子的边线宽度设置成了0.1,因为开始设置成0了以后,或者把theBarPlot.linestyle  =nil ,会有一种情况,就是我们传回来的数据是0.x,就是大于0但是小于1时,柱子会不显示出来,所以这把边界设置成0.1可以保证当传进来的数字大于0小于1时也能够显示出来一个很小的线。

这里关于plotSpace的x和y range我弄成了1:1的关系,就是把所有的内容都显示出来,在注释里面也些了,如果要使plotspace滚动的话,xRange的长度应该是1/4或者1/8,意思就是分几个屏幕来显示所有的数据。

接下来绘制x轴和y轴

// add plot to graph
        theBarPlot.dataSource = self
        theBarPlot.delegate = self
        // 设定基值,大于该值的从此点向上画,小于该值的反向绘制,即向下画
        theBarPlot.baseValue = CPTDecimalFromInt(0)
        // 设定柱状图的宽度(0.0~1.0)这里柱子的宽度还是上面的plotSpace的xRange和GlobalXRange有关,这里是个百分比,是在那两个值决定之后的柱子宽度为基准的一个百分比
        theBarPlot.barWidth = CPTDecimalFromDouble(0.9)
        // 柱状图每个柱子开始绘制的偏移位置,我们让它绘制在刻度线中间,所以不偏移
        theBarPlot.barOffset = CPTDecimalFromDouble(0.0)

        // set Axis and styles
        var axisSet = graph.axisSet as! CPTXYAxisSet

        var xLineStyle = CPTMutableLineStyle()
        xLineStyle.lineColor = CPTColor.whiteColor()
        xLineStyle.lineWidth = 1.0

        var minorLineStyle = CPTMutableLineStyle()
        minorLineStyle.lineColor = CPTColor.blueColor()
        minorLineStyle.lineWidth = 0.5

        var labelStyle = CPTMutableTextStyle()
        labelStyle.fontName = FONT_HEITI
        labelStyle.fontSize = 10
        labelStyle.color = CPTColor.whiteColor()

        // xAxis
        var xAxis = axisSet.xAxis
        xAxis.axisLineStyle = nil
        // 加上这句才能显示label,如果去掉这两句,会显示1.0,2.0  而不是用户自定义的值。。。
        // CPTAxisLabelingPolicyNone就是不使用系统自定义的label而用户来自定义位置和内容
        xAxis.labelingPolicy = CPTAxisLabelingPolicyNone
        // 让x轴设置顶端的offset
        xAxis.axisConstraints = CPTConstraints.constraintWithUpperOffset(-5.0)
        // x轴大刻度线,线形设置
        xAxis.majorTickLineStyle = nil
        // 刻度线的长度
        xAxis.majorTickLength = 10
        // 间隔单位,和xMin-xMax对应
        xAxis.majorIntervalLength = CPTDecimalFromDouble(10)
        // 小刻度线
        xAxis.minorTickLineStyle = nil
        // 小刻度线间隔距离
        xAxis.minorTicksPerInterval = 1
        // 设置y轴在x轴上的重合点,貌似没啥作用,起作用的是axisConstraints
        // xAxis.orthogonalCoordinateDecimal = CPTDecimalFromInt(0)

关于自定义的x轴label的显示我们放到下一个blog去介绍,这里面先暂时不多介绍。

// yAxis 这里其实是一系列让Y轴消失的动作
        var yAxis = axisSet.yAxis
        yAxis.axisLineStyle = nil
        yAxis.majorTickLineStyle = nil
        yAxis.majorTickLength = 0
        yAxis.majorIntervalLength = CPTDecimalFromInt(500)
        yAxis.minorTickLineStyle = nil
        yAxis.minorTicksPerInterval = 0
        yAxis.labelTextStyle = nil
        yAxis.orthogonalCoordinateDecimal = CPTDecimalFromInt(0)
        // 固定Y轴坐标轴,就是在X轴横移的时候,y坐标轴不动
        yAxis.axisConstraints = CPTConstraints(lowerOffset: CGFloat(1.0))

        // 将bar plot添加到默认的空间中
        graph.addPlot(theBarPlot, toPlotSpace: graph.defaultPlotSpace)
        // 选中最新的数据
        barPlot(theBarPlot, barWasSelectedAtRecordIndex: UInt(num), withEvent: nil)
        lastHightLighBarOffset = scroll_view.contentOffset.x
}

这样整个initPlotGraphView就完成了,当然函数的最后选中特定的bar以及高亮的offset的用法我们都在下一个blog中介绍。

接下来显示左侧的纵轴的刻度是个很简单的函数,因为我们在之前把Y轴设定了消失的动作,所以这里面的Y轴其实就是我们手动设置上去的几个label

    // 显示各个label的刻度值
    func setyAxisValues(){
        let max = self.model.maxValue.integerValue
        label_1.text = "\(max)"
        label_2.text = "\(max*3/4)"
        label_3.text = "\(max/2)"
        label_4.text = "\(max/4)"
        label_5.text = "0"

        if max == 1 {
            label_2.hidden = true
            label_3.hidden = true
            label_4.hidden = true
            label_5.hidden = false
        }else{
            label_2.hidden = false
            label_3.hidden = false
            label_4.hidden = false
            label_5.hidden = false
        }
    }

这里面把传进来的最大值做相关的计算并且显示在label上。

基本上前两个难点就解决了。基本上我们已经搭建好了大的框架,下来的工作就是具体解决一些细节的东西,这里我们放到下一篇blog中详细讲解。

时间: 2024-08-04 04:21:43

用CorePlot实现类似Air Quality的柱状图滚动效果(1/2)的相关文章

用CorePlot实现类似Air Quality的柱状图滚动效果(2/2)

我们接着来看剩下的3个难点: 3. 滚动是高亮的柱子的选择以及设置高亮标签 4. 顶端时间的显示 5. 数据刷新功能 先来说高亮标签的设置吧. 这里需要设置关于barplot的几个代理方法: /** * @author KaKa, 15-06-24 14:06:57 * * BarPlot Delegate * 用于选中不同的柱子后显示出来label */ func barPlot(plot: CPTBarPlot!, barWasSelectedAtRecordIndex idx: UInt,

Android编程实现类似天气预报图文字幕垂直滚动效果的方法

本文实例讲述了Android编程实现类似天气预报图文字幕垂直滚动效果的方法.分享给大家供大家参考,具体如下: 在很多天气或者新闻的应用中,我们都能看到一些字幕滚动的效果,最简单的实现为跑马灯效果,用系统提供的属性即可实现. 复杂一些的就需要自己去用自定义控件实现. 比如 让TextView 实现垂直滚动. 这里我要讲的是垂直滚动的字幕效果,并且内容并不仅为文字,还可以加入图片或者其他元素. 废话不多说,还是直接上效果图: 首先还是看一下核心的实现: 目前我的做法是重写了ScrollView,对外

Android UI设计系列之自定义SwitchButton开关实现类似IOS中UISwitch的动画效果(2)_Android

做IOS开发的都知道,IOS提供了一个具有动态开关效果的UISwitch组件,这个组件很好用效果相对来说也很绚丽,当我们去点击开关的时候有动画效果,但遗憾的是Android上并没有给我们提供类似的组件(听说在Android4.0的版本上提供了具有动态效果的开关组件,不过我还没有去看文档),如果我们想实现类似的效果那该怎么办了呢?看来又得去自定义了. 公司的产品最近一直在做升级,主要做的就是把界面做的更绚丽更美观给用户更好的体验(唉,顾客是上帝......),其中的设置功能中就有开关按钮,原来的开

如何用Java写出类似QQ好友列表那样的效果,求大神指教最好有代码

问题描述 如何用Java写出类似QQ好友列表那样的效果,求大神指教最好有代码 请问如何用Java写出类似QQ好友列表那样的效果,求大神指教最好有代码 解决方案 http://etwo.iteye.com/blog/1460375

js实现类似菜单风格的TAB选项卡效果代码_javascript技巧

本文实例讲述了js实现类似菜单风格的TAB选项卡效果代码.分享给大家供大家参考.具体如下: 这是一款基于javascript实现的一组简洁选项卡代码,类似菜单风格的TAB选项卡,没有使用图片,因此有些地方处理的还不太到位,不介意使用图片修饰的朋友可以再次美化这上选项卡,风格有点类似于菜单. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/js-menu-style-tab-nav-codes/ 具体代码如下: <html xmlns="ht

JS实现类似51job上的地区选择效果示例_javascript技巧

本文实例讲述了JS实现类似51job上的地区选择效果.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head><title>地区选择效果</title></head> <meta http-equiv="Content-Type" content="text/html;

Android实现类似网易新闻选项卡动态滑动效果_Android

 本文会实现一个类似网易新闻(不说网易新闻大家可能不知道大概是什么样子)点击超多选项卡,选项卡动态滑动的效果. 首先来看看布局,就是用HorizontalScrollView控件来实现滑动的效果,里面包含了一个布局. 接下来我们在onCreat方法中加载布局和构建我们需要显示的数据 <code class="hljs avrasm"> @Override protected void onCreate(Bundle savedInstanceState) { super.o

js实现类似新浪微博首页内容渐显效果的方法_javascript技巧

本文实例讲述了js实现类似新浪微博首页内容渐显效果的方法.分享给大家供大家参考.具体分析如下: 要点一: if(list_li.length>=1){ list.insertBefore(li,list_li[0]); }else{ list.appendChild(li); } 从在前面插入新内容,如果没有新内容,就是在后面插入新内容. 要点二: var height=li.offsetHeight; li.style.height='0'; 取得li的高度,然后再li的高度设置为0,因为高度

jQuery实现类似淘宝网图片放大效果的方法_jquery

本文实例讲述了jQuery实现类似淘宝网图片放大效果的方法.分享给大家供大家参考.具体实现方法如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <title>类似淘宝网的图片放大代码</title> <script type="