Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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 @@ -1428,11 +1428,25 @@ public int getMinScrollX() {
return 0;
}

public int getExtraScrollX() {
return (int) (mOverScrollRange / mScaleX);
}

public int getMaxScrollX() {
int contentWidth = (int) Math.max((mDataLen - (mWidth - configManager.paddingRight) / mScaleX), 0);
int contentWidth = (int) Math.max((mDataLen - (mWidth - configManager.paddingRight) / mScaleX + getExtraScrollX()), 0);
return contentWidth;
}

@Override
protected float getMinVisibleCandles() {
return configManager.minVisibleCandles;
}

@Override
public float getDataLength() {
return mDataLen;
}

/**
* 在主区域画线
*
Expand Down Expand Up @@ -1989,19 +2003,20 @@ public boolean onSingleTapUp(MotionEvent e) {
}

public void smoothScrollToEnd() {
int endScrollX = getMaxScrollX();
int currentScrollX = getScrollOffset();
int distance = endScrollX - currentScrollX;

// android.util.Log.d("BaseKLineChartView", "smoothScrollToEnd DEBUG:");
// android.util.Log.d("BaseKLineChartView", " mDataLen=" + mDataLen + ", mItemCount=" + mItemCount + ", mPointWidth=" + mPointWidth);
// android.util.Log.d("BaseKLineChartView", " mWidth=" + mWidth + ", mScaleX=" + mScaleX + ", paddingRight=" + configManager.paddingRight);
// android.util.Log.d("BaseKLineChartView", " current=" + currentScrollX + ", end=" + endScrollX + ", distance=" + distance);

// Always scroll to end position, regardless of current position
// This ensures we go to the rightmost position to show the latest data
setScrollX(endScrollX);
// android.util.Log.d("BaseKLineChartView", "Set scroll position to end: " + endScrollX);
int screenWidthInLogicalUnits = getExtraScrollX();
int endScrollX = (int)(mDataLen + configManager.paddingRight - screenWidthInLogicalUnits);

setScrollXWithoutMinCandlesLimit(Math.max(0, endScrollX));
}

/**
* Set scroll position without applying minVisibleCandles limit
*/
private void setScrollXWithoutMinCandlesLimit(int scrollX) {
int oldX = this.mScrollX;
this.mScrollX = Math.max(0, Math.min(scrollX, (int)mDataLen));
onScrollChanged(this.mScrollX, 0, oldX, 0);
invalidate();
}

// Public getter methods for accessing protected fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public class HTKLineConfigManager {

public float candleCornerRadius = 0;

public float minVisibleCandles = 5;

public int minuteVolumeCandleColor = Color.RED;

public float minuteVolumeCandleWidth = 1.5f;
Expand Down Expand Up @@ -455,6 +457,11 @@ public void reloadOptionList(Map optionList) {
this.candleCornerRadius = candleCornerRadiusValue.floatValue();
}

