From b59d47e15a03171f5853332b498f60f6494874f8 Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 15:42:09 +0300 Subject: [PATCH 1/8] added codeowners and PR template --- .github/CODEOWNERS | 1 + .github/PULL_REQUEST_TEMPLATE.md | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..cc37007 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @furkando @NGabuaeva @EdiOanceaV2 @sinantalhakosar @rturtu @hikmet-demir @Matteoverzotti @robertbarbu27 @snowtema \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..2d04a37 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,14 @@ +## Proposed Change + +_Describe what this pull request does?_ + +## To-Do + +_Add to-do items here_ + +- [ ] Does it need backend deployment? +- [ ] Does it need confirmation on calculations? + +## Screenshot + +_Add screenshots or short video_ From 6ffdf32bf4fece0334c0744163de51f569ef688f Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 15:56:28 +0300 Subject: [PATCH 2/8] apply patch for fix scrolling problem --- .../klinechart/BaseKLineChartView.java | 14 ++++++++++- .../klinechart/ScrollAndScaleView.java | 10 ++++++-- .../container/HTKLineContainerView.java | 6 ++--- ios/Classes/HTKLineView.swift | 23 +++++++++++++++---- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java index 2f935a6..e007561 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java @@ -944,6 +944,18 @@ public void onLongPress(MotionEvent e) { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { android.util.Log.d("BaseKLineChartView", "onScrollChanged() - l=" + l + ", t=" + t + ", oldl=" + oldl + ", oldt=" + oldt); + + int maxScroll = getMaxScrollX(); + int screenWidth = (int)(getWidth() / getScaleX()); + int realDataMaxScroll = maxScroll - screenWidth; + int maxAllowedScroll = realDataMaxScroll - (int)(MIN_VISIBLE_CANDLES * getmPointWidth()); + int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); + + if (l > normalizedMaxAllowedScroll) { + setScrollX(normalizedMaxAllowedScroll); + return; + } + super.onScrollChanged(l, t, oldl, oldt); } @@ -1110,7 +1122,7 @@ public int getMinScrollX() { } public int getMaxScrollX() { - int contentWidth = (int) Math.max((mDataLen - (mWidth - configManager.paddingRight) / mScaleX), 0); + int contentWidth = (int) Math.max((mDataLen - (mWidth - configManager.paddingRight) / mScaleX + mWidth / mScaleX), 0); return contentWidth; } diff --git a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java index 8ab1668..42cbb5c 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java @@ -16,6 +16,7 @@ public abstract class ScrollAndScaleView extends RelativeLayout implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener { + protected static final int MIN_VISIBLE_CANDLES = 5; protected int mScrollX = 0; protected GestureDetectorCompat mDetector; protected ScaleGestureDetector mScaleDetector; @@ -271,8 +272,13 @@ public boolean isTouch() { * @param scrollX */ public void setScrollX(int scrollX) { - this.mScrollX = scrollX; - scrollTo(scrollX, 0); + int maxScroll = getMaxScrollX(); + int screenWidth = (int)(getWidth() / getScaleX()); + int realDataMaxScroll = maxScroll - screenWidth; + int maxAllowedScroll = realDataMaxScroll - (int)(MIN_VISIBLE_CANDLES * getmPointWidth()); + int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); + this.mScrollX = Math.max(0, Math.min(scrollX, normalizedMaxAllowedScroll)); + scrollTo(this.mScrollX, 0); } /** diff --git a/android/src/main/java/com/github/fujianlian/klinechart/container/HTKLineContainerView.java b/android/src/main/java/com/github/fujianlian/klinechart/container/HTKLineContainerView.java index c746653..a522597 100644 --- a/android/src/main/java/com/github/fujianlian/klinechart/container/HTKLineContainerView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/container/HTKLineContainerView.java @@ -71,7 +71,6 @@ 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(); @@ -79,8 +78,9 @@ public void reloadConfigManager() { // 调整滚动位置以补偿新增的数据 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.getWidth(); + klineView.setScrollX(scrollToEnd); } diff --git a/ios/Classes/HTKLineView.swift b/ios/Classes/HTKLineView.swift index 7cac2a3..b24f916 100644 --- a/ios/Classes/HTKLineView.swift +++ b/ios/Classes/HTKLineView.swift @@ -13,6 +13,8 @@ class HTKLineView: UIScrollView { var configManager: HTKLineConfigManager + private let minVisibleCandles: CGFloat = 5 + lazy var drawContext: HTDrawContext = { let drawContext = HTDrawContext.init(self, configManager) return drawContext @@ -98,7 +100,6 @@ class HTKLineView: UIScrollView { childDraw = wrDraw } - let isEnd = contentOffset.x + 1 + bounds.size.width >= contentSize.width let previousContentOffset = contentOffset.x reloadContentSize() @@ -106,8 +107,8 @@ class HTKLineView: UIScrollView { // 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 + } else if configManager.shouldScrollToEnd { + let toEndContentOffset = contentSize.width - 2 * bounds.size.width let distance = abs(contentOffset.x - toEndContentOffset) let animated = distance <= configManager.itemWidth reloadContentOffset(toEndContentOffset, animated) @@ -150,16 +151,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 = allCandlesWidth - 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) } @@ -721,7 +725,16 @@ class HTKLineView: UIScrollView { extension HTKLineView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { + let allCandlesWidth = configManager.itemWidth * CGFloat(configManager.modelArray.count) + let maxAllowedOffset = allCandlesWidth - minVisibleCandles * configManager.itemWidth + let contentOffsetX = scrollView.contentOffset.x + + if contentOffsetX > maxAllowedOffset { + scrollView.contentOffset.x = maxAllowedOffset + return + } + var visibleStartIndex = Int(floor(contentOffsetX / configManager.itemWidth)) var visibleEndIndex = Int( ceil((contentOffsetX + scrollView.bounds.size.width) / configManager.itemWidth)) From d65ada0109a4b2411e06203faa69fce0c920ee80 Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 16:05:22 +0300 Subject: [PATCH 3/8] added min visible candles to config manager --- .../github/fujianlian/klinechart/BaseKLineChartView.java | 7 ++++++- .../fujianlian/klinechart/HTKLineConfigManager.java | 7 +++++++ .../github/fujianlian/klinechart/ScrollAndScaleView.java | 9 +++++++-- ios/Classes/HTKLineConfigManager.swift | 3 +++ ios/Classes/HTKLineView.swift | 6 ++---- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java index e007561..baea4b2 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java @@ -948,7 +948,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { int maxScroll = getMaxScrollX(); int screenWidth = (int)(getWidth() / getScaleX()); int realDataMaxScroll = maxScroll - screenWidth; - int maxAllowedScroll = realDataMaxScroll - (int)(MIN_VISIBLE_CANDLES * getmPointWidth()); + int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getmPointWidth()); int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); if (l > normalizedMaxAllowedScroll) { @@ -1126,6 +1126,11 @@ public int getMaxScrollX() { return contentWidth; } + @Override + protected float getMinVisibleCandles() { + return configManager.minVisibleCandles; + } + /** * 在主区域画线 * diff --git a/android/src/main/java/com/github/fujianlian/klinechart/HTKLineConfigManager.java b/android/src/main/java/com/github/fujianlian/klinechart/HTKLineConfigManager.java index 4148528..e4f578e 100644 --- a/android/src/main/java/com/github/fujianlian/klinechart/HTKLineConfigManager.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/HTKLineConfigManager.java @@ -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; @@ -453,6 +455,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(); diff --git a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java index 42cbb5c..5be8a1a 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java @@ -16,8 +16,13 @@ public abstract class ScrollAndScaleView extends RelativeLayout implements GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener { - protected static final int MIN_VISIBLE_CANDLES = 5; 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; @@ -275,7 +280,7 @@ public void setScrollX(int scrollX) { int maxScroll = getMaxScrollX(); int screenWidth = (int)(getWidth() / getScaleX()); int realDataMaxScroll = maxScroll - screenWidth; - int maxAllowedScroll = realDataMaxScroll - (int)(MIN_VISIBLE_CANDLES * getmPointWidth()); + int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getmPointWidth()); int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); this.mScrollX = Math.max(0, Math.min(scrollX, normalizedMaxAllowedScroll)); scrollTo(this.mScrollX, 0); diff --git a/ios/Classes/HTKLineConfigManager.swift b/ios/Classes/HTKLineConfigManager.swift index 32612eb..26360b9 100644 --- a/ios/Classes/HTKLineConfigManager.swift +++ b/ios/Classes/HTKLineConfigManager.swift @@ -134,6 +134,8 @@ class HTKLineConfigManager: NSObject { var candleCornerRadius: CGFloat = 0 + var minVisibleCandles: CGFloat = 5 + var minuteVolumeCandleWidth: CGFloat = 0 var _minuteVolumeCandleWidth: CGFloat = 0 @@ -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 diff --git a/ios/Classes/HTKLineView.swift b/ios/Classes/HTKLineView.swift index b24f916..ea32988 100644 --- a/ios/Classes/HTKLineView.swift +++ b/ios/Classes/HTKLineView.swift @@ -13,8 +13,6 @@ class HTKLineView: UIScrollView { var configManager: HTKLineConfigManager - private let minVisibleCandles: CGFloat = 5 - lazy var drawContext: HTDrawContext = { let drawContext = HTDrawContext.init(self, configManager) return drawContext @@ -157,7 +155,7 @@ class HTKLineView: UIScrollView { func reloadContentOffset(_ contentOffsetX: CGFloat, _ animated: Bool = false) { let allCandlesWidth = configManager.itemWidth * CGFloat(configManager.modelArray.count) - let maxAllowedOffset = allCandlesWidth - minVisibleCandles * configManager.itemWidth + let maxAllowedOffset = allCandlesWidth - configManager.minVisibleCandles * configManager.itemWidth let offsetX = max(0, min(contentOffsetX, maxAllowedOffset)) setContentOffset(CGPoint.init(x: offsetX, y: 0), animated: animated) } @@ -726,7 +724,7 @@ extension HTKLineView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { let allCandlesWidth = configManager.itemWidth * CGFloat(configManager.modelArray.count) - let maxAllowedOffset = allCandlesWidth - minVisibleCandles * configManager.itemWidth + let maxAllowedOffset = allCandlesWidth - configManager.minVisibleCandles * configManager.itemWidth let contentOffsetX = scrollView.contentOffset.x From 832caeaa5f4a9c1878fefb63afb84fac838fe659 Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 16:43:10 +0300 Subject: [PATCH 4/8] add settings to example --- example/App.js | 7 +++++-- example/utils/businessLogic.js | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/example/App.js b/example/App.js index 572f63b..529babd 100644 --- a/example/App.js +++ b/example/App.js @@ -39,6 +39,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) @@ -164,7 +165,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]) @@ -213,7 +215,8 @@ const App = () => { lastDataLength, currentScrollPosition, showVolumeChart, - candleCornerRadius + candleCornerRadius, + minVisibleCandles: MIN_VISIBLE_CANDLES }, false) // Calculate scroll distance adjustment needed (based on item width) diff --git a/example/utils/businessLogic.js b/example/utils/businessLogic.js index 1f66904..acbb500 100644 --- a/example/utils/businessLogic.js +++ b/example/utils/businessLogic.js @@ -282,7 +282,8 @@ export const packOptionList = (modelArray, appState, shouldScrollToEnd = true, u selectedDrawTool, showVolumeChart, candleCornerRadius, - drawShouldContinue + drawShouldContinue, + minVisibleCandles } = appState const theme = ThemeManager.getCurrentTheme(isDarkTheme) @@ -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, From 7460078e3f7bf00efe783f260f0b04fa285db96e Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 16:56:13 +0300 Subject: [PATCH 5/8] fix typo --- .../github/fujianlian/klinechart/BaseKLineChartView.java | 2 +- .../github/fujianlian/klinechart/ScrollAndScaleView.java | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java index baea4b2..0550023 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java @@ -948,7 +948,7 @@ protected void onScrollChanged(int l, int t, int oldl, int oldt) { int maxScroll = getMaxScrollX(); int screenWidth = (int)(getWidth() / getScaleX()); int realDataMaxScroll = maxScroll - screenWidth; - int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getmPointWidth()); + int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getPointWidth()); int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); if (l > normalizedMaxAllowedScroll) { diff --git a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java index 5be8a1a..e0b4314 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java @@ -271,6 +271,13 @@ public boolean isTouch() { */ public abstract int getMaxScrollX(); + /** + * Get the point width + * + * @return + */ + public abstract float getPointWidth(); + /** * Set ScrollX * @@ -280,7 +287,7 @@ public void setScrollX(int scrollX) { int maxScroll = getMaxScrollX(); int screenWidth = (int)(getWidth() / getScaleX()); int realDataMaxScroll = maxScroll - screenWidth; - int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getmPointWidth()); + int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getPointWidth()); int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); this.mScrollX = Math.max(0, Math.min(scrollX, normalizedMaxAllowedScroll)); scrollTo(this.mScrollX, 0); From 271db9da9af2efb0c5a30b9fba4cf5dc6b00e0b3 Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 17:42:09 +0300 Subject: [PATCH 6/8] fix scroll on android --- .../klinechart/BaseKLineChartView.java | 10 +++++---- .../klinechart/ScrollAndScaleView.java | 22 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java index 0550023..8d52d62 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java @@ -945,10 +945,7 @@ public void onLongPress(MotionEvent e) { protected void onScrollChanged(int l, int t, int oldl, int oldt) { android.util.Log.d("BaseKLineChartView", "onScrollChanged() - l=" + l + ", t=" + t + ", oldl=" + oldl + ", oldt=" + oldt); - int maxScroll = getMaxScrollX(); - int screenWidth = (int)(getWidth() / getScaleX()); - int realDataMaxScroll = maxScroll - screenWidth; - int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getPointWidth()); + int maxAllowedScroll = (int)(mDataLen - getMinVisibleCandles() * getPointWidth()); int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); if (l > normalizedMaxAllowedScroll) { @@ -1131,6 +1128,11 @@ protected float getMinVisibleCandles() { return configManager.minVisibleCandles; } + @Override + public float getDataLength() { + return mDataLen; + } + /** * 在主区域画线 * diff --git a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java index e0b4314..620ca49 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/ScrollAndScaleView.java @@ -278,16 +278,20 @@ public boolean isTouch() { */ public abstract float getPointWidth(); + /** + * Get the total data length (itemCount * pointWidth) + * + * @return + */ + public abstract float getDataLength(); + /** * Set ScrollX * * @param scrollX */ public void setScrollX(int scrollX) { - int maxScroll = getMaxScrollX(); - int screenWidth = (int)(getWidth() / getScaleX()); - int realDataMaxScroll = maxScroll - screenWidth; - int maxAllowedScroll = realDataMaxScroll - (int)(getMinVisibleCandles() * getPointWidth()); + int maxAllowedScroll = (int)(getDataLength() - getMinVisibleCandles() * getPointWidth()); int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); this.mScrollX = Math.max(0, Math.min(scrollX, normalizedMaxAllowedScroll)); scrollTo(this.mScrollX, 0); @@ -303,13 +307,17 @@ public boolean isMultipleTouch() { } protected void checkAndFixScrollX() { - int contentSizeWidth = (getMaxScrollX()); + float dataLength = getDataLength(); + float minVisibleCandles = getMinVisibleCandles(); + float pointWidth = getPointWidth(); + int maxAllowedScroll = (int)(dataLength - minVisibleCandles * pointWidth); + int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); if (mScrollX < getMinScrollX()) { mScrollX = getMinScrollX(); mScroller.forceFinished(true); - } else if (mScrollX > contentSizeWidth) { - mScrollX = contentSizeWidth; + } else if (mScrollX > normalizedMaxAllowedScroll) { + mScrollX = normalizedMaxAllowedScroll; mScroller.forceFinished(true); if (!mHasTriggeredRightSide) { mHasTriggeredRightSide = true; From 3b11cf0e571b3c16da7359c201903ba0637e0196 Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 18:08:05 +0300 Subject: [PATCH 7/8] fix scroll to end; --- .../klinechart/BaseKLineChartView.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java index 8d52d62..9631446 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java @@ -944,14 +944,7 @@ public void onLongPress(MotionEvent e) { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { android.util.Log.d("BaseKLineChartView", "onScrollChanged() - l=" + l + ", t=" + t + ", oldl=" + oldl + ", oldt=" + oldt); - - int maxAllowedScroll = (int)(mDataLen - getMinVisibleCandles() * getPointWidth()); - int normalizedMaxAllowedScroll = Math.max(0, maxAllowedScroll); - - if (l > normalizedMaxAllowedScroll) { - setScrollX(normalizedMaxAllowedScroll); - return; - } + super.onScrollChanged(l, t, oldl, oldt); } @@ -1689,19 +1682,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 = (int)(mWidth / mScaleX); + 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 From f4f2fa9a3601fcb30ee0dec6904ca35e23f08426 Mon Sep 17 00:00:00 2001 From: Artem Slizhik Date: Thu, 15 Jan 2026 18:10:06 +0300 Subject: [PATCH 8/8] fix empty lines --- .../com/github/fujianlian/klinechart/BaseKLineChartView.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java index 9631446..547357d 100755 --- a/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java +++ b/android/src/main/java/com/github/fujianlian/klinechart/BaseKLineChartView.java @@ -944,8 +944,6 @@ public void onLongPress(MotionEvent e) { @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { android.util.Log.d("BaseKLineChartView", "onScrollChanged() - l=" + l + ", t=" + t + ", oldl=" + oldl + ", oldt=" + oldt); - - super.onScrollChanged(l, t, oldl, oldt); }