金融类自定义View(三)–股票分时图(关于细节和实现思路)

前言

  • 本篇文章首先会介绍上一篇文章上一篇文章遗留下来的问题:长按回调、缩放问题、加载更多等。
  • 剩下的会介绍如何绘制这个分时图,怎么的思路,流程怎样。
  • 建议,先看第二篇文章和对代码进行大致的浏览,不然阅读起来可能会有点吃力。

效果图

原【天厚实盘】分时图

  • 【天厚实盘】分时图_默认

  • 【天厚实盘】分时图_长按

  • 【天厚实盘】分时图_gif

仿分时图【TimeSharingView.java】

  • 第一阶段,参数准备,外边框、内虚线、折线图等的绘制

  • 第二阶段,x、y文字、实时横线和实时数据、下方透明阴影

  • 第三阶段,实时数据更新分时图

  • 第四阶段,长按十字线,左右移动分时图

  • 第五阶段,长按实时显示详细数据、加载更多处理、实时横线优化、滑动优化

  • 第六阶段,缩放、代码整理、注释

长按回调、缩放问题、加载更多

长按回调

  • 在上一节,我们已经绘制出了长按的十字,也可以左右滑动。现在的需求是仿照【天厚实盘】长按在View上面绘制出按下点(十字中央)对应点的报价详情以及涨跌幅情况。这个地方的处理,有两种:直接在View上绘制;在布局中设置布局,然后把数据回调给使用者,再设置上对应数据。很明显,第二种方式更简单,也更灵活,这里采用第二种。特别注意需要回调除了当前点,还有上一个点,便于计算涨跌幅。套路:定义接口,设置回调,设置布局。

    //长按的绘制逻辑以及回调
    protected void drawLongPress(Canvas canvas) {if (!mDrawLongPressPaint) return;//长按的逻辑....//在这里回调数据信息if (mTimeSharingListener != null) {int size = mQuotesList.size();if ((0 <= finalIndex && finalIndex < size) &&(0 <= finalIndex - 1 && finalIndex - 1 < size))//回调,需要两个数据,便于计算涨跌百分比mTimeSharingListener.onLongTouch(mQuotesList.get(finalIndex - 1),mQuotesList.get(finalIndex));}
    }
    