Number minVisibleCandlesValue = (Number)configList.get("minVisibleCandles");
if (minVisibleCandlesValue != null) {
this.minVisibleCandles = minVisibleCandlesValue.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 @@ -17,6 +17,12 @@ public abstract class ScrollAndScaleView extends RelativeLayout implements
GestureDetector.OnGestureListener,
ScaleGestureDetector.OnScaleGestureListener {
protected int mScrollX = 0;

/**
* Get minimum visible candles
* @return minimum number of candles that should be visible
*/
protected abstract float getMinVisibleCandles();
protected GestureDetectorCompat mDetector;
protected ScaleGestureDetector mScaleDetector;

Expand Down Expand Up @@ -265,6 +271,20 @@ public boolean isTouch() {
*/
public abstract int getMaxScrollX();

/**
* Get the point width
*
* @return
*/
public abstract float getPointWidth();

/**
* Get the total data length (itemCount * pointWidth)
*
* @return
*/
public abstract float getDataLength();

/**
* Set ScrollX
*
Expand All @@ -286,7 +306,6 @@ public boolean isMultipleTouch() {

protected void checkAndFixScrollX() {
int contentSizeWidth = (getMaxScrollX());

if (mScrollX < getMinScrollX()) {
mScrollX = getMinScrollX();
mScroller.forceFinished(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,16 @@ public void reloadConfigManager() {
klineView.setMTextSize(klineView.configManager.candleTextFontSize);
klineView.setMTextColor(klineView.configManager.candleTextColor);
klineView.reloadColor();
Boolean isEnd = klineView.getScrollOffset() >= klineView.getMaxScrollX();
int previousScrollX = klineView.getScrollOffset();
klineView.notifyChanged();

if (klineView.configManager.shouldAdjustScrollPosition) {
// 调整滚动位置以补偿新增的数据
int newScrollX = previousScrollX + klineView.configManager.scrollPositionAdjustment;
klineView.setScrollX(newScrollX);
} else if (isEnd || klineView.configManager.shouldScrollToEnd) {
klineView.setScrollX(klineView.getMaxScrollX());
} else if (klineView.configManager.shouldScrollToEnd) {
int scrollToEnd = klineView.getMaxScrollX() - klineView.getExtraScrollX();
klineView.setScrollX(scrollToEnd);
}
Comment thread
rturtu marked this conversation as resolved.


Expand Down Expand Up @@ -410,7 +410,7 @@ public void addCandlesticksAtTheEnd(ReadableArray candlesticksArray) {

try {
// Check if user is currently at the end of the chart
boolean wasAtEnd = klineView.getScrollOffset() >= klineView.getMaxScrollX() - 10;
boolean wasAtEnd = klineView.getScrollOffset() >= klineView.getMaxScrollX() - 10 - klineView.getExtraScrollX();

// Get existing model for preserving indicator lists structure
KLineEntity templateEntity = null;
Expand Down
7 changes: 5 additions & 2 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {


const App = () => {
const MIN_VISIBLE_CANDLES = 10
const [isDarkTheme, setIsDarkTheme] = useState(false)
const [selectedTimeType, setSelectedTimeType] = useState(2) // Corresponds to 1 minute
const [selectedMainIndicator, setSelectedMainIndicator] = useState(1) // Corresponds to MA (1=MA, 2=BOLL)
Expand Down Expand Up @@ -167,7 +168,8 @@ const App = () => {
lastDataLength,
currentScrollPosition,
showVolumeChart,
candleCornerRadius
candleCornerRadius,
minVisibleCandles: MIN_VISIBLE_CANDLES
}, shouldScrollToEnd, kLineViewRef.current ? true : false)
setOptionListValue(newOptionList)
}, [klineData, selectedMainIndicator, selectedSubIndicator, showVolumeChart, isDarkTheme, selectedTimeType, selectedDrawTool, showIndicatorSelector, showTimeSelector, showDrawToolSelector, drawShouldContinue, optionList, lastDataLength, currentScrollPosition, candleCornerRadius])
Expand Down Expand Up @@ -216,7 +218,8 @@ const App = () => {
lastDataLength,
currentScrollPosition,
showVolumeChart,
candleCornerRadius
candleCornerRadius,
minVisibleCandles: MIN_VISIBLE_CANDLES
}, false)

// Calculate scroll distance adjustment needed (based on item width)
Expand Down
4 changes: 3 additions & 1 deletion example/utils/businessLogic.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ export const packOptionList = (modelArray, appState, shouldScrollToEnd = true, u
selectedDrawTool,
showVolumeChart,
candleCornerRadius,
drawShouldContinue
drawShouldContinue,
minVisibleCandles
} = appState

const theme = ThemeManager.getCurrentTheme(isDarkTheme)
Expand Down Expand Up @@ -355,6 +356,7 @@ export const packOptionList = (modelArray, appState, shouldScrollToEnd = true, u
itemWidth: 8 * pixelRatio,
candleWidth: 6 * pixelRatio,
candleCornerRadius: candleCornerRadius * pixelRatio,
minVisibleCandles: minVisibleCandles || 5,
minuteVolumeCandleColor: processColor(showVolumeChart ? COLOR(0.0941176, 0.509804, 0.831373, 0.501961) : 'transparent'),
minuteVolumeCandleWidth: showVolumeChart ? 2 * pixelRatio : 0,
macdCandleWidth: 1 * pixelRatio,
Expand Down
3 changes: 3 additions & 0 deletions ios/Classes/HTKLineConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ class HTKLineConfigManager: NSObject {

var candleCornerRadius: CGFloat = 0

var minVisibleCandles: CGFloat = 5

var minuteVolumeCandleWidth: CGFloat = 0

var _minuteVolumeCandleWidth: CGFloat = 0
Expand Down Expand Up @@ -448,6 +450,7 @@ class HTKLineConfigManager: NSObject {
_minuteVolumeCandleWidth = configList["minuteVolumeCandleWidth"] as? CGFloat ?? 0
_macdCandleWidth = configList["macdCandleWidth"] as? CGFloat ?? 0
candleCornerRadius = configList["candleCornerRadius"] as? CGFloat ?? 0
minVisibleCandles = configList["minVisibleCandles"] as? CGFloat ?? 5
reloadScrollViewScale(1)
paddingTop = configList["paddingTop"] as? CGFloat ?? 0
paddingRight = configList["paddingRight"] as? CGFloat ?? 0
Expand Down
29 changes: 25 additions & 4 deletions ios/Classes/HTKLineView.swift
Comment thread
rturtu marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,25 @@ class HTKLineView: UIScrollView {
childDraw = wrDraw
}

let isEnd = contentOffset.x + 1 + bounds.size.width >= contentSize.width
let previousContentOffset = contentOffset.x
reloadContentSize()


let rightScreenOffset = contentOffset.x + bounds.size.width + 1
let lastCandlestickOffset = contentSize.width - bounds.size.width - configManager.itemWidth / 2
// how many candlesticks +- should it consider to auto scroll to end when new data is added
// if the user is over-scrolled, then the candlesticks have space to appear on screen without scrolling
// and at some point it will enter this range and become auto-scrolling to end
let candlesticksCountOffset = 1.5 * configManager.itemWidth
// Extra spacing at the end is bounds.size.width
let isEnd = lastCandlestickOffset - candlesticksCountOffset <= rightScreenOffset && rightScreenOffset <= lastCandlestickOffset + candlesticksCountOffset

if configManager.shouldAdjustScrollPosition {
// Adjust scroll position to compensate for newly added data
let newContentOffset = previousContentOffset + configManager.scrollPositionAdjustment
reloadContentOffset(newContentOffset, false)
} else if configManager.shouldScrollToEnd || isEnd {
let toEndContentOffset = contentSize.width - bounds.size.width
let toEndContentOffset = contentSize.width - 2 * bounds.size.width
let distance = abs(contentOffset.x - toEndContentOffset)
let animated = distance <= configManager.itemWidth
reloadContentOffset(toEndContentOffset, animated)
Expand Down Expand Up @@ -183,16 +192,19 @@ class HTKLineView: UIScrollView {
let contentWidth =
configManager.itemWidth * CGFloat(configManager.modelArray.count)
+ configManager.paddingRight
+ bounds.size.width
contentSize = CGSize.init(width: contentWidth, height: frame.size.height)
}

func reloadContentOffset(_ contentOffsetX: CGFloat, _ animated: Bool = false) {
let offsetX = max(0, min(contentOffsetX, contentSize.width - bounds.size.width))
let allCandlesWidth = configManager.itemWidth * CGFloat(configManager.modelArray.count)
let maxAllowedOffset = max(0, allCandlesWidth - configManager.minVisibleCandles * configManager.itemWidth)
let offsetX = max(0, min(contentOffsetX, maxAllowedOffset))
setContentOffset(CGPoint.init(x: offsetX, y: 0), animated: animated)
}

func smoothScrollToEnd() {
let endOffsetX = contentSize.width - bounds.size.width
let endOffsetX = contentSize.width - 2 * bounds.size.width
reloadContentOffset(endOffsetX, true)
}

Expand Down Expand Up @@ -1034,7 +1046,16 @@ class HTKLineView: UIScrollView {
extension HTKLineView: UIScrollViewDelegate {

func scrollViewDidScroll(_ scrollView: UIScrollView) {
let allCandlesWidth = configManager.itemWidth * CGFloat(configManager.modelArray.count)
let maxAllowedOffset = max(0, allCandlesWidth - configManager.minVisibleCandles * configManager.itemWidth)

let contentOffsetX = scrollView.contentOffset.x

if contentOffsetX > maxAllowedOffset {
scrollView.contentOffset.x = maxAllowedOffset
return
Comment thread
rturtu marked this conversation as resolved.
}
Comment thread
rturtu marked this conversation as resolved.

var visibleStartIndex = Int(floor(contentOffsetX / configManager.itemWidth))
var visibleEndIndex = Int(
ceil((contentOffsetX + scrollView.bounds.size.width) / configManager.itemWidth))
Expand Down
Loading