HorizontalScrollView scrollTo 不会滚动到视图的原始宽度之上
HorizontalScrollView scrollTo doesn't scroll above original width of the view
我有一个包含 HorizontalScrollView 的自定义视图。滚动视图的宽度为 match_parent,其子视图的宽度最初是根据自定义视图的属性值以编程方式设置的。在某些时候,滚动视图的子视图的宽度以编程方式更新(增加)。问题是,更新后,scrollTo 方法仍然无法滚动到原始宽度值以上(scrollBy 也一样)。
封闭视图(自定义视图)的左右内边距等于屏幕的一半,如果相关的话。
示例:
- HorizontalScrollView 的初始子宽度:1000;
- HorizontalScrollView 的父级宽度:1080;
- HorizontalScrollView 的 left/right padding = 540;
- 新的 HorizontalScrollView 的子级宽度:2000;
- scrollTo(1100, 0) = scrollTo(1000, 0); <---- 问题来了
相关代码:
自定义视图初始化:
private void init(AttributeSet attrs, int defStyle) {
inflate(getContext(), R.layout.timeline_view, this);
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TimelineView, defStyle, 0);
m_timelineInitialLength = a.getDimension(R.styleable.TimelineView_timelineInitialLength, DEFAULT_LENGTH);
a.recycle();
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
LinearLayout timelineWidget = findViewById(R.id.timeline_widget);
int horizontalPadding = displayMetrics.widthPixels / 2;
timelineWidget.setPadding(horizontalPadding, 0, horizontalPadding, 0);
m_horizontalScrollView = findViewById(R.id.scroll_view);
m_horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
m_currentScrollX = m_horizontalScrollView.getScrollX();
}
});
m_currentScrollX = m_horizontalScrollView.getScrollX();
update(m_timelineInitialLength, m_timelineInitialStepWidth, m_currentScrollX);
}
Update 和 scrollTo(此方法也在初始化结束时调用。m_rulerView 和 m_timelineLayout 是 HorizontalScrollView 的子级):
private void update(float timelineLength, float timelineStepWidth, int currentScrollX) {
m_rulerView = findViewById(R.id.ruler);
m_rulerView.setIndicator(null);
m_rulerView.getLayoutParams().width = (int) timelineLength;
m_rulerView.requestLayout();
m_timelineLayout = findViewById(R.id.timeline);
m_timelineLayout.getLayoutParams().width = (int) timelineLength;
m_timelineLayout.requestLayout();
m_horizontalScrollView.scrollTo(currentScrollX, 0);
}
问题似乎是滚动发生在重绘子元素之前,在调用 requestLayout 之前仅安排在将来某个时间点执行视图的更新(对于 invalidate() 也是如此)。
我使用变通方法解决了问题,方法是在一段时间后执行 scrollTo:
new Handler().postDelayed(() -> ((Activity) getContext()).runOnUiThread(() -> m_horizontalScrollView.scrollTo(currentScrollX, 0)), 200);
我仍然愿意接受更好的解决方案。
我有一个包含 HorizontalScrollView 的自定义视图。滚动视图的宽度为 match_parent,其子视图的宽度最初是根据自定义视图的属性值以编程方式设置的。在某些时候,滚动视图的子视图的宽度以编程方式更新(增加)。问题是,更新后,scrollTo 方法仍然无法滚动到原始宽度值以上(scrollBy 也一样)。
封闭视图(自定义视图)的左右内边距等于屏幕的一半,如果相关的话。
示例:
- HorizontalScrollView 的初始子宽度:1000;
- HorizontalScrollView 的父级宽度:1080;
- HorizontalScrollView 的 left/right padding = 540;
- 新的 HorizontalScrollView 的子级宽度:2000;
- scrollTo(1100, 0) = scrollTo(1000, 0); <---- 问题来了
相关代码:
自定义视图初始化:
private void init(AttributeSet attrs, int defStyle) {
inflate(getContext(), R.layout.timeline_view, this);
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TimelineView, defStyle, 0);
m_timelineInitialLength = a.getDimension(R.styleable.TimelineView_timelineInitialLength, DEFAULT_LENGTH);
a.recycle();
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
LinearLayout timelineWidget = findViewById(R.id.timeline_widget);
int horizontalPadding = displayMetrics.widthPixels / 2;
timelineWidget.setPadding(horizontalPadding, 0, horizontalPadding, 0);
m_horizontalScrollView = findViewById(R.id.scroll_view);
m_horizontalScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
m_currentScrollX = m_horizontalScrollView.getScrollX();
}
});
m_currentScrollX = m_horizontalScrollView.getScrollX();
update(m_timelineInitialLength, m_timelineInitialStepWidth, m_currentScrollX);
}
Update 和 scrollTo(此方法也在初始化结束时调用。m_rulerView 和 m_timelineLayout 是 HorizontalScrollView 的子级):
private void update(float timelineLength, float timelineStepWidth, int currentScrollX) {
m_rulerView = findViewById(R.id.ruler);
m_rulerView.setIndicator(null);
m_rulerView.getLayoutParams().width = (int) timelineLength;
m_rulerView.requestLayout();
m_timelineLayout = findViewById(R.id.timeline);
m_timelineLayout.getLayoutParams().width = (int) timelineLength;
m_timelineLayout.requestLayout();
m_horizontalScrollView.scrollTo(currentScrollX, 0);
}
问题似乎是滚动发生在重绘子元素之前,在调用 requestLayout 之前仅安排在将来某个时间点执行视图的更新(对于 invalidate() 也是如此)。
我使用变通方法解决了问题,方法是在一段时间后执行 scrollTo:
new Handler().postDelayed(() -> ((Activity) getContext()).runOnUiThread(() -> m_horizontalScrollView.scrollTo(currentScrollX, 0)), 200);
我仍然愿意接受更好的解决方案。