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.pricebyvolumechart; /*from w w w. j a v a 2 s . com*/ /* 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.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.os.Handler; 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.R; import com.jogden.spunkycharts.pricebyvolumechart.draw.DrawSemanticsA; import com.jogden.spunkycharts.traditionalchart.TraditionalChartPreferences; public class PriceByVolumeChartPanel extends RelativeLayout implements ChartPanelSurfaceView.DrawingThreads{ public interface DrawSemantics{ public void drawSegment(Canvas canvas, int xPos, int price); public boolean hasDisplayValues(); //DEBUG //public void setColor(int colorId); } public interface OnNewHighVolume{ public void onNewHighVolume(int v); } private PriceByVolumeChartPanel _this = this; private OnNewHighVolume _callback = null; private Integer _actualHigh = 0; private int _segCount = 0; private int _activeBucket = -1; private int _height = 0; private int _width = 0; private int _leftBuffer = ApplicationPreferences.getSegmentVerticalPadding(); private int _rightBuffer = ApplicationPreferences.getSegmentVerticalPadding(); private int _topBuffer = 0; private int _bottomBuffer = 0; private Handler _myHandler = null; private int _segHeight = ApplicationPreferences.getSegmentWidth(); // use traditional prefs for now private float _segThickness = .95f; // // TraditionalChartPreferences.getDefaultSegThickness(); private int _lineColor = TraditionalChartPreferences.getDefaultLineColor(); private ArrayList<Integer> _data = new ArrayList<Integer>(); private Class<? extends DrawSemanticsA> _drawSemanticsTy; private ChartPanelSurfaceView _surface = null; @Override protected void onMeasure( int wMeasureSpec, int hMeasureSpec ){ super.onMeasure(wMeasureSpec,hMeasureSpec); _height = MeasureSpec.getSize(hMeasureSpec); _width = MeasureSpec.getSize(wMeasureSpec); } /* RACE CONDITION WITH THE ADAPTER WHEN WE HAVE * A NEW HIGH/LOW AND NEED TO RECREATE BUCKETS !?!? * * DO WE NEED TO SYNC ON _activeBucket ?? ... (yes) */ public void update(int bucket, int volume) { int v; synchronized(_data){ try{ v = _data.get(bucket) + volume; _data.set(bucket, v ); }catch(IndexOutOfBoundsException e){ throw new IndexOutOfBoundsException( "_data ListArray doesn't have that bucket(index)" ); } } if( v > _actualHigh){ _actualHigh = v; forceDraw(); if( _callback != null) _callback.onNewHighVolume(v); } if(_activeBucket != bucket){ _activeBucket = bucket; } Log.d("PBV-UPDATE", "Active Bucket:" + String.valueOf(_activeBucket)); Log.d("PBV-UDPATE", "[ " + _data.toString() + " ]"); _surface.postUpdate(); } public void clear() { synchronized(_data){ _data.clear(); _activeBucket = -1; } } public void populate( int[] buckets ) { String logStr = "[ "; int v; synchronized(_data){ _data.clear(); for( int i = 0; i < buckets.length; ++i) { v = buckets[i]; logStr += (String.valueOf(buckets[i]) + ", "); if(v > _actualHigh) _actualHigh = v; _data.add( i, v); } if(_callback != null) _callback.onNewHighVolume(_actualHigh); logStr += " ]"; Log.d("PBV-POPULATE", logStr); _setSegCount(buckets.length); _resetView(); } } public void setDrawSemantics( Class<? extends DrawSemanticsA> type ){ this._drawSemanticsTy = type; } public PriceByVolumeChartPanel(Context context, int segHeight) { super(context); _segHeight = segHeight; } public PriceByVolumeChartPanel(Context context, AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes( attrs, R.styleable.CustomSpunky ); _segHeight = (int) ta.getDimension( R.styleable.CustomSpunky_seg_width, ApplicationPreferences.getSegmentWidth() ); ta.recycle(); } public PriceByVolumeChartPanel( Context context, AttributeSet attrs, int steez ){ super(context, attrs, steez); TypedArray ta = context.obtainStyledAttributes( attrs, R.styleable.CustomSpunky, steez, 0 ); _segHeight = (int) ta.getDimension( R.styleable.CustomSpunky_seg_width, ApplicationPreferences.getSegmentWidth() ); ta.recycle(); } public void init(OnNewHighVolume callback, Handler handler) { _myHandler = handler; _surface = (ChartPanelSurfaceView)_this.findViewWithTag( "primary_surface" ); _callback = callback; _surface.setParent(this); } public void forceDraw() { _surface.postDraw(); } public void forceStop() { _surface.postStop(); } public void forceStart() { _surface.postStart(); } public int getSegmentHeight() { return _segHeight; } public float getSegmentThickness() { return _segThickness; } public int buckets() { return _data.size(); } private void _setSegCount(int count) { if(count <= 0) return; else _segCount = count; int h = _height / count; if( h <= 0) return; else _segHeight = h; _resetView(); } public void setHorizontalBuffers( int leftBuffer, int rightBuffer ){ _leftBuffer = leftBuffer; _rightBuffer = rightBuffer; } public void setVerticalBuffers( int topBuffer, int bottomBuffer ){ _topBuffer = topBuffer; _bottomBuffer = bottomBuffer; } public void setSegmentThickness( float thickness ){ if(thickness < 0 || thickness > 1) throw new IllegalArgumentException( "thickness arg must be between 0 and 1" ); _segThickness = thickness; } public Thread newDrawingThreadInstance( SurfaceHolder holder, ChartPanelSurfaceView parent, boolean active ){ return new DrawingThread(holder,parent); //return active ? new ActiveDrawingThread(holder, parent) //: new NonActiveDrawingThread(holder, parent); } private void _resetView() { final LayoutParams lParams = (LayoutParams)_surface.getLayoutParams(); lParams.height = _segHeight * _segCount; lParams.topMargin = _topBuffer; _myHandler.post( new Runnable(){ public void run(){ _surface.setLayoutParams(lParams); }}); } private DrawSemantics _getDrawSemantics() { DrawSemantics drawSemantics = null; try { drawSemantics = (DrawSemantics)_drawSemanticsTy.getConstructor( Context.class, int.class, int.class, int.class, int.class, float.class, int.class, float.class ).newInstance( this.getContext(),_width, _segHeight, _leftBuffer, _rightBuffer, _actualHigh, _lineColor, _segThickness ); } catch (Exception e) { } return drawSemantics; } /* class NonActiveDrawingThread extends Thread { private Canvas canvas = null; private DrawSemantics drawSemantics; private SurfaceHolder holder; private ChartPanelSurfaceView parent; public NonActiveDrawingThread( SurfaceHolder holder, ChartPanelSurfaceView parent) { super(); this.holder = holder; this.parent = parent; } @Override public void run() { int beg = 0, end = 0; /* note: we are decrementing and using [,] try{ if( (drawSemantics = _getDrawSemantics()) == null) return; synchronized(holder){ canvas = holder.lockCanvas(); if(canvas == null) return; final int height = canvas.getHeight(); int yPos; if(drawSemantics.hasDisplayValues()){ canvas.drawColor( Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR ); final int INCR = ApplicationPreferences.getTimeoutIncrement(); final int TOUT = ApplicationPreferences.getLongTimeout(); int timeOutCount = 0; /* stall for a valid bucket value, leave if none while( _activeBucket < 0 || _activeBucket >= _data.size() ) try { if( (timeOutCount+=INCR) > TOUT ) return; Thread.sleep(INCR); } catch (InterruptedException e) { } if( parent == _nonActiveSurfaceT){ end = _segCount; beg = _activeBucket +1 ; yPos = height-(_segHeight/2); //DEBUG drawSemantics.setColor(Color.GREEN); }else if(parent == _nonActiveSurfaceB){ end = _activeBucket; beg = 0; yPos = height-(_segHeight/2); //DEBUG drawSemantics.setColor(Color.BLUE); }else{ throw new RuntimeException( "invalid parent in NonActiveDrawingThread" ); } if( yPos <= 0) return; synchronized(_data){ Iterator<Integer> iter = _data.iterator(); Integer curr = null; /* move iter to beginning for( int i = 0; i < beg; ++i) curr = iter.next(); for( ; beg < end && !Thread.interrupted(); ++beg, yPos -= _segHeight ){ curr = iter.next(); drawSemantics.drawSegment( canvas, yPos, curr ); } } } } }catch(IllegalStateException e){ throw e; }catch(NoSuchElementException e){ e.printStackTrace(); }finally{ if(canvas != null) holder.unlockCanvasAndPost(canvas); } } }; */ /* REDRAW THE WHOLE THING; THEN OVER-WRITE THE ACTIVE SEG */ class DrawingThread extends Thread implements ChartPanelSurfaceView.Flags{ private DrawSemantics drawSemantics; private Canvas canvas; private boolean running = true; private SurfaceHolder holder; public DrawingThread( SurfaceHolder holder, ChartPanelSurfaceView parent ){ super(); this.holder = holder; } public void setRunningFlag(boolean status) { running = status; } private void draw() { int yPos; if(canvas == null) return; final int height = canvas.getHeight(); /* check state *//* sync issues ? */ if( _activeBucket < 0 || _activeBucket >= _data.size() || !drawSemantics.hasDisplayValues() || _data.size() != _segCount) return; /* need to reset, double buffering doesn't let us retain non-active */ canvas.drawColor( Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR ); yPos = height-(_segHeight/2); if( yPos <= 0) return; synchronized(_data){ Iterator<Integer> iter = _data.iterator(); for( int beg = 0; beg < _segCount && !Thread.interrupted() && iter.hasNext(); ++beg, yPos -= _segHeight ){ drawSemantics.drawSegment( canvas, yPos, iter.next() ); } } } @Override public void run() { if( (drawSemantics = _getDrawSemantics()) == null) return; synchronized(holder){ try{ canvas = holder.lockCanvas(); draw(); } catch(RuntimeException e) { if( canvas != null) /* if we fail initially, clear the canvas */ canvas.drawColor( Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR ); } finally { if(canvas != null) holder.unlockCanvasAndPost(canvas); } } while(running){ synchronized(holder){ try{ synchronized(this){ wait(2500); // DEBUG } try{ canvas = holder.lockCanvas(); draw(); } finally { if(canvas != null) holder.unlockCanvasAndPost(canvas); } } catch (InterruptedException e) { } catch(NoSuchElementException e) { e.printStackTrace(); } catch(RuntimeException e) { throw e; } } } } }; }