Back to project page SpunkyCharts.
The source code is released under:
GNU General Public License
If you think the Android project SpunkyCharts listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.jogden.spunkycharts.traditionalchart; /* ww w. java 2 s .c om*/ /* Copyright (C) 2014 Jonathon Ogden < jeog.dev@gmail.com > This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses. */ import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.os.ConditionVariable; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.widget.RelativeLayout; import com.jogden.spunkycharts.ApplicationPreferences; import com.jogden.spunkycharts.ChartPanelSurfaceView; import com.jogden.spunkycharts.GlobalChartPreferences; import com.jogden.spunkycharts.R; import com.jogden.spunkycharts.misc.OHLC; import com.jogden.spunkycharts.traditionalchart.draw.DrawSemanticsA; import com.jogden.spunkycharts.traditionalchart.draw.DrawSemantics_LINE; public class TraditionalChartPanel extends RelativeLayout implements ChartPanelSurfaceView.DrawingThreads{ public interface CallBackOnRemove { public void call(Number number, Number number2); } public interface DrawSemantics{ public void drawSegment(Canvas canvas, int xPos, OHLC ohlc); public boolean hasDisplayValues(); } private CallBackOnRemove _callbackOnRemove; private float _displayHigh = 0; private float _displayLow = Float.MAX_VALUE; private float _actualHigh = 0; private float _actualLow = Float.MAX_VALUE; private OHLC _activeSeg = new OHLC(0); private int _height = 0; private int _width = 0; private int _topBuffer = ApplicationPreferences.getSegmentVerticalPadding(); private int _bottomBuffer = ApplicationPreferences.getSegmentVerticalPadding(); private int _segWidth = ApplicationPreferences.getSegmentWidth(); private float _segThickness = TraditionalChartPreferences.getDefaultSegThickness(); private GlobalChartPreferences.LineThickness _lineThickness = GlobalChartPreferences.getDefaultLineThickness(); private int _lineColor = TraditionalChartPreferences.getDefaultLineColor(); private Deque<OHLC> _data = new ArrayDeque<OHLC>(); private Class<? extends DrawSemanticsA> _drawSemanticsTy; private ChartPanelSurfaceView _activeSurface = null; private ChartPanelSurfaceView _nonActiveSurface = null; @Override protected void onMeasure( int wMeasureSpec, int hMeasureSpec ){ super.onMeasure(wMeasureSpec,hMeasureSpec); _height = MeasureSpec.getSize(hMeasureSpec); _width = MeasureSpec.getSize(wMeasureSpec); } public <T extends Number> void update(T value) { float val = value.floatValue(); if( val > _activeSeg.high ){ _activeSeg.high = val; if( val > _actualHigh ) _actualHigh = val; } if( val < _activeSeg.low ){ _activeSeg.low = val; if( val < _actualLow ) _actualLow = val; } _activeSeg.close = val; _activeSurface.postUpdate(); } public float last() { return _activeSeg.close; } public boolean isFull() { return _data.size() > (_nonActiveSurface.getMeasuredWidth() / _segWidth); } public void clear() { synchronized(_data){ _data.clear(); _activeSeg = new OHLC(0); } } public synchronized void addSubSegment( OHLC ohlc, boolean drawNow ){ synchronized(_data){ _activeSeg = new OHLC(ohlc); /* copy */ _data.addFirst(_activeSeg); } final int max = _width / _segWidth; /* issue here if width is very small and max < 1 */ if (_data.size() > max && max != 0) { OHLC pped = _dropSubSegment(); if ( pped != null && (pped.high >= _actualHigh || pped.low <= _actualLow) ){ _resetHighLow(); _callbackOnRemove.call( this._actualHigh, this._actualLow ); } } if(drawNow) _nonActiveSurface.postDraw(); } private synchronized OHLC _dropSubSegment() { OHLC tmp; synchronized(_data){ tmp= _data.pollLast(); } return tmp; } public void setDrawSemantics( Class<? extends DrawSemanticsA> type ){ this._drawSemanticsTy = type; } public TraditionalChartPanel(Context context, int segWidth) { super(context); _segWidth = segWidth; } public TraditionalChartPanel(Context context, AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes( attrs, R.styleable.CustomSpunky ); _segWidth = (int) ta.getDimension( R.styleable.CustomSpunky_seg_width, ApplicationPreferences.getSegmentWidth() ); ta.recycle(); } public TraditionalChartPanel(Context context, AttributeSet attrs, int steez) { super(context, attrs, steez); TypedArray ta = context.obtainStyledAttributes( attrs, R.styleable.CustomSpunky, steez, 0 ); _segWidth = (int) ta.getDimension( R.styleable.CustomSpunky_seg_width, ApplicationPreferences.getSegmentWidth() ); ta.recycle(); } public void init() { _activeSurface = (ChartPanelSurfaceView)this.findViewWithTag( "active_surface" ); _nonActiveSurface = (ChartPanelSurfaceView)this.findViewWithTag( "non_active_surface" ); _activeSurface.setParent(this); _nonActiveSurface.setParent(this); } public void forceDraw() { _activeSurface.postDraw(); _nonActiveSurface.postDraw(); } public void forceStop(){ _activeSurface.postStop(); _nonActiveSurface.postStop(); } public void forceStart(){ _activeSurface.postStart(); _nonActiveSurface.postStart(); } public int getSegmentWidth() { return _segWidth; } public float getSegmentThickness() { return _segThickness; } public GlobalChartPreferences.LineThickness getLineThickness() { return _lineThickness; } public int size() { return _data.size(); } public void setLineColor(int colorId) { _lineColor = colorId; } public void setSegmentWidth(int w) { if (w > 0) { _segWidth = w; } LayoutParams lParams = new LayoutParams( w, LayoutParams.MATCH_PARENT ); lParams.addRule(ALIGN_PARENT_RIGHT); _activeSurface.setLayoutParams(lParams); _nonActiveSurface.postInvalidate(); } public synchronized void setYRange( float _displayHigh, float _displayLow, float _actualHigh, float _actualLow ){ this._displayHigh = _displayHigh; this._displayLow = _displayLow; this._actualHigh = _actualHigh; this._actualLow = _actualLow; } public void setVerticalBuffers( int topBuf, int bottomBuf ){ this._topBuffer = topBuf; this._bottomBuffer = bottomBuf; } public void setSegmentThickness( float thickness ){ if(thickness < 0 || thickness > 1) throw new IllegalArgumentException( "thickness arg must be between 0 and 1" ); _segThickness = thickness; } public void setLineThickness( GlobalChartPreferences.LineThickness thickness ){ _lineThickness = thickness; } public void setOnRemoveCallBack( CallBackOnRemove callback ){ _callbackOnRemove = callback; } private void _resetHighLow() { float high = 0; float low = Float.MAX_VALUE; synchronized(_data){ for(OHLC ohlc : _data ){ if( ohlc.high > high ) high = ohlc.high; if( ohlc.low < low ) low = ohlc.low; } } this.setYRange(_displayHigh, _displayLow, high, low); } class NonActiveDrawingThread extends Thread { private int xAdj; private Canvas canvas = null; private DrawSemantics drawSemantics; private boolean isLine = false; private SurfaceHolder holder; public NonActiveDrawingThread(SurfaceHolder holder) { super(); this.holder = holder; } private OHLC adjLineSeg(OHLC ohlc, OHLC prev) { /* use the prev elements close as our open */ /* prob should just cache the val so we dont need to call this */ OHLC tmp = new OHLC(ohlc); if( prev != null) tmp.close = prev.open; return tmp; } @Override public void run() { try{ if( (drawSemantics = _getDrawSemantics()) == null) return; if (drawSemantics.getClass() == DrawSemantics_LINE.class) isLine = true; else isLine = false; xAdj = _segWidth / 2; synchronized(holder){ canvas = holder.lockCanvas(); if(canvas == null) return; final int wdth = canvas.getWidth(); int xPos = wdth - xAdj; if( xPos <= 0) return; if(drawSemantics.hasDisplayValues()){ canvas.drawColor( Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR ); synchronized(_data){ Iterator<OHLC> iter = _data.iterator(); OHLC curr, prev = null; if (iter.hasNext()) prev = iter.next(); /* skip the first(active) */ for( ; iter.hasNext() && xPos >= xAdj && !Thread.interrupted(); xPos-=_segWidth ){ curr = iter.next(); drawSemantics.drawSegment( canvas, xPos, isLine ? adjLineSeg(curr,prev) : curr ); prev = curr; } } } } }finally{ if(canvas != null) holder.unlockCanvasAndPost(canvas); } } }; public Thread newDrawingThreadInstance( SurfaceHolder holder, ChartPanelSurfaceView parent, boolean active ){ /* disregard parent param */ return active ? new ActiveDrawingThread(holder) : new NonActiveDrawingThread(holder); } class ActiveDrawingThread extends Thread implements ChartPanelSurfaceView.Flags{ private int xAdj ; private DrawSemantics drawSemantics; // private boolean isLine = false; private Canvas canvas; private boolean running = false; private SurfaceHolder holder; public ActiveDrawingThread(SurfaceHolder holder) { super(); this.holder = holder; } public void setRunningFlag(boolean status) { running = status; } private void draw() { synchronized(holder){ canvas = holder.lockCanvas(); if(canvas == null) return; canvas.drawColor( Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR ); drawSemantics.drawSegment( canvas, xAdj, _activeSeg ); holder.unlockCanvasAndPost(canvas); } } @Override public void run() { try{ xAdj = _segWidth / 2; if( (drawSemantics = _getDrawSemantics()) == null) return; /* if (drawSemantics.getClass() == DrawSemantics_LINE.class) isLine = true; else isLine = false; */ while(running){ synchronized(this){ this.wait(2500); // DEBUG if(drawSemantics.hasDisplayValues()) draw(); } } } catch (InterruptedException e) { } } }; private DrawSemantics _getDrawSemantics() { DrawSemantics drawSemantics = null; try { drawSemantics = _drawSemanticsTy.getConstructor( Context.class, int.class, int.class, int.class, int.class, float.class, float.class, int.class, int.class, float.class ).newInstance( this.getContext(),_segWidth,_height, _topBuffer, _bottomBuffer, _displayHigh,_displayLow, _lineColor,_lineThickness.value,_segThickness ); } catch (Exception e) { } return drawSemantics; } }