dataList, boolean isNeedReqPre) {
if (dataList == null || dataList.isEmpty() || totalDataList == null) {
return;
}
isNeedRequestPreData = isNeedReqPre;
totalDataList.addAll(0, dataList);
startDataNum += dataList.size();
if (quotaThread != null) {
quotaThread.quotaListCalculate(totalDataList);
}
}
/**
* 分页加载,向前期滑动时,进行分页加载添加数据,建议每次添加数据在2000条左右
* 配合setOnRequestDataListListener接口使用实现自动分页加载
* 首次调用该方法后会记录该次list.size,后续继续调用时会将传进来的list.size与首次
* 的进行比较,如果比首次的size小,则判定为数据已拿完,不再自动调用接口请求数据。
*
* ---该方法仅在能保证每次分页加载拿到的数据list.size相同的情况下调用,否则,请调用
* 上面的方法,手动传入isNeedReqPre用来判断是否需要继续自动调用接口请求数据
*/
public void addPreDataList(List dataList) {
if (dataList == null || dataList.isEmpty() || totalDataList == null) {
return;
}
if (initTotalListSize == 0) {
initTotalListSize = dataList.size();
}
isNeedRequestPreData = dataList.size() >= initTotalListSize;
totalDataList.addAll(0, dataList);
startDataNum += dataList.size();
if (quotaThread != null) {
quotaThread.quotaListCalculate(totalDataList);
}
}
/**
* 重置所有数据,默认不作定位
*/
public void resetDataList(List dataList) {
if (dataList == null || dataList.isEmpty()){
return;
}
resetDataList(dataList, false);
}
/**
* 重置所有数据
*
* @param isNeedLocateCurrent 重置后的数据是否需要定位到重置前移动到的时间点,例如:
* 重置前已经滑动到9月20号,true则在重置后会将新数据定位到9月20号。
* false则不作定位,view右边直接显示为最新的数据
*/
public void resetDataList(List dataList, boolean isNeedLocateCurrent) {
if (dataList == null || dataList.isEmpty() || viewDataList == null || totalDataList == null) {
return;
}
long currentStartTime = 0;
if (viewDataList.size() > 0) {
currentStartTime = viewDataList.get(0).getTime();
}
isShowDetail = false;
this.totalDataList.clear();
this.totalDataList.addAll(dataList);
QuotaUtil.initMa(totalDataList, false);
switch (mainImgType) {
case MAIN_IMG_EMA:
QuotaUtil.initEma(totalDataList, false);
break;
case MAIN_IMG_BOLL:
QuotaUtil.initBoll(totalDataList, false);
break;
default:
break;
}
switch (deputyImgType) {
case DEPUTY_IMG_MACD:
QuotaUtil.initMACD(totalDataList, false);
break;
case DEPUTY_IMG_KDJ:
QuotaUtil.initKDJ(totalDataList, false);
break;
case DEPUTY_IMG_RSI:
QuotaUtil.initRSI(totalDataList, false);
break;
default:
break;
}
if (isNeedLocateCurrent) {
int halfSizeNum = totalDataList.size() / 2;
startDataNum = -1;
if (totalDataList.get(0).getTime() <= currentStartTime
&& totalDataList.get(halfSizeNum).getTime() > currentStartTime) {
for (int i = 0; i < halfSizeNum; i++) {
if (i + 1 < totalDataList.size() && totalDataList.get(i).getTime() <= currentStartTime
&& totalDataList.get(i + 1).getTime() > currentStartTime) {
startDataNum = i;
break;
}
}
} else if (totalDataList.get(halfSizeNum).getTime() <= currentStartTime
&& totalDataList.get(totalDataList.size() - 1).getTime() >= currentStartTime) {
for (int i = halfSizeNum; i < totalDataList.size(); i++) {
if (i + 1 < totalDataList.size() && totalDataList.get(i).getTime() <= currentStartTime
&& totalDataList.get(i + 1).getTime() > currentStartTime) {
startDataNum = i;
break;
}
}
}
if (totalDataList.size() < maxViewDataNum) {
startDataNum = 0;
} else if (totalDataList.size() - startDataNum < maxViewDataNum || startDataNum == -1) {
startDataNum = totalDataList.size() - maxViewDataNum;
}
} else {
if (totalDataList.size() < maxViewDataNum) {
startDataNum = 0;
} else {
startDataNum = totalDataList.size() - maxViewDataNum;
}
}
resetViewData();
}
/**
* 设置主图显示类型
* MA: MAIN_IMG_MA
* EMA: MAIN_IMG_EMA
* BOLL: MAIN_IMG_BOLL
*/
public void setMainImgType(int type) {
switch (type) {
case MAIN_IMG_MA:
QuotaUtil.initMa(totalDataList, false);
break;
case MAIN_IMG_EMA:
QuotaUtil.initEma(totalDataList, false);
break;
case MAIN_IMG_BOLL:
QuotaUtil.initBoll(totalDataList, false);
break;
default:
break;
}
this.mainImgType = type;
invalidate();
}
/**
* 是否显示副图
*/
public void setDeputyPicShow(boolean showState) {
this.isShowDeputy = showState;
if (isShowDeputy) {
setDeputyImgType(deputyImgType);
}
invalidate();
}
/**
* 设置副图显示类型
* MACD: DEPUTY_IMG_MACD
* KDJ: DEPUTY_IMG_KDJ
* RSI: DEPUTY_IMG_RSI
*/
public void setDeputyImgType(int type) {
this.deputyImgType = type;
switch (deputyImgType) {
case DEPUTY_IMG_MACD:
QuotaUtil.initMACD(totalDataList, false);
break;
case DEPUTY_IMG_KDJ:
QuotaUtil.initKDJ(totalDataList, false);
break;
case DEPUTY_IMG_RSI:
QuotaUtil.initRSI(totalDataList, false);
break;
default:
break;
}
invalidate();
}
/**
* 设置十字线的横线上下移动模式
* 固定指向收盘价: CROSS_HAIR_MOVE_CLOSE
* 固定指向开盘价: CROSS_HAIR_MOVE_OPEN
* 上下自由滑动: CROSS_HAIR_MOVE_FREE
*/
public void setCrossHairMoveMode(int moveMode) {
this.crossHairMoveMode = moveMode;
}
/**
* 获取副图是否显示
*/
public boolean getVicePicShow() {
return this.isShowDeputy;
}
/**
* 退出页面时停止子线程并置空,便于回收,避免内存泄露
*/
public void cancelQuotaThread() {
if (quotaThread != null) {
quotaThread.setUIHandler(null);
quotaThread.quit();
quotaThread = null;
}
removeCallbacks(mDelayRunnable);
removeCallbacks(longPressRunnable);
}
/**
* 是否显示分时图
*/
public void setShowInstant(boolean state){
this.isShowInstant = state;
invalidate();
}
/**
* 获取分时图是否显示
*/
public boolean isShowInstant(){
return this.isShowInstant;
}
private void initAttrs(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.KLineView);
tickMarkCol = typedArray.getColor(R.styleable.KLineView_klTickMarkLineCol, 0xffF7F7FB);
abscissaTextCol = typedArray.getColor(R.styleable.KLineView_klAbscissaTextCol, 0xff9BACBD);
abscissaTextSize = typedArray.getInt(R.styleable.KLineView_klAbscissaTextSize, 8);
ordinateTextCol = typedArray.getColor(R.styleable.KLineView_klOrdinateTextCol, abscissaTextCol);
ordinateTextSize = typedArray.getInt(R.styleable.KLineView_klOrdinateTextSize, abscissaTextSize);
topMaTextSize = typedArray.getInt(R.styleable.KLineView_klTopMaTextSize, 10);
priceIncreaseCol = typedArray.getColor(R.styleable.KLineView_klPriceIncreaseCol, 0xffFF5442);
priceFallCol = typedArray.getColor(R.styleable.KLineView_klPriceFallCol, 0xff2BB8AB);
priceMa5Col = typedArray.getColor(R.styleable.KLineView_klPriceMa5LineCol, 0xffFFA800);
priceMa10Col = typedArray.getColor(R.styleable.KLineView_klPriceMa10LineCol, 0xff2668FF);
priceMa30Col = typedArray.getColor(R.styleable.KLineView_klPriceMa30LineCol, 0xffFF45A1);
priceMaxLabelCol = typedArray.getColor(R.styleable.KLineView_klPriceMaxLabelCol, 0xffC0C6C9);
priceMaxLabelTextCol = typedArray.getColor(R.styleable.KLineView_klPriceMaxLabelTextCol, 0xffffffff);
priceMaxLabelTextSize = typedArray.getInt(R.styleable.KLineView_klPriceMaxLabelTextSize, 10);
priceMinLabelCol = typedArray.getColor(R.styleable.KLineView_klPriceMinLabelCol, priceMaxLabelCol);
priceMinLabelTextCol = typedArray.getColor(R.styleable.KLineView_klPriceMinLabelTextCol, priceMaxLabelTextCol);
priceMinLabelTextSize = typedArray.getInt(R.styleable.KLineView_klPriceMinLabelTextSize, 10);
volumeTextCol = typedArray.getColor(R.styleable.KLineView_klVolumeTextCol, 0xff9BACBD);
volumeTextSize = typedArray.getInt(R.styleable.KLineView_klVolumeTextSize, 10);
volumeMa5Col = typedArray.getColor(R.styleable.KLineView_klVolumeMa5LineCol, priceMa5Col);
volumeMa10Col = typedArray.getColor(R.styleable.KLineView_klVolumeMa10LineCol, priceMa10Col);
macdTextCol = typedArray.getColor(R.styleable.KLineView_klMacdTextCol, volumeTextCol);
macdPositiveCol = typedArray.getColor(R.styleable.KLineView_klMacdPositiveCol, priceIncreaseCol);
macdNegativeCol = typedArray.getColor(R.styleable.KLineView_klMacdNegativeCol, priceFallCol);
difLineCol = typedArray.getColor(R.styleable.KLineView_klDifLineCol, priceMa10Col);
deaLineCol = typedArray.getColor(R.styleable.KLineView_klDeaLineCol, priceMa30Col);
kLineCol = typedArray.getColor(R.styleable.KLineView_klKLineCol, priceMa5Col);
dLineCol = typedArray.getColor(R.styleable.KLineView_klDLineCol, priceMa10Col);
jLineCol = typedArray.getColor(R.styleable.KLineView_klJLineCol, priceMa30Col);
crossHairCol = typedArray.getColor(R.styleable.KLineView_klCrossHairCol, 0xff828EA2);
crossHairRightLabelCol = typedArray.getColor(R.styleable.KLineView_klCrossHairRightLabelCol, 0xff3193FF);
crossHairRightLabelTextCol = typedArray.getColor(R.styleable.KLineView_klCrossHairRightLabelTextCol, 0xffffffff);
crossHairRightLabelTextSize = typedArray.getInt(R.styleable.KLineView_klCrossHairRightLabelTextSize, 10);
crossHairBottomLabelCol = typedArray.getColor(R.styleable.KLineView_klCrossHairBottomLabelCol, priceMaxLabelCol);
crossHairBottomLabelTextCol = typedArray.getColor(R.styleable.KLineView_klCrossHairBottomLabelTextCol, 0xffffffff);
crossHairBottomLabelTextSize = typedArray.getInt(R.styleable.KLineView_klCrossHairBottomLabelTextSize, 8);
detailFrameCol = typedArray.getColor(R.styleable.KLineView_klDetailFrameCol, 0xffB5C0D0);
detailTextCol = typedArray.getColor(R.styleable.KLineView_klDetailTextCol, 0xff808F9E);
detailTextSize = typedArray.getInt(R.styleable.KLineView_klDetailTextSize, 10);
detailBgCol = typedArray.getColor(R.styleable.KLineView_klDetailBgCol, 0xe6ffffff);
typedArray.recycle();
}
}
private void initData() {
super.setOnTouchListener(this);
super.setClickable(true);
super.setFocusable(true);
gestureDetector = new GestureDetector(getContext(), new CustomGestureListener());
moveLimitDistance = ViewConfiguration.get(getContext()).getScaledTouchSlop();
detailRectWidth = dp2px(103);
detailRectHeight = dp2px(120);
detailTextVerticalSpace = (detailRectHeight - dp2px(4)) / 8;
detailLeftTitleArr = new String[]{"时间", "开", "高", "低", "收", "涨跌额", "涨跌幅", "成交量"};
initQuotaThread();
initStopDelay();
strokePaint = new Paint();
strokePaint.setAntiAlias(true);
strokePaint.setTextSize(sp2px(abscissaTextSize));
strokePaint.setStyle(Paint.Style.STROKE);
fillPaint = new Paint();
fillPaint.setAntiAlias(true);
fillPaint.setStyle(Paint.Style.FILL);
instantFillPaint = new Paint();
instantFillPaint.setAntiAlias(true);
instantFillPaint.setStyle(Paint.Style.FILL);
curvePath = new Path();
instantPath = new Path();
longPressRunnable = new Runnable() {
@Override
public void run() {
isLongPress = true;
isShowDetail = true;
getClickKData(longPressDownX);
invalidate();
}
};
}
private void initQuotaThread() {
Handler uiHandler = new Handler(this);
quotaThread = new QuotaThread("quotaThread", Process.THREAD_PRIORITY_BACKGROUND);
quotaThread.setUIHandler(uiHandler);
quotaThread.start();
}
@Override
public boolean handleMessage(Message msg) {
if (msg.what == QuotaThread.HANDLER_QUOTA_LIST) {
invalidate();
} else if (msg.what == QuotaThread.HANDLER_QUOTA_SINGLE) {
if (endDataList == null || totalDataList == null) {
return false;
}
KData endLastData = endDataList.get(endDataList.size() - 1);
int totalSize = totalDataList.size();
KData totalLastData = totalDataList.get(totalSize - 1);
if (endLastData.getTime() == totalLastData.getTime()) {
totalDataList.remove(totalSize - 1);
}
totalDataList.add(endLastData);
if (totalSize >= maxViewDataNum
&& startDataNum == totalSize - maxViewDataNum - 1) {
startDataNum++;
resetViewData();
} else {
resetViewData();
}
}
return false;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
leftStart = getPaddingLeft();
topStart = getPaddingTop();
rightEnd = getMeasuredWidth() - getPaddingRight();
bottomEnd = getMeasuredHeight() - getPaddingBottom();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (totalDataList.isEmpty() || viewDataList.isEmpty()) {
return;
}
resetData();
drawTickMark(canvas);
drawAbscissa(canvas);
drawOrdinate(canvas);
drawCrossHairLine(canvas);
drawVolume(canvas);
if (isShowInstant){
crossHairMoveMode = CROSS_HAIR_MOVE_CLOSE;
drawInstant(canvas);
} else {
drawMainDeputyRect(canvas);
drawBezierCurve(canvas);
drawTopPriceMAData(canvas);
drawBotMAData(canvas);
drawMaxMinPriceLabel(canvas);
drawDetailData(canvas);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
longPressDownX = event.getX();
longPressDownY = event.getY();
dispatchDownX = event.getX();
dispatchDownY = event.getY();
isLongPress = false;
postDelayed(longPressRunnable, LONG_PRESS_TIME_OUT);
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
//长按控制
float diffDispatchMoveX = Math.abs(event.getX() - longPressDownX);
float diffDispatchMoveY = Math.abs(event.getY() - longPressDownY);
float moveDistanceX = Math.abs(event.getX() - dispatchDownX);
float moveDistanceY = Math.abs(event.getY() - dispatchDownY);
longPressMoveY = event.getY();
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (isHorizontalMove || (diffDispatchMoveX > diffDispatchMoveY + dp2px(5)
&& diffDispatchMoveX > moveLimitDistance)
|| (isLongPress && diffDispatchMoveY > moveLimitDistance)) {
isHorizontalMove = true;
removeCallbacks(longPressRunnable);
if (isLongPress && (moveDistanceX > 1 || moveDistanceY > 1)) {
getClickKData(event.getX());
if (lastKData != null) {
invalidate();
}
}
dispatchDownX = event.getX();
dispatchDownY = event.getY();
return isLongPress || super.dispatchTouchEvent(event);
} else if (!isLongPress && !isHorizontalMove && !isDoubleFinger
&& diffDispatchMoveY > diffDispatchMoveX + dp2px(5)
&& diffDispatchMoveY > moveLimitDistance) {
removeCallbacks(longPressRunnable);
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(false);
}
return false;
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
isHorizontalMove = false;
removeCallbacks(longPressRunnable);
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(false);
}
}
return isLongPress || super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
singleClickDownX = event.getX();
singleClickDownY = event.getY();
flingVelocityX = 0;
mulFirstDownX = event.getX(0);
mulFirstDownY = event.getY(0);
break;
case MotionEvent.ACTION_POINTER_DOWN:
isShowDetail = false;
isDoubleFinger = true;
removeCallbacks(longPressRunnable);
mulSecondDownX = event.getX(1);
float mulSecondDownY = event.getY(1);
lastDiffMoveX = Math.abs(mulSecondDownX - mulFirstDownX);
lastDiffMoveY = Math.abs(mulSecondDownY - mulFirstDownY);
break;
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() > 1) {
float mulFirstMoveX = event.getX(0);
float mulFirstMoveY = event.getY(0);
float mulSecondMoveX = event.getX(1);
float mulSecondMoveY = event.getY(1);
float diffMoveX = Math.abs(mulSecondMoveX - mulFirstMoveX);
float diffMoveY = Math.abs(mulSecondMoveY - mulFirstMoveY);
//双指分开,放大显示
if ((diffMoveX >= diffMoveY && diffMoveX - lastDiffMoveX > 1)
|| (diffMoveY >= diffMoveX && diffMoveY - lastDiffMoveY > 1)) {
if (maxViewDataNum <= VIEW_DATA_NUM_MIN) {
maxViewDataNum = VIEW_DATA_NUM_MIN;
//如果view中显示的数据量小于当前最大数据条数,则不管双指如何落点,都向左放大
} else if (viewDataList != null && viewDataList.size() < maxViewDataNum) {
maxViewDataNum -= 2;
startDataNum = totalDataList.size() - maxViewDataNum;
//如果双指起始落点都在中线左侧,则左边不动,放大右边
} else if (verticalXList != null && mulFirstDownX < verticalXList.get(2)
&& mulSecondDownX <= verticalXList.get(2)) {
maxViewDataNum -= 2;
//如果双指起始落点在中线左右两侧,则左右同时放大
} else if ((verticalXList != null && mulFirstDownX <= verticalXList.get(2)
&& mulSecondDownX >= verticalXList.get(2))
|| (verticalXList != null && mulFirstDownX >= verticalXList.get(2)
&& mulSecondDownX <= verticalXList.get(2))) {
maxViewDataNum -= 2;
startDataNum += 1;
//如果双指起始落点在中线右侧,则右边不动,放大左边
} else if (verticalXList != null && mulFirstDownX >= verticalXList.get(2)
&& mulSecondDownX > verticalXList.get(2)) {
maxViewDataNum -= 2;
startDataNum += 2;
}
resetViewData();
//双指靠拢,缩小显示
} else if ((diffMoveX >= diffMoveY && diffMoveX - lastDiffMoveX < -1)
|| (diffMoveY >= diffMoveX && diffMoveY - lastDiffMoveY < -1)) {
if (maxViewDataNum >= VIEW_DATA_NUM_MAX) {
maxViewDataNum = VIEW_DATA_NUM_MAX;
//如果view显示的数据是totalDataList最末尾的数据,则只向左边缩小
} else if (totalDataList != null
&& startDataNum + maxViewDataNum >= totalDataList.size()) {
maxViewDataNum += 2;
startDataNum = totalDataList.size() - maxViewDataNum;
//如果view显示的数据是totalDataList最开始的数据,则只向右边缩小
} else if (startDataNum <= 0) {
startDataNum = 0;
maxViewDataNum += 2;
//如果双指起始落点都在中线左侧,则左边不动,右边缩小
} else if (verticalXList != null && mulFirstDownX < verticalXList.get(2)
&& mulSecondDownX <= verticalXList.get(2)) {
maxViewDataNum += 2;
//如果双指起始落点在中线左右两侧,则左右同时缩小
} else if ((verticalXList != null && mulFirstDownX <= verticalXList.get(2)
&& mulSecondDownX >= verticalXList.get(2))
|| (verticalXList != null && mulFirstDownX >= verticalXList.get(2)
&& mulSecondDownX <= verticalXList.get(2))) {
maxViewDataNum += 2;
startDataNum -= 1;
//如果双指起始落点都在中线右侧,则右边不动,左边缩小
} else if (verticalXList != null && mulFirstDownX >= verticalXList.get(2)
&& mulSecondDownX > verticalXList.get(2)) {
maxViewDataNum += 2;
startDataNum -= 2;
}
resetViewData();
}
lastDiffMoveX = Math.abs(mulSecondMoveX - mulFirstMoveX);
lastDiffMoveY = Math.abs(mulSecondMoveY - mulFirstMoveY);
}
break;
case MotionEvent.ACTION_UP:
if (!isDoubleFinger) {
float diffTouchMoveX = Math.abs(event.getX() - singleClickDownX);
float diffTouchMoveY = Math.abs(event.getY() - singleClickDownY);
if (diffTouchMoveY < moveLimitDistance && diffTouchMoveX < moveLimitDistance) {
isShowDetail = true;
if (crossHairMoveMode == CROSS_HAIR_MOVE_FREE) {
longPressMoveY = event.getY();
}
getClickKData(singleClickDownX);
if (lastKData != null) {
invalidate();
}
}
}
isDoubleFinger = false;
break;
case MotionEvent.ACTION_CANCEL:
isDoubleFinger = false;
break;
}
return true;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return !isDoubleFinger && gestureDetector.onTouchEvent(event);
}
private class CustomGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if ((startDataNum == 0 && distanceX < 0)
|| (totalDataList != null && startDataNum == totalDataList.size() - maxViewDataNum && distanceX > 0)
|| startDataNum < 0
|| (viewDataList != null && viewDataList.size() < maxViewDataNum)) {
if (isShowDetail) {
isShowDetail = false;
if (!viewDataList.isEmpty()) {
lastKData = viewDataList.get(viewDataList.size() - 1);
}
invalidate();
}
return true;
} else {
isShowDetail = false;
if (Math.abs(distanceX) > 1) {
moveData(distanceX);
invalidate();
}
}
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (totalDataList != null && startDataNum > 0
&& startDataNum < totalDataList.size() - 1 - maxViewDataNum) {
if (velocityX > 8000) {
flingVelocityX = 8000;
} else if (velocityX < -8000) {
flingVelocityX = -8000;
} else {
flingVelocityX = velocityX;
}
stopDelay();
}
return true;
}
}
private void stopDelay() {
post(mDelayRunnable);
}
private void initStopDelay() {
mDelayRunnable = new Runnable() {
@Override
public void run() {
if (flingVelocityX < -200) {
if (flingVelocityX < -6000) {
startDataNum += 6;
} else if (flingVelocityX < -4000) {
startDataNum += 5;
} else if (flingVelocityX < -2500) {
startDataNum += 4;
} else if (flingVelocityX < -1000) {
startDataNum += 3;
} else {
startDataNum++;
}
flingVelocityX += 200;
if (startDataNum > totalDataList.size() - maxViewDataNum) {
startDataNum = totalDataList.size() - maxViewDataNum;
}
} else if (flingVelocityX > 200) {
if (flingVelocityX > 6000) {
startDataNum -= 6;
} else if (flingVelocityX > 4000) {
startDataNum -= 5;
} else if (flingVelocityX > 2500) {
startDataNum -= 4;
} else if (flingVelocityX > 1000) {
startDataNum -= 3;
} else {
startDataNum--;
}
flingVelocityX -= 200;
if (startDataNum < 0) {
startDataNum = 0;
}
}
resetViewData();
requestNewData();
if (Math.abs(flingVelocityX) > 200) {
postDelayed(this, 15);
}
}
};
}
private void moveData(float distanceX) {
if (maxViewDataNum < 60) {
setSpeed(distanceX, 10);
} else {
setSpeed(distanceX, 3.5);
}
if (startDataNum < 0) {
startDataNum = 0;
}
if (totalDataList != null){
int size = totalDataList.size();
if (startDataNum > size - maxViewDataNum) {
startDataNum = size - maxViewDataNum;
}
}
requestNewData();
resetViewData();
}
private void setSpeed(float distanceX, double num) {
if (Math.abs(distanceX) > 1 && Math.abs(distanceX) < 2) {
startDataNum += (int) (distanceX * 10) % 2;
} else if (Math.abs(distanceX) < 10) {
startDataNum += (int) distanceX % 2;
} else {
startDataNum += (int) distanceX / num;
}
}
private void requestNewData() {
if (totalDataList != null && startDataNum <= totalDataList.size() / 3 && isNeedRequestPreData) {
isNeedRequestPreData = false;
if (requestListener != null){
requestListener.requestData();
}
}
}
private void resetViewData() {
if (viewDataList == null || totalDataList == null){
return;
}
viewDataList.clear();
int currentViewDataNum = Math.min(maxViewDataNum, totalDataList.size());
if (startDataNum >= 0) {
for (int i = 0; i < currentViewDataNum; i++) {
if (i + startDataNum < totalDataList.size()) {
viewDataList.add(totalDataList.get(i + startDataNum));
}
}
} else {
for (int i = 0; i < currentViewDataNum; i++) {
viewDataList.add(totalDataList.get(i));
}
}
if (viewDataList.size() > 0 && !isShowDetail) {
lastKData = viewDataList.get(viewDataList.size() - 1);
} else if (viewDataList.isEmpty()) {
lastKData = null;
}
invalidate();
}
private void resetData() {
if (verticalXList == null || horizontalYList == null || rightEnd == 0) {
return;
}
//垂直刻度线
float horizontalSpace = (rightEnd - leftStart - (dp2px(46))) / 4;
verticalXList.clear();
for (int i = 0; i < 5; i++) {
verticalXList.add(leftStart + horizontalSpace * (i) + dp2px(6));
}
//水平刻度线
verticalSpace = (bottomEnd - topStart - dp2px(38)) / 5;
horizontalYList.clear();
for (int i = 0; i < 6; i++) {
horizontalYList.add(topStart + verticalSpace * i + dp2px(18));
}
//副图顶线
deputyTopY = horizontalYList.get(4) + dp2px(12);
if (verticalXList == null || horizontalYList == null || viewDataList == null) {
return;
}
avgPriceRectWidth = (verticalXList.get(verticalXList.size() - 1)
- verticalXList.get(0)) / maxViewDataNum;
maxPrice = viewDataList.get(0).getMaxPrice();
minPrice = viewDataList.get(0).getMinPrice();
maxVolume = viewDataList.get(0).getVolume();
mMaxMacd = viewDataList.get(0).getMacd();
mMinMacd = viewDataList.get(0).getMacd();
double maxDea = viewDataList.get(0).getDea();
double minDea = viewDataList.get(0).getDea();
double maxDif = viewDataList.get(0).getDif();
double minDif = viewDataList.get(0).getDif();
mMaxK = viewDataList.get(0).getK();
double maxD = viewDataList.get(0).getD();
double maxJ = viewDataList.get(0).getJ();
int viewDataSize = viewDataList.size();
for (int i = 0; i < viewDataSize; i++) {
KData viewKData = viewDataList.get(i);
double rightX = verticalXList.get(verticalXList.size() - 1)
- (viewDataList.size() - i - 1) * avgPriceRectWidth;
double leftX = rightX - avgPriceRectWidth;
double centerX = rightX - avgPriceRectWidth/2;
viewKData.setLeftX(leftX);
viewKData.setRightX(rightX);
viewKData.setCenterX(centerX);
if (viewKData.getMaxPrice() >= maxPrice) {
maxPrice = viewKData.getMaxPrice();
maxPriceX = viewKData.getLeftX() + avgPriceRectWidth / 2;
}
if (viewKData.getMinPrice() <= minPrice) {
minPrice = viewKData.getMinPrice();
minPriceX = viewKData.getLeftX() + avgPriceRectWidth / 2;
}
if (viewKData.getVolume() >= maxVolume) {
maxVolume = viewKData.getVolume();
}
if (!isShowDeputy || isShowInstant){
continue;
}
if (deputyImgType == DEPUTY_IMG_MACD) {
if (viewKData.getMacd() >= mMaxMacd) {
mMaxMacd = viewKData.getMacd();
}
if (viewKData.getMacd() <= mMinMacd) {
mMinMacd = viewKData.getMacd();
}
if (viewKData.getDea() >= maxDea) {
maxDea = viewKData.getDea();
}
if (viewKData.getDea() <= minDea) {
minDea = viewKData.getDea();
}
if (viewKData.getDif() >= maxDif) {
maxDif = viewKData.getDif();
}
if (viewKData.getDif() <= minDif) {
minDif = viewKData.getDif();
}
} else if (deputyImgType == DEPUTY_IMG_KDJ) {
if (viewKData.getK() >= mMaxK) {
mMaxK = viewKData.getK();
}
if (viewKData.getD() >= maxD) {
maxD = viewKData.getD();
}
if (viewKData.getJ() >= maxJ) {
maxJ = viewKData.getJ();
}
}
}
topPrice = maxPrice + (maxPrice - minPrice) * 0.1;
botPrice = minPrice - (maxPrice - minPrice) * 0.1;
if (!isShowDeputy) {
priceImgBot = horizontalYList.get(4);
volumeImgBot = horizontalYList.get(5);
} else {
priceImgBot = horizontalYList.get(3);
volumeImgBot = horizontalYList.get(4);
}
//priceData
avgHeightPerPrice = (priceImgBot - horizontalYList.get(0)) / (topPrice - botPrice);
mMaxPriceY = (horizontalYList.get(0) + (topPrice - maxPrice) * avgHeightPerPrice);
mMinPriceY = (horizontalYList.get(0) + (topPrice - minPrice) * avgHeightPerPrice);
//volumeData
avgHeightPerVolume = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY) / maxVolume;
for (KData kData : viewDataList) {
double openPrice = kData.getOpenPrice();
double closedPrice = kData.getClosePrice();
kData.setCloseY((float) (horizontalYList.get(0) + (topPrice - closedPrice) * avgHeightPerPrice));
kData.setOpenY((float) (horizontalYList.get(0) + (topPrice - openPrice) * avgHeightPerPrice));
}
if (!isShowDeputy){
return;
}
switch (deputyImgType) {
case DEPUTY_IMG_MACD:
//MACD
if (mMaxMacd > 0 && mMinMacd < 0) {
avgHeightMacd = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY) / Math.abs(mMaxMacd - mMinMacd);
deputyCenterY = (float) (deputyTopY + mMaxMacd * avgHeightMacd);
} else if (mMaxMacd <= 0) {
avgHeightMacd = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY) / Math.abs(mMinMacd);
deputyCenterY = deputyTopY;
} else if (mMinMacd >= 0) {
avgHeightMacd = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY) / Math.abs(mMaxMacd);
deputyCenterY = horizontalYList.get(horizontalYList.size() - 1);
}
//DEA
if (maxDea > 0 && minDea < 0) {
avgHeightDea = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(24)) / (maxDea - minDea);
} else if (maxDea <= 0) {
avgHeightDea = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(24)) / Math.abs(minDea);
} else if (minDea >= 0) {
avgHeightDea = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(24)) / Math.abs(maxDea);
}
//DIF
if (maxDif > 0 && minDif < 0) {
avgHeightDif = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(24)) / (maxDif - minDif);
} else if (maxDif <= 0) {
avgHeightDif = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(24)) / Math.abs(minDif);
} else if (minDif >= 0) {
avgHeightDif = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(24)) / Math.abs(maxDif);
}
break;
case DEPUTY_IMG_KDJ:
//K
avgHeightK = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(12)) / mMaxK;
//D
avgHeightD = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(12)) / maxD;
//J
avgHeightJ = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY - dp2px(12)) / maxJ;
break;
case DEPUTY_IMG_RSI:
avgHeightRSI = (horizontalYList.get(horizontalYList.size() - 1) - deputyTopY) / 100;
break;
}
}
//刻度线
private void drawTickMark(Canvas canvas) {
if (verticalXList == null || horizontalYList == null){
return;
}
resetStrokePaint(tickMarkCol, 0);
for (Float aFloat : verticalXList) {
canvas.drawLine(aFloat, topStart + dp2px(18), aFloat, bottomEnd - dp2px(20), strokePaint);
}
float horizontalRightEnd;
for (int i = 0; i < horizontalYList.size(); i++) {
if (i == 0 || i == 5 || i == 4 || (isShowDeputy && i == 3)) {
horizontalRightEnd = rightEnd;
} else {
horizontalRightEnd = verticalXList.get(verticalXList.size() - 1);
}
canvas.drawLine(leftStart + dp2px(6),
horizontalYList.get(i),
horizontalRightEnd,
horizontalYList.get(i),
strokePaint);
}
canvas.drawLine(leftStart + dp2px(6),
horizontalYList.get(4) + verticalSpace / 2,
verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(4) + verticalSpace / 2,
strokePaint);
//数量中线
if (isShowDeputy) {
canvas.drawLine(leftStart + dp2px(6),
horizontalYList.get(3) + verticalSpace / 2,
verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(3) + verticalSpace / 2,
strokePaint);
}
}
//主副图蜡烛图
private void drawMainDeputyRect(Canvas canvas) {
int viewDataSize = viewDataList.size();
//drawPriceRectAndLine
for (KData viewKData : viewDataList) {
double openPrice = viewKData.getOpenPrice();
double closedPrice = viewKData.getClosePrice();
double higherPrice;
double lowerPrice;
if (openPrice >= closedPrice) {
higherPrice = openPrice;
lowerPrice = closedPrice;
fillPaint.setColor(priceFallCol);
resetStrokePaint(priceFallCol, 0);
} else {
higherPrice = closedPrice;
lowerPrice = openPrice;
fillPaint.setColor(priceIncreaseCol);
resetStrokePaint(priceIncreaseCol, 0);
}
//如果开盘价==收盘价,则给1px的高度
float upPriceCoordinate = (float) (mMaxPriceY + (maxPrice - higherPrice) * avgHeightPerPrice);
float downPriceCoordinate = (float) (mMaxPriceY + (maxPrice - lowerPrice) * avgHeightPerPrice);
if (upPriceCoordinate == downPriceCoordinate){
downPriceCoordinate = upPriceCoordinate + 1;
}
//priceRect
canvas.drawRect((float) viewKData.getLeftX() + dp2px(0.5f),
upPriceCoordinate,
(float) viewKData.getRightX() - dp2px(0.5f),
downPriceCoordinate,
fillPaint);
//priceLine
canvas.drawLine((float) (viewKData.getCenterX()),
(float) (mMaxPriceY + (maxPrice - viewKData.getMaxPrice()) * avgHeightPerPrice),
(float) (viewKData.getCenterX()),
(float) (mMaxPriceY + (maxPrice - viewKData.getMinPrice()) * avgHeightPerPrice),
strokePaint);
//MACD
if (isShowDeputy && deputyImgType == DEPUTY_IMG_MACD) {
double macd = viewKData.getMacd();
if (macd > 0) {
fillPaint.setColor(macdPositiveCol);
canvas.drawRect((float) (viewKData.getLeftX() + dp2px(0.5f)),
(float) (deputyCenterY - macd * avgHeightMacd),
(float) viewKData.getRightX() - dp2px(0.5f),
deputyCenterY,
fillPaint);
} else {
fillPaint.setColor(macdNegativeCol);
canvas.drawRect((float) (viewKData.getLeftX() + dp2px(0.5f)),
deputyCenterY,
(float) viewKData.getRightX() - dp2px(0.5f),
(float) (deputyCenterY + Math.abs(macd) * avgHeightMacd),
fillPaint);
}
}
}
}
private void drawVolume(Canvas canvas){
for (KData kData : viewDataList) {
//volumeRect
if (!isShowInstant){
double openPrice = kData.getOpenPrice();
double closedPrice = kData.getClosePrice();
if (openPrice >= closedPrice) {
fillPaint.setColor(priceFallCol);
} else {
fillPaint.setColor(priceIncreaseCol);
}
} else {
fillPaint.setColor(0xff4db7f3);
}
canvas.drawRect((float) (kData.getLeftX() + dp2px(0.5f)),
(float) (volumeImgBot - kData.getVolume() * avgHeightPerVolume),
(float) kData.getRightX() - dp2px(0.5f),
volumeImgBot,
fillPaint);
}
}
//贝塞尔曲线
private void drawBezierCurve(Canvas canvas) {
mainMa5PointList.clear();
mainMa10PointList.clear();
mainMa30PointList.clear();
volumeMa5PointList.clear();
volumeMa10PointList.clear();
deputyMa5PointList.clear();
deputyMa10PointList.clear();
deputyMa30PointList.clear();
for (KData kData : viewDataList) {
if (!kData.isInitFinish()) {
break;
}
//volumeMA
Pointer volumeMa5Point = new Pointer();
if (kData.getVolumeMa5() > 0) {
volumeMa5Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
volumeMa5Point.setY((float) (volumeImgBot
- kData.getVolumeMa5() * avgHeightPerVolume));
volumeMa5PointList.add(volumeMa5Point);
}
Pointer volumeMa10Point = new Pointer();
if (kData.getVolumeMa10() > 0) {
volumeMa10Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
volumeMa10Point.setY((float) (volumeImgBot
- kData.getVolumeMa10() * avgHeightPerVolume));
volumeMa10PointList.add(volumeMa10Point);
}
switch (mainImgType) {
//priceMA
case MAIN_IMG_MA:
Pointer priceMa5Point = new Pointer();
if (kData.getPriceMa5() > 0) {
priceMa5Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
priceMa5Point.setY((float) (mMaxPriceY
+ (maxPrice - kData.getPriceMa5()) * avgHeightPerPrice));
mainMa5PointList.add(priceMa5Point);
}
Pointer priceMa10Point = new Pointer();
if (kData.getPriceMa10() > 0) {
priceMa10Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
priceMa10Point.setY((float) (mMaxPriceY
+ (maxPrice - kData.getPriceMa10()) * avgHeightPerPrice));
mainMa10PointList.add(priceMa10Point);
}
Pointer priceMa30Point = new Pointer();
if (kData.getPriceMa30() > 0) {
priceMa30Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
priceMa30Point.setY((float) (mMaxPriceY
+ (maxPrice - kData.getPriceMa30()) * avgHeightPerPrice));
mainMa30PointList.add(priceMa30Point);
}
break;
//priceEMA
case MAIN_IMG_EMA:
Pointer ema5Point = new Pointer();
if (kData.getEma5() > 0) {
ema5Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
ema5Point.setY((float) (mMaxPriceY
+ (maxPrice - kData.getEma5()) * avgHeightPerPrice));
mainMa5PointList.add(ema5Point);
}
Pointer ema10Point = new Pointer();
if (kData.getEma10() > 0) {
ema10Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
ema10Point.setY((float) (mMaxPriceY
+ (maxPrice - kData.getEma10()) * avgHeightPerPrice));
mainMa10PointList.add(ema10Point);
}
Pointer ema30Point = new Pointer();
if (kData.getEma30() > 0) {
ema30Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
ema30Point.setY((float) (mMaxPriceY
+ (maxPrice - kData.getEma30()) * avgHeightPerPrice));
mainMa30PointList.add(ema30Point);
}
break;
//priceBOLL
case MAIN_IMG_BOLL:
Pointer bollMbPoint = new Pointer();
if (kData.getBollMb() > 0) {
bollMbPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
bollMbPoint.setY((float) (mMaxPriceY
+ (maxPrice - kData.getBollMb()) * avgHeightPerPrice));
mainMa5PointList.add(bollMbPoint);
}
Pointer bollUpPoint = new Pointer();
if (kData.getBollUp() > 0) {
bollUpPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
bollUpPoint.setY((float) (mMaxPriceY
+ (maxPrice - kData.getBollUp()) * avgHeightPerPrice));
mainMa10PointList.add(bollUpPoint);
}
Pointer bollDnPoint = new Pointer();
if (kData.getBollDn() > 0) {
bollDnPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
bollDnPoint.setY((float) (mMaxPriceY
+ (maxPrice - kData.getBollDn()) * avgHeightPerPrice));
mainMa30PointList.add(bollDnPoint);
}
break;
}
if (isShowDeputy && deputyImgType == DEPUTY_IMG_MACD) {
Pointer difPoint = new Pointer();
if (kData.getDif() > 0) {
difPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
difPoint.setY((float) (deputyCenterY - kData.getDif() * avgHeightDif));
} else {
difPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
difPoint.setY((float) (deputyCenterY + Math.abs(kData.getDif() * avgHeightDif)));
}
deputyMa10PointList.add(difPoint);
Pointer deaPoint = new Pointer();
if (kData.getDea() > 0) {
deaPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
deaPoint.setY((float) (deputyCenterY - kData.getDea() * avgHeightDea));
} else {
deaPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
deaPoint.setY((float) (deputyCenterY + Math.abs(kData.getDea() * avgHeightDea)));
}
deputyMa30PointList.add(deaPoint);
} else if (isShowDeputy && deputyImgType == DEPUTY_IMG_KDJ) {
Pointer kPoint = new Pointer();
if (kData.getK() > 0) {
kPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
kPoint.setY((float) (horizontalYList.get(5) - kData.getK() * avgHeightK));
deputyMa5PointList.add(kPoint);
}
Pointer dPoint = new Pointer();
if (kData.getD() > 0) {
dPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
dPoint.setY((float) (horizontalYList.get(5) - kData.getD() * avgHeightD));
deputyMa10PointList.add(dPoint);
}
Pointer jPoint = new Pointer();
if (kData.getJ() > 0) {
jPoint.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
jPoint.setY((float) (horizontalYList.get(5) - kData.getJ() * avgHeightJ));
deputyMa30PointList.add(jPoint);
}
} else if (isShowDeputy && deputyImgType == DEPUTY_IMG_RSI) {
Pointer rs1Point = new Pointer();
if (kData.getRs1() >= 0) {
rs1Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
rs1Point.setY((float) (horizontalYList.get(5) - kData.getRs1() * avgHeightRSI));
deputyMa5PointList.add(rs1Point);
}
Pointer rs2Point = new Pointer();
if (kData.getRs2() >= 0) {
rs2Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
rs2Point.setY((float) (horizontalYList.get(5) - kData.getRs2() * avgHeightRSI));
deputyMa10PointList.add(rs2Point);
}
Pointer rs3Point = new Pointer();
if (kData.getRs3() >= 0) {
rs3Point.setX((float) (kData.getLeftX() + avgPriceRectWidth / 2));
rs3Point.setY((float) (horizontalYList.get(5) - kData.getRs3() * avgHeightRSI));
deputyMa30PointList.add(rs3Point);
}
}
}
drawVolumeBezierCurve(canvas);
drawMainBezierCurve(canvas);
if (isShowDeputy) {
drawDeputyCurve(canvas);
}
}
//主图 MA曲线
private void drawMainBezierCurve(@NonNull Canvas canvas) {
QuotaUtil.setBezierPath(mainMa5PointList, curvePath);
resetStrokePaint(priceMa5Col, 0);
canvas.drawPath(curvePath, strokePaint);
QuotaUtil.setBezierPath(mainMa10PointList, curvePath);
resetStrokePaint(priceMa10Col, 0);
canvas.drawPath(curvePath, strokePaint);
QuotaUtil.setBezierPath(mainMa30PointList, curvePath);
resetStrokePaint(priceMa30Col, 0);
canvas.drawPath(curvePath, strokePaint);
}
//volume MA曲线
private void drawVolumeBezierCurve(@NonNull Canvas canvas) {
QuotaUtil.setBezierPath(volumeMa5PointList, curvePath);
resetStrokePaint(priceMa5Col, 0);
canvas.drawPath(curvePath, strokePaint);
QuotaUtil.setBezierPath(volumeMa10PointList, curvePath);
resetStrokePaint(priceMa10Col, 0);
canvas.drawPath(curvePath, strokePaint);
}
//副图 曲线
private void drawDeputyCurve(@NonNull Canvas canvas) {
QuotaUtil.setLinePath(deputyMa5PointList, curvePath);
resetStrokePaint(priceMa5Col, 0);
canvas.drawPath(curvePath, strokePaint);
QuotaUtil.setLinePath(deputyMa10PointList, curvePath);
resetStrokePaint(priceMa10Col, 0);
canvas.drawPath(curvePath, strokePaint);
QuotaUtil.setLinePath(deputyMa30PointList, curvePath);
resetStrokePaint(priceMa30Col, 0);
canvas.drawPath(curvePath, strokePaint);
}
//获取单击位置的数据
private void getClickKData(float clickX) {
if (isShowDetail) {
detailRightDataList.clear();
for (int i = 0; i < viewDataList.size(); i++) {
if (viewDataList.get(i).getLeftX() <= clickX
&& viewDataList.get(i).getRightX() >= clickX) {
lastKData = viewDataList.get(i);
detailRightDataList.add(formatDate(lastKData.getTime()));
detailRightDataList.add(setPrecision(lastKData.getOpenPrice(), 2));
detailRightDataList.add(setPrecision(lastKData.getMaxPrice(), 2));
detailRightDataList.add(setPrecision(lastKData.getMinPrice(), 2));
detailRightDataList.add(setPrecision(lastKData.getClosePrice(), 2));
double upDnAmount = lastKData.getUpDnAmount();
if (upDnAmount > 0) {
detailRightDataList.add("+" + setPrecision(upDnAmount, 2));
detailRightDataList.add("+" + setPrecision(lastKData.getUpDnRate() * 100, 2) + "%");
} else {
detailRightDataList.add(setPrecision(upDnAmount, 2));
detailRightDataList.add(setPrecision(lastKData.getUpDnRate() * 100, 2) + "%");
}
detailRightDataList.add(setPrecision(lastKData.getVolume(), 2));
break;
} else {
lastKData = null;
}
}
} else {
lastKData = viewDataList.get(viewDataList.size() - 1);
}
}
//十字线
private void drawCrossHairLine(Canvas canvas) {
if (lastKData == null || !isShowDetail) {
return;
}
//垂直
resetStrokePaint(crossHairCol, 0);
canvas.drawLine((float) (lastKData.getLeftX() + avgPriceRectWidth / 2),
horizontalYList.get(0),
(float) (lastKData.getLeftX() + avgPriceRectWidth / 2),
horizontalYList.get(horizontalYList.size() - 1),
strokePaint);
//水平
double moveY;
switch (crossHairMoveMode) {
case CROSS_HAIR_MOVE_OPEN:
moveY = lastKData.getOpenY();
break;
case CROSS_HAIR_MOVE_FREE:
moveY = longPressMoveY;
break;
case CROSS_HAIR_MOVE_CLOSE:
default:
moveY = lastKData.getCloseY();
break;
}
if (moveY < horizontalYList.get(0)) {
moveY = horizontalYList.get(0);
} else if (moveY > priceImgBot) {
moveY = priceImgBot;
}
resetStrokePaint(crossHairCol, 0);
canvas.drawLine(verticalXList.get(0),
(float) moveY,
verticalXList.get(verticalXList.size() - 1),
(float) moveY,
strokePaint);
//底部标签
RectF grayRectF = new RectF((float) (lastKData.getLeftX() + avgPriceRectWidth / 2 - dp2px(25)),
bottomEnd - dp2px(20),
(float) (lastKData.getLeftX() + avgPriceRectWidth / 2 + dp2px(25)),
bottomEnd);
fillPaint.setColor(crossHairBottomLabelCol);
canvas.drawRoundRect(grayRectF, 4, 4, fillPaint);
//底部标签text
String moveTime = formatDate(lastKData.getTime());
resetStrokePaint(crossHairBottomLabelTextCol, crossHairBottomLabelTextSize);
canvas.drawText(moveTime,
(float) (lastKData.getLeftX() + avgPriceRectWidth / 2 - strokePaint.measureText(moveTime) / 2),
bottomEnd - dp2px(7),
strokePaint);
//右侧标签
RectF blueRectF = new RectF(rightEnd - dp2px(38),
(float) moveY - dp2px(7),
rightEnd - dp2px(1),
(float) moveY + dp2px(7));
fillPaint.setColor(crossHairRightLabelCol);
canvas.drawRoundRect(blueRectF, 4, 4, fillPaint);
curvePath.reset();
curvePath.moveTo(verticalXList.get(verticalXList.size() - 1), (float) moveY);
curvePath.lineTo(rightEnd - dp2px(37), (float) moveY - dp2px(3));
curvePath.lineTo(rightEnd - dp2px(37), (float) moveY + dp2px(3));
curvePath.close();
canvas.drawPath(curvePath, fillPaint);
double avgPricePerHeight;
if (!isShowDeputy) {
avgPricePerHeight = (topPrice - botPrice)
/ (horizontalYList.get(4) - horizontalYList.get(0));
} else {
avgPricePerHeight = (topPrice - botPrice)
/ (horizontalYList.get(3) - horizontalYList.get(0));
}
String movePrice = formatKDataNum(topPrice
- avgPricePerHeight * ((float) moveY - horizontalYList.get(0)));
Rect textRect = new Rect();
resetStrokePaint(crossHairRightLabelTextCol, crossHairRightLabelTextSize);
strokePaint.getTextBounds(movePrice, 0, movePrice.length(), textRect);
canvas.drawText(movePrice,
rightEnd - dp2px(38) + (blueRectF.width() - textRect.width()) / 2,
(float) moveY + dp2px(7) - (blueRectF.height() - textRect.height()) / 2,
strokePaint);
}
//最高价、最低价标签
private void drawMaxMinPriceLabel(Canvas canvas) {
//maxPrice
Rect maxPriceRect = new Rect();
String maxPriceStr = setPrecision(maxPrice, 2);
resetStrokePaint(priceMaxLabelTextCol, priceMaxLabelTextSize);
strokePaint.getTextBounds(maxPriceStr, 0, maxPriceStr.length(), maxPriceRect);
RectF maxRectF;
float maxPriceTextX;
if (maxPriceX + maxPriceRect.width() + dp2px(8) < verticalXList.get(verticalXList.size() - 1)) {
maxRectF = new RectF((float) (maxPriceX + dp2px(3)),
(float) mMaxPriceY - dp2px(7),
(float) (maxPriceX + maxPriceRect.width() + dp2px(8)),
(float) mMaxPriceY + dp2px(7));
curvePath.reset();
curvePath.moveTo((float) maxPriceX, (float) mMaxPriceY);
curvePath.lineTo((float) (maxPriceX + dp2px(4)), (float) mMaxPriceY - dp2px(3));
curvePath.lineTo((float) (maxPriceX + dp2px(4)), (float) mMaxPriceY + dp2px(3));
curvePath.close();
maxPriceTextX = (float) (maxPriceX + dp2px(5));
} else {
maxRectF = new RectF((float) (maxPriceX - dp2px(3)),
(float) mMaxPriceY - dp2px(7),
(float) (maxPriceX - maxPriceRect.width() - dp2px(8)),
(float) mMaxPriceY + dp2px(7));
curvePath.reset();
curvePath.moveTo((float) maxPriceX, (float) mMaxPriceY);
curvePath.lineTo((float) (maxPriceX - dp2px(4)), (float) mMaxPriceY - dp2px(3));
curvePath.lineTo((float) (maxPriceX - dp2px(4)), (float) mMaxPriceY + dp2px(3));
curvePath.close();
maxPriceTextX = (float) (maxPriceX - dp2px(5)) - maxPriceRect.width();
}
fillPaint.setColor(priceMaxLabelCol);
canvas.drawRoundRect(maxRectF, 4, 4, fillPaint);
canvas.drawPath(curvePath, fillPaint);
resetStrokePaint(priceMaxLabelTextCol, priceMaxLabelTextSize);
canvas.drawText(maxPriceStr,
maxPriceTextX,
(float) mMaxPriceY + maxPriceRect.height() / 2f,
strokePaint);
//minPrice
Rect minPriceRect = new Rect();
String minPriceStr = setPrecision(minPrice, 2);
resetStrokePaint(priceMinLabelTextCol, priceMinLabelTextSize);
strokePaint.getTextBounds(minPriceStr, 0, minPriceStr.length(), minPriceRect);
RectF minRectF;
float minPriceTextX;
if (minPriceX + minPriceRect.width() + dp2px(8) < verticalXList.get(verticalXList.size() - 1)) {
minRectF = new RectF((float) (minPriceX + dp2px(3)),
(float) mMinPriceY - dp2px(7),
(float) (minPriceX + minPriceRect.width() + dp2px(8)),
(float) mMinPriceY + dp2px(7));
curvePath.reset();
curvePath.moveTo((float) minPriceX, (float) mMinPriceY);
curvePath.lineTo((float) (minPriceX + dp2px(4)), (float) mMinPriceY - dp2px(3));
curvePath.lineTo((float) (minPriceX + dp2px(4)), (float) mMinPriceY + dp2px(3));
curvePath.close();
minPriceTextX = (float) (minPriceX + dp2px(5));
} else {
minRectF = new RectF((float) (minPriceX - dp2px(3)),
(float) mMinPriceY - dp2px(7),
(float) (minPriceX - minPriceRect.width() - dp2px(8)),
(float) mMinPriceY + dp2px(7));
curvePath.reset();
curvePath.moveTo((float) minPriceX, (float) mMinPriceY);
curvePath.lineTo((float) (minPriceX - dp2px(4)), (float) mMinPriceY - dp2px(3));
curvePath.lineTo((float) (minPriceX - dp2px(4)), (float) mMinPriceY + dp2px(3));
curvePath.close();
minPriceTextX = (float) (minPriceX - dp2px(5)) - minPriceRect.width();
}
fillPaint.setColor(priceMinLabelCol);
canvas.drawRoundRect(minRectF, 4, 4, fillPaint);
canvas.drawPath(curvePath, fillPaint);
resetStrokePaint(priceMinLabelTextCol, priceMinLabelTextSize);
canvas.drawText(minPriceStr,
minPriceTextX,
(float) mMinPriceY + minPriceRect.height() / 2f,
strokePaint);
}
private void drawDetailData(Canvas canvas) {
if (lastKData == null || !isShowDetail) {
return;
}
resetStrokePaint(detailTextCol, detailTextSize);
strokePaint.getTextBounds(detailLeftTitleArr[0], 0, detailLeftTitleArr[0].length(), detailTextRect);
if (lastKData.getLeftX() + avgPriceRectWidth / 2 <= getMeasuredWidth() / 2f) {
//边框(右侧)
fillPaint.setColor(detailBgCol);
canvas.drawRect(verticalXList.get(verticalXList.size() - 1) - detailRectWidth,
horizontalYList.get(0),
verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(0) + detailRectHeight,
fillPaint);
resetStrokePaint(detailFrameCol, 0);
canvas.drawLine(verticalXList.get(verticalXList.size() - 1) - detailRectWidth,
horizontalYList.get(0),
verticalXList.get(verticalXList.size() - 1) - detailRectWidth,
horizontalYList.get(0) + detailRectHeight,
strokePaint);
canvas.drawLine(verticalXList.get(verticalXList.size() - 1) - detailRectWidth,
horizontalYList.get(0),
verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(0),
strokePaint);
canvas.drawLine(verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(0),
verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(0) + detailRectHeight,
strokePaint);
canvas.drawLine(verticalXList.get(verticalXList.size() - 1) - detailRectWidth,
horizontalYList.get(0) + detailRectHeight,
verticalXList.get(verticalXList.size() - 1),
horizontalYList.get(0) + detailRectHeight,
strokePaint);
//详情字段
resetStrokePaint(detailTextCol, detailTextSize);
for (int i = 0; i < detailLeftTitleArr.length; i++) {
canvas.drawText(detailLeftTitleArr[i],
verticalXList.get(verticalXList.size() - 1) - detailRectWidth + dp2px(4),
horizontalYList.get(0) + detailTextVerticalSpace * i
+ detailTextRect.height() + (detailTextVerticalSpace - detailTextRect.height()) / 2,
strokePaint);
}
//详情数据
for (int i = 0; i < detailRightDataList.size(); i++) {
if (i == 5 || i == 6) {
if (lastKData.getUpDnAmount() > 0) {
resetStrokePaint(priceIncreaseCol, detailTextSize);
} else {
resetStrokePaint(priceFallCol, detailTextSize);
}
} else {
resetStrokePaint(detailTextCol, detailTextSize);
}
canvas.drawText(detailRightDataList.get(i),
verticalXList.get(verticalXList.size() - 1) - dp2px(4)
- strokePaint.measureText(detailRightDataList.get(i)),
horizontalYList.get(0) + detailTextVerticalSpace * i
+ detailTextRect.height() + (detailTextVerticalSpace - detailTextRect.height()) / 2,
strokePaint);
}
} else {
//边框(左侧)
fillPaint.setColor(detailBgCol);
canvas.drawRect(verticalXList.get(0),
horizontalYList.get(0),
verticalXList.get(0) + detailRectWidth,
horizontalYList.get(0) + detailRectHeight,
fillPaint);
resetStrokePaint(detailFrameCol, 0);
canvas.drawLine(verticalXList.get(0),
horizontalYList.get(0),
verticalXList.get(0),
horizontalYList.get(0) + detailRectHeight,
strokePaint);
canvas.drawLine(verticalXList.get(0),
horizontalYList.get(0),
verticalXList.get(0) + detailRectWidth,
horizontalYList.get(0),
strokePaint);
canvas.drawLine(verticalXList.get(0) + detailRectWidth,
horizontalYList.get(0),
verticalXList.get(0) + detailRectWidth,
horizontalYList.get(0) + detailRectHeight,
strokePaint);
canvas.drawLine(verticalXList.get(0),
horizontalYList.get(0) + detailRectHeight,
verticalXList.get(0) + detailRectWidth,
horizontalYList.get(0) + detailRectHeight,
strokePaint);
//文字详情
resetStrokePaint(detailTextCol, detailTextSize);
for (int i = 0; i < detailLeftTitleArr.length; i++) {
canvas.drawText(detailLeftTitleArr[i],
verticalXList.get(0) + dp2px(4),
horizontalYList.get(0) + detailTextVerticalSpace * i
+ detailTextRect.height() + (detailTextVerticalSpace - detailTextRect.height()) / 2,
strokePaint);
}
//详情数据
for (int i = 0; i < detailRightDataList.size(); i++) {
if (i == 5 || i == 6) {
if (lastKData.getUpDnAmount() > 0) {
resetStrokePaint(priceIncreaseCol, detailTextSize);
} else {
resetStrokePaint(priceFallCol, detailTextSize);
}
} else {
resetStrokePaint(detailTextCol, detailTextSize);
}
canvas.drawText(detailRightDataList.get(i),
verticalXList.get(0) + detailRectWidth - dp2px(4)
- strokePaint.measureText(detailRightDataList.get(i)),
horizontalYList.get(0) + detailTextVerticalSpace * i
+ detailTextRect.height() + (detailTextVerticalSpace - detailTextRect.height()) / 2,
strokePaint);
}
}
}
//顶部价格MA
private void drawTopPriceMAData(Canvas canvas) {
if (lastKData == null) {
return;
}
String ma5Str = STR_MA5 + setPrecision(lastKData.getPriceMa5(), 2);
String ma10Str = STR_MA10 + setPrecision(lastKData.getPriceMa10(), 2);
String ma30Str = STR_MA30 + setPrecision(lastKData.getPriceMa30(), 2);
resetStrokePaint(priceMa5Col, topMaTextSize);
strokePaint.getTextBounds(ma5Str, 0, ma5Str.length(), topMa5Rect);
canvas.drawText(ma5Str,
leftStart + dp2px(6),
topStart + topMa5Rect.height() + dp2px(6),
strokePaint);
resetStrokePaint(priceMa10Col, topMaTextSize);
strokePaint.getTextBounds(ma10Str, 0, ma10Str.length(), topMa10Rect);
canvas.drawText(ma10Str,
leftStart + dp2px(6) + topMa5Rect.width() + dp2px(10),
topStart + topMa5Rect.height() + dp2px(6),
strokePaint);
resetStrokePaint(priceMa30Col, topMaTextSize);
strokePaint.getTextBounds(ma30Str, 0, ma30Str.length(), topMa30Rect);
canvas.drawText(ma30Str,
leftStart + dp2px(6) + topMa5Rect.width() + topMa10Rect.width() + dp2px(10) * 2,
topStart + topMa5Rect.height() + dp2px(6),
strokePaint);
}
//数量MA
private void drawBotMAData(Canvas canvas) {
if (lastKData == null) {
return;
}
//VOL
String volStr = STR_VOL + setPrecision(lastKData.getVolume(), 2);
Rect volRect = new Rect();
resetStrokePaint(volumeTextCol, volumeTextSize);
strokePaint.getTextBounds(volStr, 0, volStr.length(), volRect);
canvas.drawText(volStr,
verticalXList.get(0),
priceImgBot + volRect.height() + dp2px(2),
strokePaint);
String ma5Str = STR_MA5 + setPrecision(lastKData.getVolumeMa5(), 2);
Rect volMa5Rect = new Rect();
resetStrokePaint(priceMa5Col, volumeTextSize);
strokePaint.getTextBounds(ma5Str, 0, ma5Str.length(), volMa5Rect);
canvas.drawText(ma5Str,
verticalXList.get(0) + volRect.width() + dp2px(10),
priceImgBot + volRect.height() + dp2px(2),
strokePaint);
String ma10Str = STR_MA10 + setPrecision(lastKData.getVolumeMa10(), 2);
resetStrokePaint(priceMa10Col, volumeTextSize);
canvas.drawText(ma10Str,
verticalXList.get(0) + volMa5Rect.width() + volRect.width() + dp2px(10) * 2,
priceImgBot + volRect.height() + dp2px(2),
strokePaint);
String titleStr = "";
String firstStr = "";
String secondStr = "";
String thirdStr = "";
if (isShowDeputy && deputyImgType == DEPUTY_IMG_MACD) {
titleStr = STR_MACD_TITLE;
firstStr = STR_MACD + setPrecision(lastKData.getMacd(), 2);
secondStr = STR_DIF + setPrecision(lastKData.getDif(), 2);
thirdStr = STR_DEA + setPrecision(lastKData.getDea(), 2);
} else if (isShowDeputy && deputyImgType == DEPUTY_IMG_KDJ) {
titleStr = STR_KDJ_TITLE;
firstStr = STR_K + setPrecision(lastKData.getK(), 2);
secondStr = STR_D + setPrecision(lastKData.getD(), 2);
thirdStr = STR_J + setPrecision(lastKData.getJ(), 2);
} else if (isShowDeputy && deputyImgType == DEPUTY_IMG_RSI) {
titleStr = STR_RSI_TITLE;
firstStr = STR_RS1 + setPrecision(lastKData.getRs1(), 2);
secondStr = STR_RS2 + setPrecision(lastKData.getRs2(), 2);
thirdStr = STR_RS3 + setPrecision(lastKData.getRs3(), 2);
}
Rect titleRect = new Rect();
resetStrokePaint(volumeTextCol, volumeTextSize);
strokePaint.getTextBounds(titleStr, 0, titleStr.length(), titleRect);
canvas.drawText(titleStr,
verticalXList.get(0),
horizontalYList.get(4) + titleRect.height(),
strokePaint);
resetStrokePaint(priceMa5Col, volumeTextSize);
canvas.drawText(firstStr,
verticalXList.get(0) + titleRect.width() + dp2px(10),
horizontalYList.get(4) + titleRect.height(),
strokePaint);
float firstWidth = strokePaint.measureText(firstStr);
resetStrokePaint(priceMa10Col, volumeTextSize);
canvas.drawText(secondStr,
verticalXList.get(0) + titleRect.width() + dp2px(20) + firstWidth,
horizontalYList.get(4) + titleRect.height(),
strokePaint);
float secondWidth = strokePaint.measureText(secondStr);
resetStrokePaint(priceMa30Col, volumeTextSize);
canvas.drawText(thirdStr,
verticalXList.get(0) + titleRect.width() + dp2px(30) + firstWidth + secondWidth,
horizontalYList.get(4) + titleRect.height(),
strokePaint);
}
//横坐标
private void drawAbscissa(Canvas canvas) {
resetStrokePaint(abscissaTextCol, abscissaTextSize);
for (int i = 0; i < verticalXList.size(); i++) {
if (i == 0 && viewDataList.get(0).getLeftX() <= verticalXList.get(0) + avgPriceRectWidth / 2
&& viewDataList.get(0).getRightX() > verticalXList.get(0)) {
canvas.drawText(formatDate(viewDataList.get(0).getTime()),
leftStart + dp2px(6),
bottomEnd - dp2px(7),
strokePaint);
} else if (i == verticalXList.size() - 1) {
String dateStr = formatDate(viewDataList.get(viewDataList.size() - 1).getTime());
canvas.drawText(dateStr,
rightEnd - dp2px(41) - strokePaint.measureText(dateStr),
bottomEnd - dp2px(7),
strokePaint);
} else {
for (KData data : viewDataList) {
if (data.getLeftX() <= verticalXList.get(i) && data.getRightX() >= verticalXList.get(i)) {
String dateStr = formatDate(data.getTime());
canvas.drawText(dateStr,
verticalXList.get(i) - strokePaint.measureText(dateStr) / 2,
bottomEnd - dp2px(7),
strokePaint);
break;
}
}
}
}
}
//纵坐标
private void drawOrdinate(@NonNull Canvas canvas) {
Rect rect = new Rect();
resetStrokePaint(ordinateTextCol, ordinateTextSize);
//最高价
strokePaint.getTextBounds(formatKDataNum(topPrice), 0, formatKDataNum(topPrice).length(), rect);
canvas.drawText(formatKDataNum(topPrice),
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
horizontalYList.get(0) + rect.height() + dp2px(2),
strokePaint);
//最低价
strokePaint.getTextBounds(formatKDataNum(botPrice), 0, formatKDataNum(botPrice).length(), rect);
canvas.drawText(formatKDataNum(botPrice),
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
priceImgBot - dp2px(2),
strokePaint);
if (!isShowDeputy) {
double avgPrice = (topPrice - botPrice) / 4;
for (int i = 0; i < 3; i++) {
canvas.drawText(formatKDataNum(topPrice - avgPrice * (i + 1)),
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
horizontalYList.get(i + 1) + rect.height() / 2f,
strokePaint);
}
} else {
double avgPrice = (topPrice - botPrice) / 3;
for (int i = 0; i < 2; i++) {
canvas.drawText(formatKDataNum(topPrice - avgPrice * (i + 1)),
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
horizontalYList.get(i + 1) + rect.height() / 2f,
strokePaint);
}
String topDeputy = "";
String botDeputy = "";
String centerDeputy = "";
if (deputyImgType == DEPUTY_IMG_MACD) {
if (mMaxMacd > 0 && mMinMacd < 0) {
topDeputy = setPrecision(mMaxMacd, 2);
botDeputy = setPrecision(mMinMacd, 2);
centerDeputy = setPrecision((mMaxMacd - mMinMacd) / 2, 2);
} else if (mMaxMacd <= 0) {
topDeputy = "0";
botDeputy = setPrecision(mMinMacd, 2);
centerDeputy = setPrecision((mMinMacd - mMaxMacd) / 2, 2);
} else if (mMinMacd >= 0) {
topDeputy = setPrecision(mMaxMacd, 2);
botDeputy = "0";
centerDeputy = setPrecision((mMaxMacd - mMinMacd) / 2, 2);
}
} else if (deputyImgType == DEPUTY_IMG_KDJ) {
topDeputy = setPrecision(mMaxK, 2);
centerDeputy = setPrecision(mMaxK / 2, 2);
botDeputy = "0";
} else if (deputyImgType == DEPUTY_IMG_RSI) {
topDeputy = "100";
centerDeputy = "50";
botDeputy = "0";
}
canvas.drawText(topDeputy,
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
horizontalYList.get(horizontalYList.size() - 2) + rect.height() + dp2px(2),
strokePaint);
canvas.drawText(centerDeputy,
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
horizontalYList.get(horizontalYList.size() - 1) - verticalSpace / 2 + rect.height() / 2f,
strokePaint);
canvas.drawText(botDeputy,
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
horizontalYList.get(horizontalYList.size() - 1) - dp2px(2),
strokePaint);
}
//最高量
strokePaint.getTextBounds(formatVolNum(maxVolume), 0, formatVolNum(maxVolume).length(), rect);
canvas.drawText(formatVolNum(maxVolume),
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
priceImgBot + rect.height() + dp2px(2),
strokePaint);
//最高量/2
canvas.drawText(formatVolNum(maxVolume / 2),
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
volumeImgBot - verticalSpace / 2 + rect.height() / 2f,
strokePaint);
//数量 0
canvas.drawText("0",
verticalXList.get(verticalXList.size() - 1) + dp2px(4),
volumeImgBot - dp2px(2),
strokePaint);
}
//分时图
private void drawInstant(Canvas canvas){
if (canvas == null || curvePath == null || viewDataList == null || strokePaint == null){
return;
}
curvePath.reset();
instantPath.reset();
float startX = (float) viewDataList.get(0).getCenterX();
float startY = (float) viewDataList.get(0).getCloseY();
curvePath.moveTo(startX, startY);
instantPath.moveTo(startX, startY);
int viewDataSize = viewDataList.size();
for (int i = 1; i < viewDataSize; i++) {
KData viewData = viewDataList.get(i);
curvePath.lineTo((float) viewData.getCenterX(), (float) viewData.getCloseY());
instantPath.lineTo((float) viewData.getCenterX(), (float) viewData.getCloseY());
if (i == viewDataSize - 1){
instantPath.lineTo(verticalXList.get(verticalXList.size() - 1), (float) viewData.getCloseY());
}
}
resetStrokePaint(0xff1aa3f0, 0);
canvas.drawPath(curvePath, strokePaint);
instantPath.lineTo(verticalXList.get(verticalXList.size() - 1), horizontalYList.get(horizontalYList.size() - 2));
instantPath.lineTo(startX, horizontalYList.get(horizontalYList.size() - 2));
instantPath.close();
LinearGradient gradient = new LinearGradient(0, (int)mMaxPriceY, 0,
horizontalYList.get(horizontalYList.size() - 2), 0x801aa3f0, 0x0d1aa3f0, Shader.TileMode.CLAMP);
instantFillPaint.setShader(gradient);
canvas.drawPath(instantPath, instantFillPaint);
}
private int dp2px(float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
private int sp2px(float spValue) {
final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private String formatDate(long timeStamp) {
if (timeStamp <= 0) {
return "";
}
SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm");
return format.format(new Date(timeStamp));
}
/**
* 设置小数位精度
*
* @param scale 保留几位小数
*/
private String setPrecision(Double num, int scale) {
BigDecimal bigDecimal = new BigDecimal(num);
return bigDecimal.setScale(scale, BigDecimal.ROUND_DOWN).toPlainString();
}
/**
* 按量级格式化价格
*/
private String formatKDataNum(double num) {
if (num < 1) {
return setPrecision(num, 6);
} else if (num < 10) {
return setPrecision(num, 4);
} else if (num < 100) {
return setPrecision(num, 3);
} else if (num < 10000) {
return setPrecision(num, 2);
} else if (num < 100000000) {
return setPrecision(num / 10000, 2) + "万";
} else {
return setPrecision(num / 100000000, 2) + "亿";
}
}
/**
* 按量级格式化数量
*/
private String formatVolNum(double num) {
if (num < 10000) {
return setPrecision(num, 2);
} else if (num < 100000000) {
return setPrecision(num / 10000, 2) + "万";
} else {
return setPrecision(num / 100000000, 2) + "亿";
}
}
private void resetStrokePaint(int colorId, int textSize) {
strokePaint.setColor(colorId);
strokePaint.setTextSize(sp2px(textSize));
}
}
================================================
FILE: app/src/main/java/com/example/admin/klineview/kline/Pointer.java
================================================
package com.example.admin.klineview.kline;
/**
* Created by xiesuichao on 2018/7/3.
*/
public class Pointer {
private float x;
private float y;
public Pointer() {
}
public Pointer(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
@Override
public String toString() {
return "Pointer{" +
"x=" + x +
", y=" + y +
'}';
}
}
================================================
FILE: app/src/main/java/com/example/admin/klineview/kline/QuotaThread.java
================================================
package com.example.admin.klineview.kline;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.annotation.NonNull;
import java.util.List;
/**
* 子线程计算五项数据
* Created by xiesuichao on 2018/8/18.
*/
public class QuotaThread extends HandlerThread implements Handler.Callback {
public static final int HANDLER_QUOTA_LIST = 100;
public static final int HANDLER_QUOTA_SINGLE = 101;
private Handler uiHandler;
private Handler workHandler;
public QuotaThread(String name, int priority) {
super(name, priority);
}
public void setUIHandler(Handler uiHandler) {
this.uiHandler = uiHandler;
}
public void quotaListCalculate(List dataList) {
if (workHandler == null) {
return;
}
Message message = Message.obtain(null, HANDLER_QUOTA_LIST);
message.obj = dataList;
workHandler.sendMessage(message);
}
public void quotaSingleCalculate(List dataList){
if (workHandler == null) {
return;
}
Message message = Message.obtain(null, HANDLER_QUOTA_SINGLE);
message.obj = dataList;
workHandler.sendMessage(message);
}
private void calculateKDataQuota(List dataList, boolean isEndData) {
QuotaUtil.initEma(dataList, isEndData);
QuotaUtil.initBoll(dataList, isEndData);
QuotaUtil.initMACD(dataList, isEndData);
QuotaUtil.initKDJ(dataList, isEndData);
QuotaUtil.initRSI(dataList, isEndData);
QuotaUtil.initMa(dataList, isEndData);
}
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
this.workHandler = new Handler(getLooper(), this);
}
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == HANDLER_QUOTA_LIST) {
handleData(msg, HANDLER_QUOTA_LIST, false);
}else if (msg.what == HANDLER_QUOTA_SINGLE){
handleData(msg, HANDLER_QUOTA_SINGLE, true);
}
return true;
}
private void handleData(Message msg, int whatId, boolean isEndData){
if (msg == null || uiHandler == null) {
return;
}
try {
List dataList = (List) msg.obj;
calculateKDataQuota(dataList, isEndData);
Message message = Message.obtain(null, whatId);
uiHandler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
================================================
FILE: app/src/main/java/com/example/admin/klineview/kline/QuotaUtil.java
================================================
package com.example.admin.klineview.kline;
import android.graphics.Path;
import java.util.ArrayList;
import java.util.List;
/**
* 五项数据计算公式
* Created by xiesuichao on 2018/8/12.
*/
public class QuotaUtil {
private static final int QUOTA_DAY5 = 5;
private static final int QUOTA_DAY10 = 10;
private static final int QUOTA_DAY30 = 30;
private static final float BEZIER_RATIO = 0.16f;
private static List cacheList = new ArrayList<>();
/**
* 初始化 MA5,MA10, MA30
*
* @param isEndData 是否是添加到list末尾的数据
*/
public static void initMa(List dataList, boolean isEndData) {
if (dataList == null || dataList.isEmpty() || cacheList == null) {
return;
}
cacheList.clear();
cacheList.addAll(dataList);
int size = dataList.size();
for (int i = 0; i < size; i++) {
if (i + QUOTA_DAY5 <= size) {
//priceMa5
dataList.get(i + QUOTA_DAY5 - 1).setPriceMa5(getPriceMa(cacheList.subList(i, i + QUOTA_DAY5)));
//volumeMa5
dataList.get(i + QUOTA_DAY5 - 1).setVolumeMa5(getVolumeMa(cacheList.subList(i, i + QUOTA_DAY5)));
}
if (i + QUOTA_DAY10 <= size) {
//priceMa10
dataList.get(i + QUOTA_DAY10 - 1).setPriceMa10(getPriceMa(cacheList.subList(i, i + QUOTA_DAY10)));
//volumeMa10
dataList.get(i + QUOTA_DAY10 - 1).setVolumeMa10(getVolumeMa(cacheList.subList(i, i + QUOTA_DAY10)));
}
if (i + QUOTA_DAY30 <= size) {
//priceMa30
if (dataList.get(i + QUOTA_DAY30 - 1).getPriceMa30() != 0 && !isEndData) {
break;
} else {
dataList.get(i + QUOTA_DAY30 - 1).setPriceMa30(getPriceMa(cacheList.subList(i, i + QUOTA_DAY30)));
}
}
dataList.get(i).setInitFinish(true);
}
}
private static double getVolumeMa(List dataList) {
if (dataList == null || dataList.isEmpty()) {
return -1;
}
double sum = 0;
for (KData data : dataList) {
sum += data.getVolume();
}
return sum / dataList.size();
}
private static double getPriceMa(List dataList) {
if (dataList == null || dataList.isEmpty()) {
return -1;
}
double sum = 0;
for (KData data : dataList) {
sum += data.getClosePrice();
}
return sum / dataList.size();
}
/**
* 初始化 EMA5, EMA10, EMA30
*/
public static void initEma(List dataList, boolean isEndData) {
if (dataList == null || dataList.isEmpty()) {
return;
}
double lastEma5 = dataList.get(0).getClosePrice();
double lastEma10 = dataList.get(0).getClosePrice();
double lastEma30 = dataList.get(0).getClosePrice();
dataList.get(0).setEma5(lastEma5);
dataList.get(0).setEma10(lastEma10);
dataList.get(0).setEma30(lastEma30);
int size = dataList.size();
for (int i = 1; i < size; i++) {
if (dataList.get(i).getEma30() != 0 && !isEndData) {
break;
}
double currentEma5 = 2 * (dataList.get(i).getClosePrice() - lastEma5) / (QUOTA_DAY5 + 1) + lastEma5;
double currentEma10 = 2 * (dataList.get(i).getClosePrice() - lastEma10) / (QUOTA_DAY10 + 1) + lastEma10;
double currentEma30 = 2 * (dataList.get(i).getClosePrice() - lastEma30) / (QUOTA_DAY30 + 1) + lastEma30;
dataList.get(i).setEma5(currentEma5);
dataList.get(i).setEma10(currentEma10);
dataList.get(i).setEma30(currentEma30);
lastEma5 = currentEma5;
lastEma10 = currentEma10;
lastEma30 = currentEma30;
}
}
/**
* BOLL(n)计算公式:
* MA=n日内的收盘价之和÷n。
* MD=(n-1)日的平方根(C-MA)的两次方之和除以n
* MB=(n-1)日的MA
* UP=MB+k×MD
* DN=MB-k×MD
* K为参数,可根据股票的特性来做相应的调整,一般默认为2
*
* @param dataList 数据集合
* @param period 周期,一般为26
* @param k 参数,可根据股票的特性来做相应的调整,一般默认为2
*/
public static void initBOLL(List dataList, int period, int k, boolean isEndData) {
if (dataList == null || dataList.isEmpty()
|| period < 0 || period > dataList.size() - 1) {
return;
}
double mb;//上轨线
double up;//中轨线
double dn;//下轨线
//n日
double sum = 0;
//n-1日
double sum2 = 0;
//n日MA
double ma;
//n-1日MA
double ma2;
double md;
int size = dataList.size();
for (int i = 0; i < size; i++) {
if (dataList.get(i).getBollMb() != 0 && !isEndData) {
break;
}
KData quotes = dataList.get(i);
sum += quotes.getClosePrice();
sum2 += quotes.getClosePrice();
if (i > period - 1)
sum -= dataList.get(i - period).getClosePrice();
if (i > period - 2)
sum2 -= dataList.get(i - period + 1).getClosePrice();
//这个范围不计算,在View上的反应就是不显示这个范围的boll线
if (i < period - 1)
continue;
ma = sum / period;
ma2 = sum2 / (period - 1);
md = 0;
for (int j = i + 1 - period; j <= i; j++) {
//n-1日
md += Math.pow(dataList.get(j).getClosePrice() - ma, 2);
}
md = Math.sqrt(md / period);
//(n-1)日的MA
mb = ma2;
up = mb + k * md;
dn = mb - k * md;
quotes.setBollMb(mb);
quotes.setBollUp(up);
quotes.setBollDn(dn);
}
}
public static void initBoll(List dataList, boolean isEndData) {
initBOLL(dataList, 26, 2, isEndData);
}
/**
* MACD
*
* @param dataList
* @param fastPeriod 日快线移动平均,标准为12,按照标准即可
* @param slowPeriod 日慢线移动平均,标准为26,可理解为天数
* @param signalPeriod 日移动平均,标准为9,按照标准即可
*/
public static void initMACD(List dataList, int fastPeriod, int slowPeriod, int signalPeriod, boolean isEndData) {
if (dataList == null || dataList.isEmpty()) {
return;
}
double preEma_12 = 0;
double preEma_26 = 0;
double preDEA = 0;
double ema_12 = 0;
double ema_26 = 0;
double dea = 0;
double dif = 0;
double macd = 0;
int size = dataList.size();
for (int i = 0; i < size; i++) {
if (dataList.get(i).getMacd() != 0 && !isEndData) {
break;
}
ema_12 = preEma_12 * (fastPeriod - 1) / (fastPeriod + 1) + dataList.get(i).getClosePrice() * 2 / (fastPeriod + 1);
ema_26 = preEma_26 * (slowPeriod - 1) / (slowPeriod + 1) + dataList.get(i).getClosePrice() * 2 / (slowPeriod + 1);
dif = ema_12 - ema_26;
dea = preDEA * (signalPeriod - 1) / (signalPeriod + 1) + dif * 2 / (signalPeriod + 1);
macd = 2 * (dif - dea);
preEma_12 = ema_12;
preEma_26 = ema_26;
preDEA = dea;
dataList.get(i).setMacd(macd);
dataList.get(i).setDea(dea);
dataList.get(i).setDif(dif);
}
}
public static void initMACD(List dataList, boolean isEndData) {
initMACD(dataList, 12, 26, 9, isEndData);
}
/**
* KDJ
*
* @param n1 标准值9
* @param n2 标准值3
* @param n3 标准值3
*/
public static void initKDJ(List dataList, int n1, int n2, int n3, boolean isEndData) {
if (dataList == null || dataList.isEmpty()) {
return;
}
//K值
double[] mK = new double[dataList.size()];
//D值
double[] mD = new double[dataList.size()];
//J值
double jValue;
double highValue = dataList.get(0).getMaxPrice();
double lowValue = dataList.get(0).getMinPrice();
//记录最高价位置
int highPosition = 0;
//记录最低价位置
int lowPosition = 0;
double rSV = 0.0;
int size = dataList.size();
for (int i = 0; i < size; i++) {
if (dataList.get(i).getK() != 0 && !isEndData) {
break;
}
if (i == 0) {
// mK[0] = 33.33;
// mD[0] = 11.11;
// jValue = 77.78;
mK[0] = 50;
mD[0] = 50;
jValue = 50;
} else {
//对最高价和最低价赋值
if (highValue <= dataList.get(i).getMaxPrice()) {
highValue = dataList.get(i).getMaxPrice();
highPosition = i;
}
if (lowValue >= dataList.get(i).getMinPrice()) {
lowValue = dataList.get(i).getMinPrice();
lowPosition = i;
}
if (i > (n1 - 1)) {
//判断存储的最高价是否高于当前最高价
if (highValue > dataList.get(i).getMaxPrice()) {
//判断最高价是不是在最近n天内,若不在最近n天内,则从最近n天找出最高价并赋值
if (highPosition < (i - (n1 - 1))) {
highValue = dataList.get(i - (n1 - 1)).getMaxPrice();
for (int j = (i - (n1 - 2)); j <= i; j++) {
if (highValue <= dataList.get(j).getMaxPrice()) {
highValue = dataList.get(j).getMaxPrice();
highPosition = j;
}
}
}
}
if ((lowValue < dataList.get(i).getMinPrice())) {
if (lowPosition < i - (n1 - 1)) {
lowValue = dataList.get(i).getMinPrice();
for (int k = i - (n1 - 2); k <= i; k++) {
if (lowValue >= dataList.get(k).getMinPrice()) {
lowValue = dataList.get(k).getMinPrice();
lowPosition = k;
}
}
}
}
}
if (highValue != lowValue) {
rSV = (dataList.get(i).getClosePrice() - lowValue) / (highValue - lowValue) * 100;
}
mK[i] = (mK[i - 1] * (n2 - 1) + rSV) / n2;
mD[i] = (mD[i - 1] * (n3 - 1) + mK[i]) / n3;
jValue = 3 * mK[i] - 2 * mD[i];
}
dataList.get(i).setK(mK[i]);
dataList.get(i).setD(mD[i]);
dataList.get(i).setJ(jValue);
}
}
public static void initKDJ(List dataList, boolean isEndData) {
initKDJ(dataList, 9, 3, 3, isEndData);
}
/**
* 初始化RSI
*
* @param period1 标准值6
* @param period2 标准值12
* @param period3 标准值24
*/
public static void initRSI(List dataList, int period1, int period2, int period3, boolean isEndData) {
if (dataList == null || dataList.isEmpty()) {
return;
}
double upRateSum;
int upRateCount;
double dnRateSum;
int dnRateCount;
int size = dataList.size();
for (int i = 0; i < size; i++) {
if (dataList.get(i).getRs3() != 0 && !isEndData) {
break;
}
upRateSum = 0;
upRateCount = 0;
dnRateSum = 0;
dnRateCount = 0;
if (i >= period1 - 1) {
for (int x = i; x >= i + 1 - period1; x--) {
if (dataList.get(x).getUpDnRate() >= 0) {
upRateSum += dataList.get(x).getUpDnRate();
upRateCount++;
} else {
dnRateSum += dataList.get(x).getUpDnRate();
dnRateCount++;
}
}
double avgUpRate = 0;
double avgDnRate = 0;
if (upRateSum > 0) {
avgUpRate = upRateSum / upRateCount;
}
if (dnRateSum < 0) {
avgDnRate = dnRateSum / dnRateCount;
}
dataList.get(i).setRs1(avgUpRate / (avgUpRate + Math.abs(avgDnRate)) * 100);
}
upRateSum = 0;
upRateCount = 0;
dnRateSum = 0;
dnRateCount = 0;
if (i >= period2 - 1) {
for (int x = i; x >= i + 1 - period2; x--) {
if (dataList.get(x).getUpDnRate() >= 0) {
upRateSum += dataList.get(x).getUpDnRate();
upRateCount++;
} else {
dnRateSum += dataList.get(x).getUpDnRate();
dnRateCount++;
}
}
double avgUpRate = 0;
double avgDnRate = 0;
if (upRateSum > 0) {
avgUpRate = upRateSum / upRateCount;
}
if (dnRateSum < 0) {
avgDnRate = dnRateSum / dnRateCount;
}
dataList.get(i).setRs2(avgUpRate / (avgUpRate + Math.abs(avgDnRate)) * 100);
}
upRateSum = 0;
upRateCount = 0;
dnRateSum = 0;
dnRateCount = 0;
if (i >= period3 - 1) {
for (int x = i; x >= i + 1 - period3; x--) {
if (dataList.get(x).getUpDnRate() >= 0) {
upRateSum += dataList.get(x).getUpDnRate();
upRateCount++;
} else {
dnRateSum += dataList.get(x).getUpDnRate();
dnRateCount++;
}
}
double avgUpRate = 0;
double avgDnRate = 0;
if (upRateSum > 0) {
avgUpRate = upRateSum / upRateCount;
}
if (dnRateSum < 0) {
avgDnRate = dnRateSum / dnRateCount;
}
dataList.get(i).setRs3(avgUpRate / (avgUpRate + Math.abs(avgDnRate)) * 100);
}
}
}
public static void initRSI(List dataList, boolean isEndData) {
initRSI(dataList, 6, 12, 24, isEndData);
}
/**
* 三阶贝塞尔曲线控制点
*
* @param pointList
* @param path
*/
public static void setBezierPath(List pointList, Path path) {
if (path == null){
return;
}
path.reset();
if (pointList == null || pointList.isEmpty()) {
return;
}
path.moveTo(pointList.get(0).getX(), pointList.get(0).getY());
Pointer leftControlPointer = new Pointer();
Pointer rightControlPointer = new Pointer();
int size = pointList.size();
for (int i = 0; i < size; i++) {
if (i == 0 && size > 2) {
leftControlPointer.setX(pointList.get(i).getX() + BEZIER_RATIO * (pointList.get(i + 1).getX()
- pointList.get(0).getX()));
leftControlPointer.setY(pointList.get(i).getY() + BEZIER_RATIO * (pointList.get(i + 1).getY()
- pointList.get(0).getY()));
rightControlPointer.setX(pointList.get(i + 1).getX() - BEZIER_RATIO * (pointList.get(i + 2).getX()
- pointList.get(i).getX()));
rightControlPointer.setY(pointList.get(i + 1).getY() - BEZIER_RATIO * (pointList.get(i + 2).getY()
- pointList.get(i).getY()));
} else if (i == size - 2 && i > 1) {
leftControlPointer.setX(pointList.get(i).getX() + BEZIER_RATIO * (pointList.get(i + 1).getX()
- pointList.get(i - 1).getX()));
leftControlPointer.setY(pointList.get(i).getY() + BEZIER_RATIO * (pointList.get(i + 1).getY()
- pointList.get(i - 1).getY()));
rightControlPointer.setX(pointList.get(i + 1).getX() - BEZIER_RATIO * (pointList.get(i + 1).getX()
- pointList.get(i).getX()));
rightControlPointer.setY(pointList.get(i + 1).getY() - BEZIER_RATIO * (pointList.get(i + 1).getY()
- pointList.get(i).getY()));
} else if (i > 0 && i < size - 2) {
leftControlPointer.setX(pointList.get(i).getX() + BEZIER_RATIO * (pointList.get(i + 1).getX()
- pointList.get(i - 1).getX()));
leftControlPointer.setY(pointList.get(i).getY() + BEZIER_RATIO * (pointList.get(i + 1).getY()
- pointList.get(i - 1).getY()));
rightControlPointer.setX(pointList.get(i + 1).getX() - BEZIER_RATIO * (pointList.get(i + 2).getX()
- pointList.get(i).getX()));
rightControlPointer.setY(pointList.get(i + 1).getY() - BEZIER_RATIO * (pointList.get(i + 2).getY()
- pointList.get(i).getY()));
}
if (i < size - 1) {
path.cubicTo(leftControlPointer.getX(), leftControlPointer.getY(),
rightControlPointer.getX(), rightControlPointer.getY(),
pointList.get(i + 1).getX(), pointList.get(i + 1).getY());
}
}
}
public static void setLinePath(List pointerList, Path path) {
if (path == null){
return;
}
if (pointerList == null || pointerList.size() <= 1) {
return;
}
path.moveTo(pointerList.get(0).getX(), pointerList.get(0).getY());
int size = pointerList.size();
for (int i = 1; i < size; i++) {
path.lineTo(pointerList.get(i).getX(), pointerList.get(i).getY());
}
}
}
================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_depth.xml
================================================
================================================
FILE: app/src/main/res/layout/activity_main.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/attrs.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#3F51B5
#303F9F
#FF4081
#ef462d
#ce2b13
#ffffff
#f5f5f5
#666666
#999999
#308BE9
#eeeeee
#dddddd
#fc8714
#4e677a
#2789ba
#415c71
#fdbc20
#4db7f3
#45afe1
#1aa3f0
#5fbff5
#edf9fe
#000000
#333333
#ff000000
#f2000000
#e6000000
#d9000000
#cc000000
#bf000000
#b3000000
#a6000000
#99000000
#8c000000
#80000000
#73000000
#66000000
#59000000
#4d000000
#40000000
#33000000
#26000000
#1a000000
#0d000000
#ccffffff
#b3ffffff
#4dffffff
#33ffffff
#80fc1f02
#b3fdbc20
#00000000
#ffffffff
#ff9e9e9e
#ff239efe
#ffff0000
================================================
FILE: app/src/main/res/values/strings.xml
================================================
KLineView
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/test/java/com/example/admin/klineview/ExampleUnitTest.java
================================================
package com.example.admin.klineview;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 29 11:21:17 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app'