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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @furkando @NGabuaeva @EdiOanceaV2 @sinantalhakosar @rturtu @hikmet-demir @Matteoverzotti @robertbarbu27 @snowtema

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @furkando @NGabuaeva @EdiOanceaV2 @sinantalhakosar @rturtu @hikmet-demir @Matteoverzotti @robertbarbu27 @snowtema
* @furkando @NGabuaeva @EdiOanceaV2 @sinantalhakosar @rturtu @hikmet-demir @Matteoverzotti @robertbarbu27

14 changes: 14 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -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_
Original file line number Diff line number Diff line change
Expand Up @@ -1110,10 +1110,20 @@ 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;
}

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

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

/**
* 在主区域画线
*
Expand Down Expand Up @@ -1670,19 +1680,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
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 @@ -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();
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,14 +271,30 @@ 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
*
* @param scrollX
*/
public void setScrollX(int scrollX) {
this.mScrollX = scrollX;
scrollTo(scrollX, 0);
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);
}

/**
Expand All @@ -285,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,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.getWidth();
klineView.setScrollX(scrollToEnd);
}


Expand Down
7 changes: 5 additions & 2 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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)
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
21 changes: 16 additions & 5 deletions ios/Classes/HTKLineView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,15 @@ class HTKLineView: UIScrollView {
childDraw = wrDraw
}

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

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
} 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)
Expand Down Expand Up @@ -150,16 +149,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 - 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 @@ -721,7 +723,16 @@ class HTKLineView: UIScrollView {
extension HTKLineView: UIScrollViewDelegate {

func scrollViewDidScroll(_ scrollView: UIScrollView) {
let allCandlesWidth = configManager.itemWidth * CGFloat(configManager.modelArray.count)
let maxAllowedOffset = allCandlesWidth - configManager.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))
Expand Down