长按回调

  • 对于缩放问题的处理,真是操碎了心,思考了好久好久。所谓缩放,我们要知道两手指缩放的距离占View有效宽度的百分比,然后根据百分比计算新的有效可视个数,然后重绘。真的有这么简单吗?我们一直绘制的依据是确定起始位置和结束位置。当两个手指缩小视图时,真正的中心点在两指中间,因此起始位置要变,结束位置也要变。最后采用的方案是采用系统的ScaleGestureDetector监听手指,根据detector.getScaleFactor()确定缩放因子。缩放思路:所谓缩放,也是计算新的起始位置和结束位置。这里根据缩放因子detector.getScaleFactor()计算新的可见个数(x缩放因子即可)。当放大时,可见的数据集合的个数(A)应该减少。detector.getScaleFactor()(B的范围[1,2)),这个时候可以新的可见数据集合(C)可以考虑采用C=A-A*(B-1);当然这样计算是否准确,还需要商榷。思路简单,但是这里细节比较多,具体可以参考代码。

    //缩放手势监听
    ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =new ScaleGestureDetector.SimpleOnScaleGestureListener() {@Overridepublic boolean onScale(ScaleGestureDetector detector) {//没有缩放if (detector.getScaleFactor() == 1) return true;//是放大还是缩小boolean isBigger = detector.getScaleFactor() > 1;//变化的个数(缩小或者放大),必须向上取整,不然当mShownMaxCount过小时容易取到0。int changeNum = (int) Math.ceil(mShownMaxCount * Math.abs(detector.getScaleFactor() - 1));//容错处理,省略...//计算新的开始位置。这个地方比较难以理解:拉伸了起始点变大,并且是拉伸数量的一半,结束点变小,也是原来的一半。// 收缩,相反。可以自己画一个图看看mBeginIndex = isBigger ? mBeginIndex + helfChangeNum : mBeginIndex - helfChangeNum;if (mBeginIndex < 0) {mBeginIndex = 0;} else if ((mBeginIndex + mShownMaxCount) > mQuotesList.size()) {mBeginIndex = mQuotesList.size() - mShownMaxCount;}mEndIndex = mBeginIndex + mShownMaxCount;//只要找好起始点和结束点就可以交给处理重绘的方法就好啦~seekAndCalculateCellData();return true;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {Log.e(TAG, "onScaleBegin: " + detector.getFocusX());//指头数量,过滤无用手势if (mFingerPressedCount != 2) return true;return true;}};
    

加载更多

  • 本身,加载更多是很简单的,只要判断移动的时候到最右端就去加载就好。可是,这里牵扯出来另外几个问题

    • 滑动到最右边的时候,需要显示右侧的内间距和绘制小圆点(其它情况不需要显示)。
    • 不再最右侧的时候,滑动时需要隐藏右侧间距;同时,来新数据后,不应该实时绘制View。
    • 滑动到最左边时,需要加载更多。
    • 加载更多触发可能在最左侧,但是加载过程中(加载过程可以左右滑动)可能又在最右侧。状态不确定。
  • 这里的处理是定义滑动枚举类型,确认实时状态

    enum PullType {PULL_RIGHT,//向右滑动PULL_LEFT,//向左滑动PULL_RIGHT_STOP,//滑动到最右边PULL_LEFT_STOP,//滑动到最左边}
    
  • 实时监听并记录状态,并且在触发加载更多时(最好设置一定阀值,比如剩余10个数据时就触发加载更多)

       /*** 移动K线图计算移动的单位和重新计算起始位置和结束位置** @param moveLen*/protected void moveKView(float moveLen) {//移动之前将右侧的内间距值为0mInnerRightBlankPadding = 0;mPullRight = moveLen > 0;int moveCount = (int) Math.ceil(Math.abs(moveLen) / mPerX);if (mPullRight) {int len = mBeginIndex - moveCount;//阀值if (len < DEF_MINLEN_LOADMORE) {//加载更多if (mTimeSharingListener != null && mCanLoadMore) {loadMoreIng();mTimeSharingListener.needLoadMore();}}//向右拉逻辑... } else {//向左拉逻辑...}//确认结束位置mEndIndex = mBeginIndex + mShownMaxCount;//开始位置和结束位置确认好,就可以重绘啦~//Log.e(TAG, "moveKView: mPullRight:" + mPullRight + ",mBeginIndex:" + mBeginIndex + ",mEndIndex:" + mEndIndex);seekAndCalculateCellData();}
    

分时图绘制思路

  • 整个分时图到这里基本全部完成了,包括如下功能:基本的边框、内部虚线、x/y周文字标示、走势折现、加载更多、左右滑动、滑动数据回调、缩放、实时横线价格展示等。
  • 总代码行数1000+行,完全继承系统View,不依赖任何第三方。虽然也不是很多,但是第一次看肯定是懵逼的。下面大致简述思路。
  • 首先是数据的模拟,为了更加符合真实的使用场景,我们把拿到的数据进行了“转换处理”。可以想象,真实使用场景,不可能你直接从服务端拿到的数据就可以刚好符合View的数据类型。至于获取数据、模拟网络环境、切线程、模拟实时Socket推数据等采用了Rx进行了处理(多说一句,Rx在线程切换真是好用到爆)。
  • 基本点,我们会把拿到数据集合(包括推过来的实时数据)全部存到全局的List中,保证单一数据集合。定义可视范围的起始点mBeginIndex和结束点mEndIndex。有大致了解代码的同学,会看到整个View的大部分操作过程中,主要是计算起始点和结束点,然后重绘View。是的,实时加载数据、左右滑动、缩放、加载更多这些核心功能,其实都是为了计算新的起始位置和结束位置。
  • 核心方法,核心方法全部都在onDraw(Canvas canvas)中完成,但是为了逻辑清晰,我们会单独绘制每一个业务功能,保证业务逻辑的清晰,方便修改和扩展。其它的,手势监控在onTouchEvent(MotionEvent event)ScaleGestureDetector mScaleGestureDetector中完成。加载数据直接由使用者传递。

     @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//Log.e("TimeSharingView", "onDraw: ");//默认加载loading界面showLoadingPaint(canvas);if (mQuotesList == null || mQuotesList.isEmpty()) {return;}drawOuterLine(canvas);drawInnerXy(canvas);drawXyTxt(canvas);drawBrokenLine(canvas);//长按处理drawLongPress(canvas);//长按情况下的时间和数据框drawLongPressTxt(canvas);}
    
  • 关于绘图,绘图全部相对于View的左上角开始,在View全局中定义了宽度和高度以及内边距等,基本上在View中绘制任何线和图全部会相对这几个值进行操作,更多的是对位置的准确把握以及对边界的准确控制。

        //控件宽高,会在onMeasure中进行测量。int mWidth;int mHeight;//上下左右padding,这里不再采用系统属性padding,因为用户容易忘记设置padding,直接在这里更改即可。float mPaddingTop = 20;float mPaddingBottom = 50;float mPaddingLeft = 8;float mPaddingRight = 90;//默认情况下结束点距离右边边距float mInnerRightBlankPadding = DEF_INNER_RIGHT_BLANK_PADDING;//为了美观,容器内(边框内部的折线图距离外边框线的上下距离)上面有一定间距,下面也有一定的间距。float mInnerTopBlankPadding = 60;float mInnerBottomBlankPadding = 60;
    
  • 关于y轴最大最小值的确认,这个比较重要。如何保证分时图准确绘制不绘制出边界?如果数据的范围本来是[1,100],突然来了一个10000的数据怎么办?处理的手段是,每次拿到数据之后遍历,找到分时图可视范围内的最大值和最小值和View有效高度,算出来单位高度,然后根据每个点的值和最小值(最大值也可以)的差值计算应有的高度坐标即可。如果出现上述中的特别特别大的数据怎么办?那可能就绘制出一条直线咯(生产环境中有遇到)。

  • 关于错误和调试,有阅读代码的同学可以在代码中看到大量的Log日志。写的过程中遇到了很多问题,由于有大量的数据还实时推数据并且实时刷新绘制,这个时候可能debug就很难发现问题,直接打Log是有效的手段,可以实时观察到数据的异常。当然,大部分问题直接卡断点就能定位到问题。

code

  • https://github.com/scsfwgy/FinancialCustomerView
  • 注:该项目会一直维护
    • 绘制各种金融类的自定义View。
    • 提供金融类自定义View的实现思路。
    • 收集整理相关算法、文档以及专业资料。
  • 另,蜡烛图(包括主图指标)大部分功能已经绘制出来啦,代码也进行了大量的重构。在分支:feature_candleview

金融类自定义View(三)--股票分时图(关于细节和实现思路)相关推荐

  1. 金融类自定义View(四)--股票蜡烛图以及MA、BOLL指标

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 金融类自定义View(四)–股票蜡烛图(包含MA.BOLL指标)以及代码重构 前言 本文只描述蜡烛图单独的绘制逻辑,至于和分时图相 ...

  2. python股票交易接口实现股票分时图K线图及抓取level2行情的开发程序分析

    大家对股票交易接口并不陌生,那么要想获取股票数据获取,也是可以通过python股票交易接口实现股票分时图K线图及抓取level2行情的开发程序分析如下: import sys from PyQt5 i ...

  3. 用python画股票分时图 github_用python的matplotlib和numpy库绘制股票K线均线和成交量的整合效果(含量化验证交易策略代码)...

    在用python的matplotlib和numpy库绘制股票K线均线的整合效果(含从网络接口爬取数据和验证交易策略代码)一文里,我讲述了通过爬虫接口得到股票数据并绘制出K线均线图形的方式,在本文里,将 ...

  4. Android 自定义View -- 简约的折线图

    转载请注明出处:http://write.blog.csdn.net/postedit/50434634 接上篇 Android 圆形百分比(进度条) 自定义view 昨天分手了,不开心,来练练自定义 ...

  5. Android自定义View 开发流程综合简述 Android自定义View(三)

    本文简述一下自定义View中常用方法 1 简述 自定义View可以认为是继承自View或者ViewGroup Android中的任何一个布局.任何一个控件其实都是直接或间接继承自View的,如Text ...

  6. TUSHARE绘制股票分时图

    1.注册网站https://tushare.pro/,获取积分 2. 点右上角的登陆,点击个人头像,复制token 3.调取模块,并加载你的token import tushare as ts pro ...

  7. 自定义view 自动轮播图+GridView

    MainActivity 主方法 package com.bawie.mymonizhoukao1;import android.support.v7.app.AppCompatActivity; i ...

  8. android开发自定义View(三)仿芝麻信用积分

    此文参考了https://github.com/HotBitmapGG/CreditSesameRingView 感谢作者的分享!! 首先看一下支付宝上显示的样子 然后看一下模仿的效果 代码 基础部分 ...

  9. Flutter 自定义View之 饼状图

    版权声明:本文为博主原创文章,转载请注明出处! 今天跟大家分享的是用Flutter来实现的自定义饼状图,下面来看看效果! 通过点击左右两侧的按钮,可以实现扇形切换,被选中的扇形有个放大的效果,中间的百 ...

  10. matlab 股票分时图_MATLAB怎样获取实时股市行情数据

    引用j88r的回答: 1Sina股票数据接口 以大秦铁路(股票代码:601006)为例,如果要获取它的最新行情,只需访问新浪的股票数据 接口: http://hq.sinajs.cn/list=sh6 ...

最新文章

  1. Python ljust()方法
  2. IIS配置不正确可能导致“远程服务器返回错误: (404) 未找到错误一例。
  3. 从源码构建 MyBatis Generator(MBG)
  4. tar ------ linux解压 tar命令
  5. 分布式配置中心阿波罗的搭建与客户端的应用
  6. 进程调度实验_Linux应用编程之进程的PID与PPID
  7. IDEA + Maven创建SpringMVC项目和XML配置
  8. SAP License:统计型实际结算型内部订单
  9. 第12章 数据库完整性
  10. 解决jsp页面乱码问题
  11. 关于C#中动态加载AppDomain的问题
  12. 广告投放类型以及各大平台计费准则+推广常用评价指标
  13. java模拟usb接口_Java练习:一个简单的USB接口程序设计
  14. 求义隆单片机c语言红外解码程序,吐槽义隆单片机,顺便送上超轻红外解码程序....
  15. Testin云测平台
  16. 又创奇迹 揭秘极米H1为何成高端家庭娱乐新爆品
  17. 【微信小程序】打开微信内置的地图
  18. JS大总结javascript事件查询综合
  19. 【极简写作】Markdown 常用快捷键
  20. Android开发基础——Kotlin简介

热门文章

  1. C语言CGI编程入门(一)
  2. PDF有口令密码怎么移除?
  3. 微信小程序自定义弹窗
  4. SLAM专题(8)卡尔曼滤波和扩展卡尔曼滤波 原理与应用
  5. 8086、80286、80386
  6. 【转】100项PPT制作技术
  7. 微信小程序实现tab切换
  8. mfc 控件显示 被遮挡_MFC控件显示和隐藏的问题
  9. 一图搞懂梯度、散度、旋度、Jacobian、Hessian、Laplacian之间的关系
  10. React-pdf:pdf预览插件实践