Android原生股票圖-分時(shí)圖講解和繪制(一)
Android原生股票圖-分時(shí)圖講解和繪制(二)
Android原生股票圖-K線圖講解和繪制(一)
一、試圖的展示效

效果一

效果二
二、試圖簡(jiǎn)介**
- 效果圖中分為兩部分:
1、主視圖,主要展示分時(shí)圖數(shù)據(jù);
2、子試圖,主要展示CJL圖和MACD圖,點(diǎn)擊子試圖時(shí)兩者進(jìn)行切換。 - 分時(shí)圖、CJL圖和MACD圖簡(jiǎn)介:
1、分時(shí)圖
分時(shí)圖只要是 均線和成交價(jià)的折線,左軸時(shí)成交價(jià)中點(diǎn)為昨日成交量,右軸成交價(jià)的漲跌幅;
2、CJL圖
左軸為成交量主要展示柱體的變化,柱體的類(lèi)型見(jiàn)圖:
CJL 柱體類(lèi)型
右軸為持倉(cāng)量主要展示折線圖。
3、MACD圖
如MACD(12,26,9)主要展示DIFF的值、DEA的值和STICK的值;計(jì)算公式如下:
DIFF白線:
DIFF=當(dāng)前周期EMA(12)-當(dāng)前周期EMA(26)
EMA(12)=當(dāng)前周期EMA(12)×11/13+當(dāng)前周期收盤(pán)價(jià)×2/13
EMA(26)=當(dāng)前周期EMA(26)×25/27+當(dāng)前周期收盤(pán)價(jià)×2/27
DEA黃線
當(dāng)前周期DEA=前一周期DEA×8/10+當(dāng)前周期DIF×2/10。
STICK(MACD柱)
STICK=當(dāng)前周期(DIF-DEA)×2
Y軸刻度生成規(guī)則:
中軸為0,取圖內(nèi)DIFF、DEA、STICK三項(xiàng)指標(biāo)最高絕對(duì)值時(shí)點(diǎn)值前兩位向上取5的最小倍數(shù),剩下位數(shù)以0填充,后將該值2等分并分別在中軸上下標(biāo)識(shí)刻度。例如當(dāng)前圖內(nèi)最高時(shí)點(diǎn)值為-1623,則前兩位16向上取2的最小倍數(shù)為20,取值為2000,2等分后上下刻度分別為2000、1000、0、-1000、-2000。
柱體類(lèi)型:
MACD 柱體類(lèi)型
三、代碼**
- MACD的計(jì)算代碼 ,主要根據(jù)closePrice生成DIF、DEA和STICK
/**
* 計(jì)算macd
*
* @param datas
*/
public static void calculateMACD(List<Minute> datas) {
float ema12 = 0;
float ema26 = 0;
float dif = 0;
float dea = 0;
float macd = 0;
for (int i = 0; i < datas.size(); i++) {
Minute point = datas.get(i);
final float closePrice = point.getClose();
if (i == 0) {
ema12 = closePrice;
ema26 = closePrice;
} else {
// EMA(12) = 前一日EMA(12) X 11/13 + 今日收盤(pán)價(jià) X 2/13
// EMA(26) = 前一日EMA(26) X 25/27 + 今日收盤(pán)價(jià) X 2/27
ema12 = ema12 * 11f / 13f + closePrice * 2f / 13f;
ema26 = ema26 * 25f / 27f + closePrice * 2f / 27f;
}
// DIF = EMA(12) - EMA(26) 。
// 今日DEA = (前一日DEA X 8/10 + 今日DIF X 2/10)
// 用(DIF-DEA)*2即為MACD柱狀圖。
dif = ema12 - ema26;
dea = dea * 8f / 10f + dif * 2f / 10f;
macd = (dif - dea) * 2f;
point.diff = dif;
point.dea = dea;
point.macd = macd;
}
}
- 子試圖點(diǎn)擊區(qū)域的處理,根據(jù)點(diǎn)擊來(lái)卻換CJL和MACD
//CJL和MACL之間的切換
@Override
protected void jumpToCJLAndMACL(float downX, float downY) {
//點(diǎn)擊子試圖判斷
if (downX > 0 && downX < mWidth) {
if (downY > mMainHeight + mVolumeTextHeight && downY < mMainHeight + mVolumeTextHeight + mVolumeHeight) {
Log.d("--->", "x = " + downX + ";" + "y = " + downY);
isCJL = !isCJL;
invalidate();
}
}
//點(diǎn)擊文字判斷
if (downX > 0 && downX < mBaseTextPaddingLeft + mTextPaint.measureText("MACD") + 10) {
if (downY > mMainHeight && downY < mMainHeight + mVolumeTextHeight) {
Log.d("--->", "x = " + downX + ";" + "y = " + downY);
isCJL = !isCJL;
invalidate();
}
}
}
- 給子試圖和主視圖添加水印
/**
* 添加主視圖水印
*
* @param canvas
*/
public void drawMainViewLogo(Canvas canvas) {
if (mBitmapLogo != null) {
int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
int mTop = mMainHeight / 2 - mBitmapLogo.getHeight() / 2 + mTopPadding;
canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
}
}
/**
* 添加子視圖水印
*
* @param canvas
*/
public void drawChildViewLogo(Canvas canvas) {
if (mBitmapLogo != null) {
int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
int mTop = mMainHeight + mVolumeTextHeight + (mVolumeHeight / 2) - mBitmapLogo.getHeight() / 2 + mTopPadding;
canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
}
}
- 分時(shí)圖基類(lèi)
public abstract class BaseMinuteView extends View implements GestureDetector.OnGestureListener,
ScaleGestureDetector.OnScaleGestureListener {
protected int ONE_MINUTE = 60000;
protected int mMainHeight; //主視圖
protected int mWidth; //試圖寬度
//可修改
protected int mVolumeHeight; //子試圖高度
protected int mVolumeTextHeight = 20; //CJL高度
protected int mTopPadding = 1; //據(jù)頂部
protected int mBottomPadding = 15;//距底部
//左右padding,允許修改
protected int mBasePaddingLeft = 50;
protected int mBasePaddingRight = 50;
protected float mBaseTextPaddingLeft = 15; //字體據(jù)左側(cè)的距離
protected float mBaseTextPaddingRight = 15;//字體據(jù)右側(cè)的距離
protected int mBaseTimePadding = 5; //下方時(shí)間文字
protected int GridColumns = 5; //列數(shù)
protected int mGridRows = 6; //主試圖的行數(shù)
protected int mGridChildRows = 4; //子試圖的行數(shù)
protected float mPointWidth; //柱子單位量
protected boolean isLongPress = false; //是否長(zhǎng)按事件
protected boolean isClosePress = true; //關(guān)閉長(zhǎng)按時(shí)間
protected boolean mScaleEnable = false; //是否可以縮放
protected GestureDetectorCompat mDetector;
protected ScaleGestureDetector mScaleDetector;
protected boolean isDrawChildView = true; //是否子試圖點(diǎn)擊事件
protected long mClickTime = 0; //點(diǎn)擊時(shí)間
protected float mScaleY = 1; //Y軸單位量
protected float mScaleX = 1; //x軸的單位量
protected float mOldScale = 1.0f; //用來(lái)判斷當(dāng)前是否縮放
protected OnScaleGestureListener mOnScaleGestureListener;
protected long mTotalTime; //總時(shí)間
protected Paint mGridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
protected Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
protected int mBackgroundColor;
protected long mPointCount = 0; //點(diǎn)的個(gè)數(shù)
protected float mValueStart;//成交量起始值/昨收價(jià)
protected final List<IMinuteLine> mPoints = new ArrayList<>();
protected final List<IMinuteTime> mTimes = new ArrayList<>();
protected Date mMainStartTime;
protected Date mMainEndTime;
protected Date mStartTime;
protected Date mEndTime;
protected int mMACDClickPoint; //MACD選擇點(diǎn)
private Bitmap mBitmapLogo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.ic_app_logo);
private IValueFormatter mVolumeFormatter;
protected int mIndex = 0; //天數(shù)索引
public BaseMinuteView(Context context) {
super(context);
init();
}
public BaseMinuteView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BaseMinuteView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
protected void init() {
mDetector = new GestureDetectorCompat(getContext(), this);
mScaleDetector = new ScaleGestureDetector(getContext(), this);
mBackgroundPaint.setDither(true);
mBackgroundColor = Color.parseColor("#2A2D4F");
mBackgroundPaint.setColor(mBackgroundColor);
mGridPaint.setDither(true);
mGridPaint.setColor(Color.parseColor("#10FFFFFF")); //網(wǎng)格線顏色
mGridPaint.setStrokeWidth(dp2px(1));
mVolumeFormatter = new BigValueFormatter();
}
/**
* @param data 數(shù)據(jù)源
* @param startTime 顯示的開(kāi)始時(shí)間
* @param endTime 顯示的結(jié)束時(shí)間
* @param times 休息開(kāi)始時(shí)間
* @param yesClosePrice 昨收價(jià)
*/
public void initData(Collection<? extends IMinuteLine> data, Date startTime, Date endTime,
Collection<? extends IMinuteTime> times, float yesClosePrice, int index) {
this.mIndex = index;
if ((data == null && data.isEmpty()) || (times == null && times.isEmpty())) {
return;
}
if (times != null) {
mTimes.clear();
mTimes.addAll(times);
mTotalTime = 0;
}
this.mMainStartTime = startTime;
this.mMainEndTime = endTime;
if (mMainStartTime.getTime() >= mMainEndTime.getTime())
throw new IllegalStateException("開(kāi)始時(shí)間不能大于結(jié)束時(shí)間");
if (mTimes.size() != 0) {
for (int i = 0; i < mTimes.size(); i++) {
mStartTime = mTimes.get(i).getStartDate();
mEndTime = mTimes.get(i).getEndDate();
mTotalTime += mEndTime.getTime() - mStartTime.getTime();
}
} else {
mTotalTime = mMainEndTime.getTime() - mMainStartTime.getTime();
}
if (mMainStartTime != mTimes.get(0).getStartDate()){
mMainStartTime = mTimes.get(0).getStartDate();
}
setValueStart(yesClosePrice); //設(shè)置開(kāi)始的值(昨日成交量)
if (data != null) {
mPoints.clear();
this.mPoints.addAll(data);
mPointCount = mPoints.size();
mMACDClickPoint = mPoints.size() - 1;
}
notifyChanged();
}
/**
* 點(diǎn)擊, 處理長(zhǎng)安時(shí)間
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mClickTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
//一個(gè)點(diǎn)的時(shí)候滑動(dòng)
if (event.getPointerCount() == 1) {
//長(zhǎng)按之后移動(dòng)
if (isLongPress || !isClosePress) {
calculateSelectedX(event.getX());
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
if (!isClosePress) {
isLongPress = false;
}
mMACDClickPoint = mPoints.size() - 1;
// if (isDrawChildView && isClickPress) {
// //當(dāng)點(diǎn)擊時(shí)間小于2000ms時(shí),為交互時(shí)間
// if (System.currentTimeMillis() - mClickTime < 500) {
// float downX = event.getX();
// float downY = event.getY();
// jumpToCJLAndMACL(downX, downY);
// }
// }
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
if (!isClosePress) {
isLongPress = false;
}
mMACDClickPoint = mPoints.size() - 1;
invalidate();
break;
}
this.mDetector.onTouchEvent(event);
this.mScaleDetector.onTouchEvent(event);
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
int height = heightSpecSize - mTopPadding - mBottomPadding - mVolumeTextHeight;
if (isDrawChildView) {
this.mMainHeight = (int) (height * 0.75f);
this.mVolumeHeight = (int) (height * 0.25f);
} else {
this.mMainHeight = (int) (height * 1f);
this.mVolumeHeight = (int) (height * 0f);
}
this.mWidth = getMeasuredWidth();
notifyChanged();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int height = h - mTopPadding - mBottomPadding - mVolumeTextHeight;
if (isDrawChildView) {
this.mMainHeight = (int) (height * 0.75f);
this.mVolumeHeight = (int) (height * 0.25f);
} else {
this.mMainHeight = (int) (height * 1f);
this.mVolumeHeight = (int) (height * 0f);
}
this.mWidth = w;
notifyChanged();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {//橫豎屏切換
super.onConfigurationChanged(newConfig);
}
@Override
protected void onDraw(Canvas canvas) {
//繪制背景顏色
canvas.drawColor(mBackgroundColor);
if (mWidth == 0 || mMainHeight == 0) {
return;
}
drawMainViewLogo(canvas);
if (isDrawChildView) {
drawChildViewLogo(canvas);
}
drawGird(canvas); //繪制網(wǎng)格
if (mPoints == null || mPoints.size() == 0) {
return;
}
super.onDraw(canvas);
}
/**
* 添加主視圖水印
*
* @param canvas
*/
public void drawMainViewLogo(Canvas canvas) {
if (mBitmapLogo != null) {
int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
int mTop = mMainHeight / 2 - mBitmapLogo.getHeight() / 2 + mTopPadding;
canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
}
}
/**
* 添加子視圖水印
*
* @param canvas
*/
public void drawChildViewLogo(Canvas canvas) {
if (mBitmapLogo != null) {
int mLeft = getWidth() / 2 - mBitmapLogo.getWidth() / 2;
int mTop = mMainHeight + mVolumeTextHeight + (mVolumeHeight / 2) - mBitmapLogo.getHeight() / 2 + mTopPadding;
canvas.drawBitmap(mBitmapLogo, mLeft, mTop, null);
}
}
//繪制網(wǎng)格線
private void drawGird(Canvas canvas) {
//先畫(huà)出坐標(biāo)軸
canvas.translate(0, mTopPadding);
canvas.scale(1, 1);
//主視圖橫向的grid
float rowSpace = mMainHeight / mGridRows;
for (int i = 1; i <= mGridRows; i++) {
canvas.drawLine(0, rowSpace * i, mWidth, rowSpace * i, mGridPaint);
}
//主試圖縱向的grid
float columnSpace = (mWidth - mBasePaddingLeft - mBasePaddingRight) / GridColumns;
for (int i = 0; i <= GridColumns; i++) {
canvas.drawLine(columnSpace * i + mBasePaddingLeft, 0,
columnSpace * i + mBasePaddingLeft, mMainHeight, mGridPaint);
}
//中間軸線
// canvas.drawLine(0, rowSpace * mGridRows / 2, mWidth, rowSpace * mGridRows / 2, mGridPaint);
if (isDrawChildView) {
//子視圖橫向的grid
float rowChildSpace = mVolumeHeight / mGridChildRows;
for (int i = 0; i <= mGridChildRows; i++) {
canvas.drawLine(0, mMainHeight + rowChildSpace * i + mVolumeTextHeight, mWidth,
mMainHeight + rowChildSpace * i + mVolumeTextHeight, mGridPaint);
}
//子試圖縱向的grid
float columnChildSpace = (mWidth - mBasePaddingLeft - mBasePaddingRight) / GridColumns;
for (int i = 0; i <= GridColumns; i++) {
canvas.drawLine(columnChildSpace * i + mBasePaddingLeft, mMainHeight + mVolumeTextHeight, columnChildSpace * i + mBasePaddingLeft,
mMainHeight + mVolumeHeight + mVolumeTextHeight, mGridPaint);
}
}
}
/*********************************************抽象方法用于實(shí)現(xiàn)繼承類(lèi)**************************************/
protected abstract void notifyChanged();
protected abstract void calculateSelectedX(float x);
protected abstract void jumpToCJLAndMACL(float downX, float downY);
protected int dp2px(float dp) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
protected int sp2px(float spValue) {
final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* 設(shè)置開(kāi)始的值 對(duì)稱(chēng)軸線
*/
protected void setValueStart(float valueStart) {
this.mValueStart = valueStart;
}
/*********************************************可設(shè)置參數(shù)**************************************/
/**
* 設(shè)置表格行數(shù)
*/
public void setGridRows(int gridRows) {
if (gridRows < 1) {
gridRows = 1;
}
mGridRows = gridRows;
}
/**
* 設(shè)置子試圖表格行數(shù)
*/
public void setGridChildRows(int gridRows) {
if (gridRows < 1) {
gridRows = 1;
}
mGridChildRows = gridRows;
}
/**
* 設(shè)置表格列數(shù)
*/
public void setGridColumns(int gridColumns) {
if (gridColumns < 1) {
gridColumns = 1;
}
GridColumns = gridColumns;
}
//是否繪制子試圖
public void setDrawChildView(boolean b) {
isDrawChildView = b;
if (!isDrawChildView) {
mVolumeHeight = 0; //子試圖高度
mVolumeTextHeight = 0; //CJL高度
}
}
//設(shè)置子試圖高度
public void setVolumeHeight(int height) {
mVolumeHeight = dp2px(height);
}
//設(shè)置CJL高度
public void setVolumeTextHeight(int height) {
mVolumeTextHeight = dp2px(height);
}
//設(shè)置主試圖高度
public void setMainHeight(int height) {
mMainHeight = dp2px(height);
}
/**
* 獲取最大能有多少個(gè)點(diǎn)
*/
public long getMaxPointCount(int count) {
return mTotalTime / ONE_MINUTE;
}
/**
* 添加一個(gè)點(diǎn)
*/
public void addPoint(IMinuteLine point) {
mPoints.add(point);
notifyChanged();
}
/**
* 修改某個(gè)點(diǎn)的值
*
* @param position 索引值
*/
public void changePoint(int position, IMinuteLine point) {
mPoints.set(position, point);
notifyChanged();
}
/**
* 根據(jù)索引獲取點(diǎn)
*/
public IMinuteLine getItem(int position) {
return mPoints.get(position);
}
/**
* 獲取點(diǎn)的個(gè)數(shù)
*/
private int getItemSize() {
return mPoints.size();
}
/**
* 刷新最后一個(gè)點(diǎn)
*/
public void refreshLastPoint(IMinuteLine point) {
changePoint(getItemSize() - 1, point);
}
//是否可以縮放
public void setScaleEnable(boolean scaleEnable) {
this.mScaleEnable = scaleEnable;
}
/**
* 設(shè)置成交量格式化器
*
* @param volumeFormatter {@link IValueFormatter} 成交量格式化器
*/
public void setVolumeFormatter(IValueFormatter volumeFormatter) {
mVolumeFormatter = volumeFormatter;
}
//設(shè)置時(shí)間單位; 1代表一分鐘, 2代表兩分鐘
public void setOpenMinute(int number) {
ONE_MINUTE = number * 60000;
}
public void setLongPress(boolean longPress) {
isLongPress = longPress;
}
public void setClosePress(boolean closePress) {
isClosePress = closePress;
}
/******************************長(zhǎng)按,點(diǎn)擊手勢(shì)*****************************************/
// 單擊, 觸摸屏按下時(shí)立刻觸發(fā)
@Override
public boolean onDown(MotionEvent e) {
Log.i("--->", "onDown");
return false;
}
//短按, 觸摸屏按下后片刻后抬起,會(huì)觸發(fā)這個(gè)手勢(shì),如果迅速抬起則不會(huì)
@Override
public void onShowPress(MotionEvent e) {
}
//抬起, 手指離開(kāi)觸摸屏?xí)r觸發(fā)(長(zhǎng)按、滾動(dòng)、滑動(dòng)時(shí),不會(huì)觸發(fā)這個(gè)手勢(shì))
@Override
public boolean onSingleTapUp(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("--->", "ACTION_DOWN");
break;
case MotionEvent.ACTION_UP: //雙指點(diǎn)擊時(shí)不會(huì)觸發(fā)
Log.i("--->", "ACTION_UP");
if (isClosePress) {
if (isDrawChildView) {
//當(dāng)點(diǎn)擊時(shí)間小于2000ms時(shí),為交互時(shí)間
if (System.currentTimeMillis() - mClickTime < 500) {
float downX = e.getX();
float downY = e.getY();
jumpToCJLAndMACL(downX, downY);
}
}
} else {
isClosePress = true;
}
break;
case MotionEvent.ACTION_MOVE:
Log.i("--->", "ACTION_MOVE");
break;
}
return false;
}
//滾動(dòng), 觸摸屏按下后移動(dòng)
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
//長(zhǎng)按, 觸摸屏按下后既不抬起也不移動(dòng),過(guò)一段時(shí)間后觸發(fā)
@Override
public void onLongPress(MotionEvent e) {
isLongPress = true;
isClosePress = false;
calculateSelectedX(e.getX());
invalidate();
}
//滑動(dòng), 觸摸屏按下后快速移動(dòng)并抬起,會(huì)先觸發(fā)滾動(dòng)手勢(shì),跟著觸發(fā)一個(gè)滑動(dòng)手勢(shì)
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
/******************************縮放手勢(shì)*****************************************/
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (!isScaleEnable()) {
return false;
}
mOldScale = 1.0f;
float f = detector.getScaleFactor();
mOldScale *= f;
// if (mScaleX < mScaleXMin) {
// mScaleX = mScaleXMin;
// } else if (mScaleX > mScaleXMax) {
// mScaleX = mScaleXMax;
// } else {
// onScaleChanged(mScaleX, oldScale);
// }
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
if (isClosePress) {
if (mOldScale > 1.0f) { //放大
mOnScaleGestureListener.setLoseNumber();
} else { //收縮
mOnScaleGestureListener.setAddNumber();
}
}
}
protected void onScaleChanged(float scale, float oldScale) {
invalidate();
}
protected boolean isScaleEnable() {
return mScaleEnable;
}
public void setViewScaleGestureListener(OnScaleGestureListener listener) {
if (listener != null) {
this.mOnScaleGestureListener = listener;
}
}
public interface OnScaleGestureListener {
void setAddNumber();
void setLoseNumber();
}
}
GestureDetectorCompat 觸摸手勢(shì):主要是用來(lái)判斷單指點(diǎn)擊屏幕和雙指點(diǎn)擊屏幕從而來(lái)區(qū)分點(diǎn)擊切換子試圖還是進(jìn)行縮放操作。
**ScaleGestureDetector **主要根據(jù)onScale()、onScaleBegin()和onScaleEnd()這三個(gè)方法來(lái)判斷試圖是放大還是縮小。
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (!isScaleEnable()) {
return false;
}
mOldScale = 1.0f;
float f = detector.getScaleFactor();
mOldScale *= f;
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
if (isClosePress) {
if (mOldScale > 1.0f) { //放大
mOnScaleGestureListener.setLoseNumber();
} else { //收縮
mOnScaleGestureListener.setAddNumber();
}
}
}
- 分時(shí)圖重點(diǎn)實(shí)現(xiàn)試圖展示效果:
public class MinuteTimeView extends BaseMinuteView {
private Paint mAvgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //文字
private Paint mTextMACDPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //文字
private Paint mTextLeftPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //左邊文字
private Paint mTextReightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//右邊文字
private Paint mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); //軸線
private Paint mTextBottomPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //下邊文字
private Paint mPricePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mPriceAveragePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mVolumePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mSelectorBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mSelectorTitlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mSelectorTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float mValueMin; //最大值
private float mValueMax; //最小值
private float mVolume = 0; //成交量
private float mInterest = 0; //持倉(cāng)量
private float mVolumeMax = 0; //最大成交量
private float mVolumeMin = 0; //最小成交量
private float mInterestMax = 0; //最大持倉(cāng)量
private float mInterestMin = 0; //最小持倉(cāng)量
private float mMACDValueY = 0;//MACD Y軸
private long mCount = 0;
private float mVolumeTimeScaleY; //各時(shí)間點(diǎn)成交量縮放比
private float mInterestTimeScaleY; //各時(shí)間點(diǎn)持倉(cāng)量縮放比
private float mMACDLineScaleY; //MACD指標(biāo)線的縮放量
private float mTextSize = 10;
private int selectedIndex;
private boolean isCJL = true; //是否為CJL圖,默認(rèn)true
public MinuteTimeView(Context context) {
super(context);
initData();
}
public MinuteTimeView(Context context, AttributeSet attrs) {
super(context, attrs);
initData();
}
public MinuteTimeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData();
}
private void initData() {
setDrawChildView(true);
mTopPadding = dp2px(mTopPadding);
mBottomPadding = dp2px(mBottomPadding);
mTextSize = sp2px(mTextSize);
mVolumeHeight = dp2px(mVolumeHeight);
mVolumeTextHeight = dp2px(mVolumeTextHeight);
mTextPaint.setDither(true);
mTextPaint.setColor(Color.parseColor("#6774FF"));
mTextPaint.setTextSize(sp2px(11));
mTextPaint.setStrokeWidth(dp2px(0.5f));
mTextMACDPaint.setDither(true);
mTextMACDPaint.setColor(Color.parseColor("#6774FF"));
mTextMACDPaint.setTextSize(sp2px(10));
mTextMACDPaint.setStrokeWidth(dp2px(0.5f));
mTextLeftPaint.setDither(true);
mTextLeftPaint.setColor(Color.parseColor("#B1B2B6"));
mTextLeftPaint.setTextSize(mTextSize);
mTextLeftPaint.setStrokeWidth(dp2px(0.5f));
mTextReightPaint.setDither(true);
mTextReightPaint.setColor(Color.parseColor("#B1B2B6"));
mTextReightPaint.setTextSize(mTextSize);
mTextReightPaint.setStrokeWidth(dp2px(0.5f));
mTextBottomPaint.setDither(true);
mTextBottomPaint.setColor(Color.parseColor("#6A798E"));
mTextBottomPaint.setTextSize(mTextSize);
mTextBottomPaint.setStrokeWidth(dp2px(0.5f));
mLinePaint.setDither(true);
mLinePaint.setColor(Color.parseColor("#6774FF")); //軸線
mLinePaint.setTextSize(mTextSize);
mLinePaint.setStrokeWidth(dp2px(0.7f));
mAvgPaint.setDither(true);
mAvgPaint.setColor(Color.parseColor("#90A901"));
mAvgPaint.setStrokeWidth(dp2px(0.5f));
mAvgPaint.setTextSize(mTextSize);
mPricePaint.setDither(true);
mPricePaint.setColor(Color.parseColor("#FF6600"));
mPricePaint.setStrokeWidth(dp2px(0.5f));
mPricePaint.setTextSize(mTextSize);
mPriceAveragePaint.setDither(true);
mPriceAveragePaint.setColor(Color.parseColor("#EFD521"));
mPriceAveragePaint.setStrokeWidth(dp2px(0.5f));
mSelectorBackgroundPaint.setDither(true);
mSelectorBackgroundPaint.setColor(Color.parseColor("#4F5490"));
mSelectorTitlePaint.setDither(true);
mSelectorTitlePaint.setColor(Color.parseColor("#9EB2CD"));
mSelectorTitlePaint.setTextSize(sp2px(10));
mSelectorTextPaint.setDither(true);
mSelectorTextPaint.setColor(Color.parseColor("#E7EDF5"));
mSelectorTextPaint.setTextSize(sp2px(13));
mVolumePaint.setDither(true);
mVolumePaint.setColor(ContextCompat.getColor(getContext(), R.color.chart_red));
}
@Override
protected void calculateSelectedX(float x) {
// selectedIndex = (int) (x * 1f / getX(mPoints.size() - 1) * (mPoints.size() - 1) + mPointWidth * 0.2f);
selectedIndex = (int) (x * 1f / getX(mPoints.size() - 1) * (mPoints.size() - 1));
if (selectedIndex < 0) {
selectedIndex = 0;
}
if (selectedIndex > mPoints.size() - 1) {
selectedIndex = mPoints.size() - 1;
}
}
/**
* 根據(jù)索引獲取x的值
*/
private float getX(int position) {
mCount = 0;
if (mPoints.size() == 0 || mPoints == null || position == -1) {
return 0;
}
Long dateTime = mPoints.get(position).getDate().getTime();
for (int i = 0; i < mTimes.size(); i++) {
Long startTime = mTimes.get(i).getStartDate().getTime();
Long endTime = mTimes.get(i).getEndDate().getTime();
if (dateTime >= startTime && dateTime <= endTime) {
mCount += (dateTime - startTime) / ONE_MINUTE;
break;
} else {
mCount += (endTime - startTime) / ONE_MINUTE;
}
}
float c = mCount * mScaleX;
return mCount * mScaleX;
}
/**
* 當(dāng)數(shù)據(jù)發(fā)生變化時(shí)調(diào)用
*/
@Override
protected void notifyChanged() {
mMACDValueY = 0;
mValueMax = mValueMin = 0;
mVolumeMax = mVolume = mVolumeMin = 0;
mInterestMax = mInterest = mInterestMin = 0;
if (mPoints.size() <= 0) {
return;
}
if (mPoints.size() > 0) {
mValueMax = mPoints.get(0).getLast();
mValueMin = mPoints.get(0).getLast();
mVolumeMax = mPoints.get(0).getVolume();//成交量
mVolume = mPoints.get(0).getVolume();
mVolumeMin = mPoints.get(0).getVolume();
mInterestMax = mPoints.get(0).getInterest(); //持倉(cāng)量
mInterest = mPoints.get(0).getInterest();
mInterestMin = mPoints.get(0).getInterest();
}
for (int i = 0; i < mPoints.size(); i++) {
IMinuteLine point = mPoints.get(i);
mValueMax = Math.max(mValueMax, point.getLast());
mValueMin = Math.min(mValueMin, point.getLast());
mVolumeMax = Math.max(mVolumeMax, point.getVolume());
mVolumeMin = Math.max(mVolumeMin, point.getVolume());
mInterestMax = Math.max(mInterestMax, point.getInterest());
mInterestMin = Math.min(mInterestMin, point.getInterest());
//MACD Y軸
mMACDValueY = Math.max(mMACDValueY, Math.max(Math.abs(point.getDea()),
Math.max(Math.abs(point.getDiff()), Math.abs(point.getMacd()))));
Log.i("mMACDValueY :", mMACDValueY + "--; --" + i);
}
mMACDValueY = StrUtil.getFaveMultipleMinimum(Long.parseLong(StrUtil.getPositiveNumber(mMACDValueY)));
Log.i("mMACDValue :", mMACDValueY + "");
if (mValueMin > mValueStart) {
mValueStart = (mValueMax + mValueMin) / 2;
}
//最大值和開(kāi)始值的單位差值
float offsetValueMax = (Math.abs(mValueMax - mValueStart)) / (mGridRows / 2);
float offsetValueMin = (Math.abs(mValueStart - mValueMin)) / (mGridRows / 2);
//以開(kāi)始的點(diǎn)為中點(diǎn)值 上下間隙多出20%
float offset = (offsetValueMax > offsetValueMin ? offsetValueMax : offsetValueMin) * 1.2f;
//坐標(biāo)軸高度以開(kāi)始的點(diǎn)對(duì)稱(chēng)
mValueMax = mValueStart + offset * (mGridRows / 2);
mValueMin = mValueStart - offset * (mGridRows / 2);
//y軸的縮放值
mScaleY = mMainHeight / (mValueMax - mValueMin);
//判斷最大值和最小值是否一致
if (mValueMax == mValueMin) {
//當(dāng)最大值和最小值都相等的時(shí)候 分別增大最大值和 減小最小值
mValueMax += Math.abs(mValueMax * 0.05f);
mValueMin -= Math.abs(mValueMax * 0.05f);
if (mValueMax == 0) {
mValueMax = 1;
}
}
if (mVolumeMax == 0) {
mVolumeMax = 1;
}
//CJL左軸的縮放值
mVolumeMin = 0;
if (mVolumeMin < 0) {
mVolumeMin = 0;
}
mVolumeMax = StrUtil.getFaveMultipleMinimum((long) mVolumeMax);
mVolumeTimeScaleY = mVolumeHeight / Math.abs(mVolumeMax - mVolumeMin);
//CJL右軸的縮放值
float interest = Math.max(Math.abs(mInterestMax - mInterest), Math.abs(mInterest - mInterestMin));
mInterestMax = mInterest + interest;
mInterestMin = mInterest - interest;
if (mInterestMin < 0) {
mInterestMin = 0;
}
mInterestMax = StrUtil.getZeroMultipleMinimum((long) mInterestMax, 1);
mInterestMin = StrUtil.getZeroMultipleMinimum((long) mInterestMin, 2);
mInterestTimeScaleY = mVolumeHeight / Math.abs(mInterestMax - mInterestMin);
Log.i("---> InterestScaleY ", mInterestTimeScaleY + "");
//MACD右軸的縮放值
mMACDLineScaleY = mVolumeHeight / (2 * mMACDValueY);
//x軸的縮放值
mScaleX = (float) (mWidth - mBaseTimePadding * 2) / getMaxPointCount(1);
//設(shè)置主狀圖的寬度
mPointWidth = (float) (mWidth - mBaseTimePadding * 2) / getMaxPointCount(1);
mVolumePaint.setStrokeWidth(dp2px((float) 0.5));
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mWidth == 0 || mMainHeight == 0 || mPoints == null || mPoints.size() == 0) {
return;
}
//繪制平均線和成交價(jià)
if (mPoints.size() > 0) {
mPricePaint.setColor(getResources().getColor(R.color.chart_FFFFFF));
IMinuteLine lastPoint = mPoints.get(0);
float lastX = getX(0);
for (int i = 0; i < mPoints.size(); i++) {
IMinuteLine curPoint = mPoints.get(i);
float curX = getX(i);
canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
getY(lastPoint.getLast()),
curX + mBaseTimePadding - mScaleX / 2,
getY(curPoint.getLast()),
mPricePaint); //成交價(jià)
// canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
// getY(lastPoint.getAverage()),
// curX + mBaseTimePadding - mScaleX / 2,
// getY(curPoint.getAverage()),
// mPriceAveragePaint); //均價(jià)線
if (isDrawChildView) {
if (isCJL) {
//CJL成交量(柱狀圖)
if (curPoint.getOpen() < curPoint.getClose()) {
mVolumePaint.setColor(getResources().getColor(R.color.color_positive_value));
} else if (curPoint.getOpen() > curPoint.getClose()) {
mVolumePaint.setColor(getResources().getColor(R.color.color_negative_value));
} else if (curPoint.getOpen() == curPoint.getClose()) {
mVolumePaint.setColor(getResources().getColor(R.color.chart_text));
}
canvas.drawLine(curX + mBaseTimePadding - mPointWidth * 0.5f,
mMainHeight + mVolumeTextHeight + mVolumeHeight,
curX + mBaseTimePadding - mPointWidth * 0.5f,
getVolumeCJLY(curPoint.getVolume()),
mVolumePaint);
} else {
//MACD成交量(柱狀圖)
if (curPoint.getMacd() > 0) {
mVolumePaint.setColor(getResources().getColor(R.color.color_positive_value));
canvas.drawLine(curX + mBaseTimePadding - mPointWidth * 0.5f,
getMACDLineY(0),
curX + mBaseTimePadding - mPointWidth * 0.5f,
getMACDLineY(curPoint.getMacd()),
mVolumePaint);
} else if (curPoint.getMacd() < 0) {
mVolumePaint.setColor(getResources().getColor(R.color.color_negative_value));
canvas.drawLine(curX + mBaseTimePadding - mPointWidth * 0.5f,
getMACDLineY(0),
curX + mBaseTimePadding - mPointWidth * 0.5f,
getMACDLineY(curPoint.getMacd()),
mVolumePaint);
}
}
}
//給上一個(gè)只賦值
lastPoint = curPoint;
lastX = curX;
}
}
drawText(canvas); //繪制文字
//畫(huà)指示線
if (isLongPress || !isClosePress) {
if (selectedIndex >= mPoints.size() || selectedIndex < 0 || mPoints.size() == 0) {
return;
}
IMinuteLine point = mPoints.get(selectedIndex);
float x = getX(selectedIndex) + mBaseTimePadding - mPointWidth * 0.5f;
//軸線
canvas.drawLine(x, 0, x, mMainHeight + mVolumeHeight + mVolumeTextHeight, mLinePaint);//Y
// canvas.drawLine(0, getY(point.getLast()), mWidth, getY(point.getLast()), mLinePaint);//X
drawMainSelector(selectedIndex, point, canvas);
}
}
/**
* draw選擇器
*
* @param canvas
*/
private void drawMainSelector(int selectedIndex, IMinuteLine point, Canvas canvas) {
Paint.FontMetrics metrics = mTextLeftPaint.getFontMetrics();
float textHeight = metrics.descent - metrics.ascent;
float padding = DensityUtil.dp2px(5);
float margin = DensityUtil.dp2px(5);
float width = 0;
float left = 5;
float top = 10;
float bottom = 10;
List<String> strings = new ArrayList<>();
strings.add(DateUtil.getStringDateByLong(point.getDate().getTime(), 8));
strings.add("價(jià)格");
strings.add(point.getLast() + "");
strings.add("均價(jià)");
strings.add(point.getAverage() + "");
strings.add("漲跌");
strings.add(point.getUpdown());
strings.add(point.getPercent());
strings.add("持倉(cāng)量");
strings.add(point.getInterest() + "");
strings.add(point.getChgInterest());
strings.add("成交量");
strings.add(point.getVolume() + "");
strings.add(point.getChgVolume());//成交量變化
// strings.add("成交價(jià)");
// strings.add(point.getLast() + "");
for (int i = 0; i < strings.size(); i++) {
width = Math.max(width, mSelectorTextPaint.measureText(strings.get(i)));
}
width = width + dp2px(6) * 2;
float x = getX(selectedIndex);
if (x > mWidth / 2) {
left = margin;
} else {
left = mWidth - width - margin;
}
float height = top + padding * 2 + (textHeight - metrics.bottom - metrics.top) / 2 +
(textHeight + padding) * (strings.size() - 1);
RectF r = new RectF(left, top, left + width, top + height + bottom);
canvas.drawRoundRect(r, padding, padding, mSelectorBackgroundPaint);
float y = top + padding * 2 + (textHeight - metrics.bottom - metrics.top) / 2;
// if (x > mWidth / 2) {
// left = margin + padding;
// mSelectorTextPaint.setTextAlign(Paint.Align.LEFT);
// mSelectorTitlePaint.setTextAlign(Paint.Align.LEFT);
// } else {
// left = mWidth - margin - padding;
// mSelectorTextPaint.setTextAlign(Paint.Align.RIGHT);
// mSelectorTitlePaint.setTextAlign(Paint.Align.RIGHT);
// }
for (String s : strings) {
if (StrUtil.isTimeText(s)) {
mSelectorTextPaint.setColor(getResources().getColor(R.color.color_text_positive_paint));
canvas.drawText(s, left + padding, y, mSelectorTextPaint);
} else if (StrUtil.isChinaText(s)) {
canvas.drawText(s, left + padding, y, mSelectorTitlePaint);
} else {
if (StrUtil.isPositiveOrNagativeNumberText(s)) {
mSelectorTextPaint.setColor(getResources().getColor(R.color.color_negative_value));
canvas.drawText(s, left + padding, y, mSelectorTextPaint);
} else if (s.equals("- -")) {
mSelectorTextPaint.setColor(getResources().getColor(R.color.color_text_positive_paint));
canvas.drawText(s, left + padding, y, mSelectorTextPaint);
} else {
mSelectorTextPaint.setColor(getResources().getColor(R.color.color_text_positive_paint));
canvas.drawText(s, left + padding, y, mSelectorTextPaint);
}
}
y += textHeight + padding;
}
//繪制 MACD, DIFF, DEA, STICK 的指標(biāo)
if (isDrawChildView) {
//GJL
if (!isCJL) {
drawMACDText(selectedIndex, point, canvas);
invalidate();
}
}
}
private void drawText(Canvas canvas) {
Paint.FontMetrics fm = mTextLeftPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
float baseLine = (textHeight - fm.bottom - fm.top) / 2;
float rowValue = (mValueMax - mValueMin) / mGridRows;
float rowSpace = mMainHeight / mGridRows;
//畫(huà)左邊的值
mTextLeftPaint.setColor(getResources().getColor(R.color.color_positive_value));
canvas.drawText(StrUtil.getOneDecimals(mValueStart + rowValue * 3), mBaseTextPaddingLeft, baseLine, mTextLeftPaint); //繪制最大值
mTextLeftPaint.setColor(getResources().getColor(R.color.color_negative_value));
canvas.drawText(StrUtil.getOneDecimals(mValueStart - rowValue * 3), mBaseTextPaddingLeft, mMainHeight - textHeight + baseLine, mTextLeftPaint); //繪制最小值
for (int i = 0; i < 3; i++) {
if (i == 0) {
String text = StrUtil.getOneDecimals(mValueStart + rowValue * 1.5);
mTextLeftPaint.setColor(getResources().getColor(R.color.color_positive_value));
canvas.drawText(text, mBaseTextPaddingLeft, (float) (rowSpace * 1.5 + baseLine / 2), mTextLeftPaint);
} else if (i == 1) {
String text = StrUtil.getOneDecimals(mValueStart);
mTextLeftPaint.setColor(getResources().getColor(R.color.color_central_paint));
canvas.drawText(text, mBaseTextPaddingLeft, fixTextY(rowSpace * 3), mTextLeftPaint);
} else if (i == 2) {
String text = StrUtil.getOneDecimals(mValueStart - rowValue * 1.5);
mTextLeftPaint.setColor(getResources().getColor(R.color.color_negative_value));
canvas.drawText(text, mBaseTextPaddingLeft,
(float) (mMainHeight - textHeight / 2 - rowSpace * 1.5 + baseLine / 2), mTextLeftPaint);
}
}
//畫(huà)右邊的值
mTextReightPaint.setTextAlign(Paint.Align.RIGHT);
mTextReightPaint.setColor(getResources().getColor(R.color.color_positive_value));
String text = StrUtil.subAddAndSubMark(StrUtil.floatToString((mValueMax - mValueStart) * 100f / mValueStart, 2) + "%");
canvas.drawText("+" + text, mWidth - mBaseTextPaddingRight, baseLine, mTextReightPaint);
mTextReightPaint.setColor(getResources().getColor(R.color.color_negative_value));
text = StrUtil.subAddAndSubMark(StrUtil.floatToString(Math.abs(mValueMin - mValueStart) * 100f / mValueStart, 2) + "%");
canvas.drawText("-" + text, mWidth - mBaseTextPaddingRight,
mMainHeight - textHeight + baseLine, mTextReightPaint);
for (int i = 0; i < 3; i++) {
if (i == 0) {
text = StrUtil.subAddAndSubMark(StrUtil.floatToString((rowValue * 1.5f) * 100f / mValueStart, 2) + "%");
mTextReightPaint.setColor(getResources().getColor(R.color.color_positive_value));
canvas.drawText("+" + text, mWidth - mBaseTextPaddingRight,
(float) (rowSpace * 1.5 + baseLine / 2), mTextReightPaint);
} else if (i == 1) {
text = "0";
mTextReightPaint.setColor(getResources().getColor(R.color.color_central_paint));
canvas.drawText(text, mWidth - mBaseTextPaddingRight,
fixTextY(rowSpace * 3), mTextReightPaint);
} else if (i == 2) {
text = StrUtil.subAddAndSubMark(StrUtil.floatToString((rowValue * 1.5f) * 100f / mValueStart, 2) + "%");
mTextReightPaint.setColor(getResources().getColor(R.color.color_negative_value));
canvas.drawText("-" + text, mWidth - mBaseTextPaddingRight,
(float) (mMainHeight - textHeight / 2 - rowSpace * 1.5 + baseLine / 2), mTextReightPaint);
}
}
//畫(huà)時(shí)間
float y = mMainHeight + mVolumeHeight + mVolumeTextHeight + baseLine;
mTextBottomPaint.setTextAlign(Paint.Align.LEFT);
mCount = 0;
if (mIndex == 1) {
canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 8),
mBaseTimePadding, y, mTextBottomPaint); //起始時(shí)間
for (int i = 0; i < mTimes.size() - 1; i++) {
mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
float x = mScaleX * mCount;
String dataStr = DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 8);
if (dataStr.equals("04:30")) continue;
canvas.drawText(dataStr,
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 8)),
y, mTextBottomPaint); //中間時(shí)間
}
canvas.drawText(DateUtil.getStringDateByLong(mMainEndTime.getTime(), 8),
mWidth - mBaseTimePadding - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainEndTime.getTime(), 8)),
y, mTextBottomPaint);//結(jié)束時(shí)間
} else { //大于1天時(shí)
if (mTimes.size() / 4 == mIndex && mTimes.size() != mIndex) {
canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9), mBaseTimePadding, y, mTextBottomPaint); //起始時(shí)間
for (int i = 0; i < mTimes.size() - 1; i++) {
mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
float x = mScaleX * mCount;
String dataStr = DateUtil.getStringDateByLong(mTimes.get(i).getStartDate().getTime(), 8);
if (dataStr.equals("09:00")) { //上期所
canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i).getStartDate().getTime(), 9),
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
y, mTextBottomPaint);
}
}
} else if (mTimes.size() / 2 == mIndex && mTimes.size() != mIndex) {
canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9), mBaseTimePadding, y, mTextBottomPaint); //起始時(shí)間
for (int i = 0; i < mTimes.size() - 1; i++) {
if (i + 1 == mTimes.size()) return;
mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
float x = mScaleX * mCount;
String dataStr = DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 8);
if (dataStr.equals("09:30")) { // //上證指數(shù) (INDEX)
canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
y, mTextBottomPaint);
} else if (dataStr.equals("06:00")) { //上證指數(shù) (INDEX)
canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
y, mTextBottomPaint);
}
}
} else if (mTimes.size() == mIndex) {
canvas.drawText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9), mBaseTimePadding, y, mTextBottomPaint); //起始時(shí)間
for (int i = 0; i < mTimes.size() - 1; i++) {
if (i + 1 == mTimes.size()) return;
mCount += (mTimes.get(i).getEndDate().getTime() - mTimes.get(i).getStartDate().getTime()) / ONE_MINUTE;
float x = mScaleX * mCount;
String dataStr = DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 8);
if (dataStr.equals("08:00")) { //LME
canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
y, mTextBottomPaint);
} else if (dataStr.equals("21:30")) { //上證指數(shù) (INDEX)
canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
y, mTextBottomPaint);
} else if (dataStr.equals("09:30")) { //外匯 (INDEX)
canvas.drawText(DateUtil.getStringDateByLong(mTimes.get(i + 1).getStartDate().getTime(), 9),
mBaseTimePadding + x - mTextBottomPaint.measureText(DateUtil.getStringDateByLong(mMainStartTime.getTime(), 9)) / 3,
y, mTextBottomPaint);
}
}
}
}
if (isDrawChildView) {
//GJL
if (isCJL) {
drawCJL(canvas);
} else {
drawMACD(canvas);
}
}
}
//MACD , 存在問(wèn)題
private void drawMACD(Canvas canvas) {
//上面文字
mTextPaint.setColor(getResources().getColor(R.color.color_child_text));
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
float baseLine = (textHeight - fm.bottom - fm.top) / 2;
mTextPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("MACD", mBaseTextPaddingLeft, mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextPaint); //存在問(wèn)題??
//繪制 MACD, DIFF, DEA, STICK 的指標(biāo)
drawMACDText(0, mPoints.get(mMACDClickPoint), canvas);
//左邊文字
float rowChildSpace = mVolumeHeight / mGridChildRows;
mTextPaint.setColor(getResources().getColor(R.color.color_candle_paint));
canvas.drawText(StrUtil.getPositiveNumber(mMACDValueY), mBaseTextPaddingLeft,
mMainHeight + baseLine + mVolumeTextHeight, mTextPaint); //繪制最大值
canvas.drawText("0", mBaseTextPaddingLeft,
mMainHeight + mVolumeTextHeight + fixTextY(rowChildSpace * 2), mTextPaint); //中間值
canvas.drawText(StrUtil.getPositiveNumber(-mMACDValueY), mBaseTextPaddingLeft,
mMainHeight - textHeight + baseLine + mVolumeTextHeight + mVolumeHeight, mTextPaint); //繪制最小值
//DIFF線
if (mPoints.size() > 0) {
mPricePaint.setColor(getResources().getColor(R.color.chart_FFFFFF));
mPricePaint.setStyle(Paint.Style.STROKE);
IMinuteLine lastPoint = mPoints.get(0);
float lastX = getX(0);
for (int i = 0; i < mPoints.size(); i++) {
IMinuteLine curPoint = mPoints.get(i);
float curX = getX(i);
// Log.i("diff --> :" , curPoint.getDiff() + "");
canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
getMACDLineY(lastPoint.getDiff()),
curX + mBaseTimePadding - mScaleX / 2,
getMACDLineY(curPoint.getDiff()),
mPricePaint); //成交價(jià)
//給上一個(gè)只賦值
lastPoint = curPoint;
lastX = curX;
}
}
//DEA線
if (mPoints.size() > 0) {
mPricePaint.setColor(getResources().getColor(R.color.color_cjl_line_paint));
IMinuteLine lastPoint = mPoints.get(0);
float lastX = getX(0);
for (int i = 0; i < mPoints.size(); i++) {
IMinuteLine curPoint = mPoints.get(i);
float curX = getX(i);
// Log.i("dea --> :" , curPoint.getDea() + "");
canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
getMACDLineY(lastPoint.getDea()),
curX + mBaseTimePadding - mScaleX / 2,
getMACDLineY(curPoint.getDea()),
mPricePaint); //成交價(jià)
//給上一個(gè)只賦值
lastPoint = curPoint;
lastX = curX;
}
}
}
//繪制 MACD, DIFF, DEA, STICK 的指標(biāo)
private void drawMACDText(int selectedIndex, IMinuteLine point, Canvas canvas) {
mMACDClickPoint = selectedIndex;
Paint.FontMetrics fm = mTextMACDPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
float baseLine = (textHeight - fm.bottom - fm.top) / 2;
float x = dp2px(6) + mTextPaint.measureText("MACD ") + mBaseTextPaddingLeft;
float countX = (mWidth - x) / 4;
mTextMACDPaint.setTextAlign(Paint.Align.LEFT);
mTextMACDPaint.setColor(getResources().getColor(R.color.color_macd_text));
canvas.drawText("MACD(12,26,9)", x,
mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //MACD
mTextMACDPaint.setColor(getResources().getColor(R.color.color_diff_text));
canvas.drawText("DIFF:" + StrUtil.floatToString(point.getDiff(), 2), x + countX,
mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //DIFF
mTextMACDPaint.setColor(getResources().getColor(R.color.color_dea_text));
canvas.drawText("DEA:" + StrUtil.floatToString(point.getDea(), 2), x + countX * 2,
mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //DEA
mTextMACDPaint.setColor(getResources().getColor(R.color.c6774FF));
canvas.drawText("STICK:" + StrUtil.floatToString(point.getMacd(), 2), x + countX * 3,
mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextMACDPaint); //STICK
}
//GJL
private void drawCJL(Canvas canvas) {
//上面文字
mTextPaint.setColor(getResources().getColor(R.color.color_child_text));
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
float baseLine = (textHeight - fm.bottom - fm.top) / 2;
mTextPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("CJL", mBaseTextPaddingLeft, mMainHeight + mVolumeTextHeight / 2 + textHeight - baseLine, mTextPaint);//存在問(wèn)題 ???
float rowChildSpace = mVolumeHeight / mGridChildRows;
if (mVolumeMax != mVolumeMin) {
//左邊文字
mTextPaint.setColor(getResources().getColor(R.color.color_candle_paint));
canvas.drawText(StrUtil.getPositiveNumber(mVolumeMax) + "",
mBaseTextPaddingLeft, mMainHeight + baseLine + mVolumeTextHeight, mTextPaint); //繪制最大值
String text = StrUtil.getPositiveNumber((mVolumeMax - mVolumeMin) / 2);
canvas.drawText(text, mBaseTextPaddingLeft,
mMainHeight + mVolumeTextHeight + fixTextY(rowChildSpace * 2), mTextPaint); //中間值
canvas.drawText(StrUtil.getFaveMultipleMinimum((long) mVolumeMin) + "", mBaseTextPaddingLeft,
mMainHeight - textHeight + baseLine + mVolumeTextHeight + mVolumeHeight, mTextPaint); //繪制最小值
}
//GJL線
if (mPoints.size() > 0) {
mPricePaint.setColor(getResources().getColor(R.color.color_cjl_line_paint));
IMinuteLine lastPoint = mPoints.get(0);
float lastX = getX(0);
for (int i = 0; i < mPoints.size(); i++) {
IMinuteLine curPoint = mPoints.get(i);
float curX = getX(i);
canvas.drawLine(lastX + mBaseTimePadding - mScaleX / 2,
getInterestCJLY(lastPoint.getInterest()),
curX + mBaseTimePadding - mScaleX / 2,
getInterestCJLY(curPoint.getInterest()),
mPricePaint); //成交價(jià)
//給上一個(gè)只賦值
lastPoint = curPoint;
lastX = curX;
}
}
if (mInterestMax != mInterestMin) {
//右邊文字
mTextPaint.setTextAlign(Paint.Align.RIGHT);
mTextPaint.setColor(getResources().getColor(R.color.color_candle_paint));
canvas.drawText(StrUtil.getPositiveNumber((long) mInterestMax) + "", mWidth - mBaseTextPaddingRight,
mMainHeight + baseLine + mVolumeTextHeight, mTextPaint); //繪制最大值
String text1 = StrUtil.getPositiveNumber(mInterest);
canvas.drawText(text1, mWidth - mBaseTextPaddingRight,
mMainHeight + mVolumeTextHeight + fixTextY(rowChildSpace * 2), mTextPaint); //中間值
canvas.drawText(StrUtil.getPositiveNumber((long) mInterestMin) + "",
mWidth - mBaseTextPaddingRight,
mMainHeight - textHeight + baseLine + mVolumeTextHeight + mVolumeHeight, mTextPaint); //繪制最小值
}
}
//CJL和MACL之間的切換
@Override
protected void jumpToCJLAndMACL(float downX, float downY) {
//點(diǎn)擊子試圖判斷
if (downX > 0 && downX < mWidth) {
if (downY > mMainHeight + mVolumeTextHeight && downY < mMainHeight + mVolumeTextHeight + mVolumeHeight) {
Log.d("--->", "x = " + downX + ";" + "y = " + downY);
isCJL = !isCJL;
invalidate();
}
}
//點(diǎn)擊文字判斷
if (downX > 0 && downX < mBaseTextPaddingLeft + mTextPaint.measureText("MACD") + 10) {
if (downY > mMainHeight && downY < mMainHeight + mVolumeTextHeight) {
Log.d("--->", "x = " + downX + ";" + "y = " + downY);
isCJL = !isCJL;
invalidate();
}
}
}
/**
* 修正y值
*/
private float getY(float value) {
return (mValueMax - value) * mScaleY;
}
//CJL柱形
private float getVolumeCJLY(float value) {
return mMainHeight + mVolumeTextHeight + mVolumeHeight - (value - mVolumeMin) * mVolumeTimeScaleY;
}
//CJL線
private float getInterestCJLY(float value) {
return mMainHeight + mVolumeTextHeight + mVolumeHeight - (value - mInterestMin) * mInterestTimeScaleY;
}
//MACD指標(biāo)線
private float getMACDLineY(float value) {
if (value == 0) {
return mMainHeight + mVolumeTextHeight + mVolumeHeight / 2;
} else if (value > 0) {
return mMainHeight + mVolumeTextHeight + mVolumeHeight / 2 - value * mMACDLineScaleY;
} else if (value < 0) {
return mMainHeight + mVolumeTextHeight + mVolumeHeight / 2 - value * mMACDLineScaleY;
} else {
return 0;
}
}
/**
* 解決text居中的問(wèn)題
*/
public float fixTextY(float y) {
Paint.FontMetrics fontMetrics = mTextLeftPaint.getFontMetrics();
return (y + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent);
}
}
具體詳解我放在Android原生股票圖-分時(shí)圖講解和繪制(二)中。
源碼

