我有一个ScrollView
,它的布局中嵌套了多个TextView
子级。我希望这些子程序中的文本是可选择的(android:textIsSelectable="true"
),这样用户就可以使用复制、共享和选择所有操作。但是,当其中一个孩子收到焦点(来自触摸或长按压)时,它会导致父ScrollView
滚动到聚焦子。我想这是一个特性,但它给我带来了三个问题。
当使用长按压选择文本时,它会导致文本滚动,因此,聚焦子文件上方的子程序将滚动到可见边界之外。unintuitive.
使子程序不可聚焦会阻止滚动,但不能选择文本。那么,如何将TextView
视图嵌套在一个具有可选文本的ScrollView
中,但当其中一个视图收到焦点时却防止滚动?
发布于 2020-11-28 22:57:59
在寻找这个问题的可能解决方案时,我发现了本文https://programmersought.com/article/8487993014/。它与这个问题没有直接关系,但是本文展示了ScrollView
的一些源代码的一个特定部分引起了我的注意:
public void requestChildFocus(View child, View focused) {
if (focused != null && focused.getRevealOnFocusHint()) {
If (!mIsLayoutDirty) {//This boolean value marks whether the layout has been completed. If it is completed, it is false.
scrollToChild(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
}
}
//super method [handling FOCUS_BLOCK_DESCENDANTS case] is called after scrolling on top.
//So setting the android:descendantFocusability="blocksDescendants" attribute to the ScrollView is invalid.
super.requestChildFocus(child, focused);
}
在这里,您可以看到ScrollView
将尝试滚动到聚焦子focused != null
。因此,若要禁用此行为,请创建ScrollView
的子类并覆盖此方法,如下所示:
package com.test
// imports here...
public class MyScrollView extends ScrollView {
// constructors here...
@Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, null);
}
}
通过忽略focused
参数并将null
传递给超级实现,滚动将永远不会发生,但父文件仍将请求子节点接收焦点,因此仍然可以选择文本。
唯一要做的事情是用上面定义的自定义实现替换布局文件中的ScrollView
父文件。
编辑解决方案在API 30上测试并运行良好,但当我在API 25上测试它时,会抛出NPE并导致应用程序崩溃。Android建议使用NestedScrollView
视图进行垂直滚动,但应用程序仍然崩溃。我查看了requestChildFocus(View, View)
的源代码,它略有不同:
@Override
public void requestChildFocus(View child, View focused) {
if (!mIsLayoutDirty) {
scrollToChild(focused);
} else {
// The child may not be laid out yet, we can't compute the scroll yet
mChildToScrollTo = focused;
}
super.requestChildFocus(child, focused);
}
private void scrollToChild(View child) {
child.getDrawingRect(mTempRect);
/* Offset from child's local coordinates to ScrollView coordinates */
offsetDescendantRectToMyCoords(child, mTempRect);
int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
if (scrollDelta != 0) {
scrollBy(0, scrollDelta);
}
}
方法scrollToChild(View)
是私有的,因此我们不能覆盖默认实现(因为为什么会有人想要这样做,对吗?),但是scrollBy(int, int)
是公共的。因此,与其在MyScrollView
类中重写MyScrollView
,不如重写scrollBy(int, int)
,让它什么也不做。这在API 30和25上都进行了测试,并且在不崩溃的情况下正常工作。后来,我试图恢复到扩展ScrollView
,但它仍然有效。所以你只需要重写这个方法,超级类型就不重要了。
https://stackoverflow.com/questions/65055293
复制相似问题