Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1100,8 +1100,14 @@ private void drawBuySellMark(java.util.Map<String, Object> markData, KLineEntity
float candleHigh = candlestick.getHighPrice();
float highY = yFromValue(candleHigh); // Same method used by MainDraw.drawCandle

// Circle properties - diameter should match candlestick width
float circleRadius = mPointWidth * 0.4f; // Use 80% of candlestick width for diameter
// Circle properties - diameter should match candlestick width, but never smaller
// than a dynamic floor of buySellMarkMinWidthMultiplier * the candle width when fully
// zoomed out. The on-screen candle width is mPointWidth * mScaleX, so at the most
// zoomed-out scale (mScaleXMin) it is mPointWidth * mScaleXMin; the floor stays
// constant regardless of zoom.
float candleWidthAtMaxZoomOut = mPointWidth * mScaleXMin;
float minCircleRadius = candleWidthAtMaxZoomOut * configManager.buySellMarkMinWidthMultiplier / 2f;
float circleRadius = Math.max(mPointWidth * 0.5f, minCircleRadius);

// Position both marks above the candlestick, with collision avoidance
float markCenterY;
Expand All @@ -1128,20 +1134,27 @@ private void drawBuySellMark(java.util.Map<String, Object> markData, KLineEntity
circleColor = configManager.decreaseColor; // Use same color as decreasing candlesticks
}

// The canvas is horizontally scaled by mScaleX (see drawK). Counter that scale
// around the mark's center so it stays a perfect circle at any zoom level
// instead of stretching into an ellipse.
canvas.save();
canvas.translate(candleX, markCenterY);
canvas.scale(1f / mScaleX, 1f);

// Create paint for circle
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(circleColor);
circlePaint.setStyle(Paint.Style.FILL);

// Draw circle
canvas.drawCircle(candleX, markCenterY, circleRadius, circlePaint);
canvas.drawCircle(0, 0, circleRadius, circlePaint);

// Draw border
Paint borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
borderPaint.setColor(circleColor);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(2.0f);
canvas.drawCircle(candleX, markCenterY, circleRadius, borderPaint);
canvas.drawCircle(0, 0, circleRadius, borderPaint);

// Draw text inside circle
String markText = "buy".equals(type) ? "B" : "S";
Expand All @@ -1153,9 +1166,11 @@ private void drawBuySellMark(java.util.Map<String, Object> markData, KLineEntity

// Calculate text position (center of circle)
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float textY = markCenterY - (fontMetrics.ascent + fontMetrics.descent) / 2;
float textY = -(fontMetrics.ascent + fontMetrics.descent) / 2;

canvas.drawText(markText, candleX, textY, textPaint);
canvas.drawText(markText, 0, textY, textPaint);

canvas.restore();
}

public int dp2px(float dp) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ public class HTKLineConfigManager {

public float minVisibleCandles = 5;

// Minimum buy/sell mark diameter expressed as a multiple of the candle width when
// fully zoomed out. The mark never shrinks below this floor regardless of zoom.
public float buySellMarkMinWidthMultiplier = 3;

public int minuteVolumeCandleColor = Color.RED;

public float minuteVolumeCandleWidth = 1.5f;
Expand Down Expand Up @@ -462,6 +466,11 @@ public void reloadOptionList(Map optionList) {
this.minVisibleCandles = minVisibleCandlesValue.floatValue();
}

Number buySellMarkMinWidthMultiplierValue = (Number)configList.get("buySellMarkMinWidthMultiplier");
if (buySellMarkMinWidthMultiplierValue != null) {
this.buySellMarkMinWidthMultiplier = buySellMarkMinWidthMultiplierValue.floatValue();
}

this.fontFamily = (configList.get("fontFamily")).toString();
this.textColor = ((Number) configList.get("textColor")).intValue();
this.headerTextFontSize = ((Number)configList.get("headerTextFontSize")).floatValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public abstract class ScrollAndScaleView extends RelativeLayout implements

protected float mScaleX = 1;

protected float mScaleXMax = 2f;
protected float mScaleXMax = 2.5f;

protected float mScaleXMin = 0.5f;

Expand Down
2 changes: 2 additions & 0 deletions example/utils/businessLogic.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,8 @@ export const packOptionList = (modelArray, appState, shouldScrollToEnd = true, u
candleWidth: 6 * pixelRatio,
candleCornerRadius: candleCornerRadius * pixelRatio,
minVisibleCandles: minVisibleCandles || 5,
buySellMarkMinWidthMultiplier: 3, // Min mark diameter = N * candle width when fully zoomed out

minuteVolumeCandleColor: processColor(showVolumeChart ? COLOR(0.0941176, 0.509804, 0.831373, 0.501961) : 'transparent'),
minuteVolumeCandleWidth: showVolumeChart ? 2 * pixelRatio : 0,
macdCandleWidth: 1 * pixelRatio,
Expand Down
5 changes: 5 additions & 0 deletions ios/Classes/HTKLineConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ class HTKLineConfigManager: NSObject {

var minVisibleCandles: CGFloat = 5

// Minimum buy/sell mark diameter expressed as a multiple of the candle width when
// fully zoomed out. The mark never shrinks below this floor regardless of zoom.
var buySellMarkMinWidthMultiplier: CGFloat = 3

var minuteVolumeCandleWidth: CGFloat = 0

var _minuteVolumeCandleWidth: CGFloat = 0
Expand Down Expand Up @@ -451,6 +455,7 @@ class HTKLineConfigManager: NSObject {
_macdCandleWidth = configList["macdCandleWidth"] as? CGFloat ?? 0
candleCornerRadius = configList["candleCornerRadius"] as? CGFloat ?? 0
minVisibleCandles = configList["minVisibleCandles"] as? CGFloat ?? 5
buySellMarkMinWidthMultiplier = configList["buySellMarkMinWidthMultiplier"] as? CGFloat ?? 3
reloadScrollViewScale(1)
paddingTop = configList["paddingTop"] as? CGFloat ?? 0
paddingRight = configList["paddingRight"] as? CGFloat ?? 0
Expand Down
11 changes: 9 additions & 2 deletions ios/Classes/HTKLineView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -976,8 +976,15 @@ class HTKLineView: UIScrollView {
let normalizedPrice = (markPrice - mainMinMaxRange.lowerBound) / priceRange
let markY = mainBaseY + mainHeight - CGFloat(normalizedPrice) * mainHeight

// Make circle radius match candlestick width (same as Android logic)
let circleRadius: CGFloat = configManager.itemWidth * 0.4 // 80% of candlestick width for diameter
// Make circle radius match candlestick width (same as Android logic), but never
// smaller than a dynamic floor of buySellMarkMinWidthMultiplier * the candle width
// when fully zoomed out. `_itemWidth` is the unscaled candle width and `minScale`
// (0.3, see pinchSelector) is the most zoomed-out scale, so the floor stays constant
// regardless of zoom.
let minScale: CGFloat = 0.3
let candleWidthAtMaxZoomOut = configManager._itemWidth * minScale
let minCircleRadius = candleWidthAtMaxZoomOut * configManager.buySellMarkMinWidthMultiplier / 2
let circleRadius: CGFloat = max(configManager.itemWidth * 0.4, minCircleRadius)

// Position both marks above the candlestick, with collision avoidance
let markCenterY: CGFloat
Expand Down
Loading