私はスクロールビューで非常に長い活動をしています。これは、ユーザーが入力する必要のあるさまざまなフィールドを含むフォームです。フォームの途中にチェックボックスがあり、ユーザーがチェックボックスをオンにすると、ビューの特定の部分にスクロールします。プログラムでEditTextオブジェクト(または他のビューオブジェクト)にスクロールする方法はありますか?
また、これはX座標とY座標を使用して可能であることは知っていますが、フォームがユーザーごとに変わる可能性があるため、これは避けたいと思います。
私はスクロールビューで非常に長い活動をしています。これは、ユーザーが入力する必要のあるさまざまなフィールドを含むフォームです。フォームの途中にチェックボックスがあり、ユーザーがチェックボックスをオンにすると、ビューの特定の部分にスクロールします。プログラムでEditTextオブジェクト(または他のビューオブジェクト)にスクロールする方法はありますか?
また、これはX座標とY座標を使用して可能であることは知っていますが、フォームがユーザーごとに変わる可能性があるため、これは避けたいと思います。
private final void focusOnView(){
your_scrollview.post(new Runnable() {
@Override
public void run() {
your_scrollview.scrollTo(0, your_EditBox.getBottom());
}
});
}
ビューをスクロールビューの中央にスクロールしたい場合は、SherifelKhatibの答えを大幅に改善できます。この再利用可能なメソッドは、HorizontalScrollViewの表示されている中心にビューをスムーズにスクロールします。
private final void focusOnView(final HorizontalScrollView scroll, final View view) {
new Handler().post(new Runnable() {
@Override
public void run() {
int vLeft = view.getLeft();
int vRight = view.getRight();
int sWidth = scroll.getWidth();
scroll.smoothScrollTo(((vLeft + vRight - sWidth) / 2), 0);
}
});
}
垂直ScrollView
使用の場合
...
int vTop = view.getTop();
int vBottom = view.getBottom();
int sHeight = scroll.getBottom();
scroll.smoothScrollTo(((vTop + vBottom - sHeight) / 2), 0);
...
これは私にとってうまくいきます:
targetView.getParent().requestChildFocus(targetView,targetView);
public void RequestChildFocus(子を表示、フォーカスを表示)
child-フォーカスが必要なこのViewParentの子。このビューには、フォーカスされたビューが含まれます。実際に焦点が当てられているのは必ずしもビューではありません。
フォーカス-実際にフォーカスを持っている子の子孫であるビュー
私の意見では、特定の長方形にスクロールする最良の方法は、を使用することView.requestRectangleOnScreen(Rect, Boolean)
です。View
スクロールしたい場所でそれを呼び出し、画面に表示したいローカルの長方形を渡す必要があります。2番目のパラメーターはfalse
、スムーズなスクロールとtrue
即時スクロール用である必要があります。
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, false);
WarrenFaithのAnswerに基づいて小さなユーティリティメソッドを作成しました。このコードは、そのビューがすでにスクロールビューに表示されているかどうかも考慮に入れており、スクロールする必要はありません。
public static void scrollToView(final ScrollView scrollView, final View view) {
// View needs a focus
view.requestFocus();
// Determine if scroll needs to happen
final Rect scrollBounds = new Rect();
scrollView.getHitRect(scrollBounds);
if (!view.getLocalVisibleRect(scrollBounds)) {
new Handler().post(new Runnable() {
@Override
public void run() {
scrollView.smoothScrollTo(0, view.getBottom());
}
});
}
}
TextView
リクエストに焦点を当てる必要があります。
mTextView.requestFocus();
EditTextは、ScrollView内のいくつかのレイヤーにネストされていましたが、それ自体はレイアウトのルートビューではありません。getTop()とgetBottom()は、ビューを含むビュー内の座標を報告しているように見えたため、EditTextの親を反復処理することにより、ScrollViewの上部からEditTextの上部までの距離を計算しました。
// Scroll the view so that the touched editText is near the top of the scroll view
new Thread(new Runnable()
{
@Override
public
void run ()
{
// Make it feel like a two step process
Utils.sleep(333);
// Determine where to set the scroll-to to by measuring the distance from the top of the scroll view
// to the control to focus on by summing the "top" position of each view in the hierarchy.
int yDistanceToControlsView = 0;
View parentView = (View) m_editTextControl.getParent();
while (true)
{
if (parentView.equals(scrollView))
{
break;
}
yDistanceToControlsView += parentView.getTop();
parentView = (View) parentView.getParent();
}
// Compute the final position value for the top and bottom of the control in the scroll view.
final int topInScrollView = yDistanceToControlsView + m_editTextControl.getTop();
final int bottomInScrollView = yDistanceToControlsView + m_editTextControl.getBottom();
// Post the scroll action to happen on the scrollView with the UI thread.
scrollView.post(new Runnable()
{
@Override
public void run()
{
int height =m_editTextControl.getHeight();
scrollView.smoothScrollTo(0, ((topInScrollView + bottomInScrollView) / 2) - height);
m_editTextControl.requestFocus();
}
});
}
}).start();
別のバリエーションは次のようになります。
scrollView.postDelayed(new Runnable()
{
@Override
public void run()
{
scrollView.smoothScrollTo(0, img_transparent.getTop());
}
}, 200);
または、メソッドを使用できますpost()
。
ScrollViewがChildViewの直接の親である場合、上記の回答は正常に機能します。ChildViewがScrollViewの別のViewGroupにラップされている場合、View.getTop()がその親を基準にした位置を取得するため、予期しない動作が発生します。このような場合、これを実装する必要があります。
public static void scrollToInvalidInputView(ScrollView scrollView, View view) {
int vTop = view.getTop();
while (!(view.getParent() instanceof ScrollView)) {
view = (View) view.getParent();
vTop += view.getTop();
}
final int scrollPosition = vTop;
new Handler().post(() -> scrollView.smoothScrollTo(0, scrollPosition));
}
私はこれがより良い答えには遅すぎるかもしれないことを知っていますが、望ましい完璧な解決策はポジショナーのようなシステムでなければなりません。つまり、システムがエディターフィールドの位置を決めるとき、フィールドはキーボードのすぐ上に配置されるため、UI/UXのルールに従って完璧です。
以下のコードは、Androidの方法でスムーズにポジショニングすることを目的としています。まず、現在のスクロールポイントを参照ポイントとして保持します。2つ目は、エディターに最適なポジショニングスクロールポイントを見つけることです。これを行うには、上にスクロールしてから、エディターフィールドに、ScrollViewコンポーネントが最適なポジショニングを行うように要求します。ガチャ!私たちは最高のポジションを学びました。ここで、前のポイントから新しく見つけたポイントまでスムーズにスクロールします。必要に応じて、 smoothScrollToのみの代わりにscrollToを使用してスムーズスクロールを省略できます。
注:メインコンテナのScrollViewは、scrollViewSignupという名前のメンバーフィールドです。これは、多くのことを理解できるように、私の例はサインアップ画面であったためです。
view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(final View view, boolean b) {
if (b) {
scrollViewSignup.post(new Runnable() {
@Override
public void run() {
int scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, 0);
final Rect rect = new Rect(0, 0, view.getWidth(), view.getHeight());
view.requestRectangleOnScreen(rect, true);
int new_scrollY = scrollViewSignup.getScrollY();
scrollViewSignup.scrollTo(0, scrollY);
scrollViewSignup.smoothScrollTo(0, new_scrollY);
}
});
}
}
});
このブロックをすべてのEditTextインスタンスに使用し、画面コードとすばやく統合する場合。以下のようにトラバーサーを作成するだけです。これを行うために、メインのOnFocusChangeListenerをfocusChangeListenerToScrollEditorという名前のメンバーフィールドにし、以下のようにonCreate中に呼び出します。
traverseEditTextChildren(scrollViewSignup, focusChangeListenerToScrollEditor);
また、メソッドの実装は以下のとおりです。
private void traverseEditTextChildren(ViewGroup viewGroup, View.OnFocusChangeListener focusChangeListenerToScrollEditor) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
if (view instanceof EditText)
{
((EditText) view).setOnFocusChangeListener(focusChangeListenerToScrollEditor);
}
else if (view instanceof ViewGroup)
{
traverseEditTextChildren((ViewGroup) view, focusChangeListenerToScrollEditor);
}
}
}
したがって、ここで行ったことは、すべてのEditTextインスタンスの子にフォーカスのあるリスナーを呼び出すようにすることです。
このソリューションに到達するために、ここですべてのソリューションを確認し、UI/UXの結果を改善するための新しいソリューションを生成しました。
私に多くのインスピレーションを与えてくれた他のすべての答えに感謝します。
yourScrollView.smoothScrollTo(0, yourEditText.getTop());
早くやれよ ;)
私は、よりエレガントでエラーが発生しにくいソリューションを見つけたと思います
計算は必要ありません。他の提案されたソリューションとは異なり、上下両方のスクロールを正しく処理します。
/**
* Will scroll the {@code scrollView} to make {@code viewToScroll} visible
*
* @param scrollView parent of {@code scrollableContent}
* @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
* @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
*/
void scrollToView(ScrollView scrollView, ViewGroup scrollableContent, View viewToScroll) {
Rect viewToScrollRect = new Rect(); //coordinates to scroll to
viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout)
scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
}
現在変更されているpostDelayed
場合に備えて、信頼性を高めるためにラップすることをお勧めしますScrollView
/**
* Will scroll the {@code scrollView} to make {@code viewToScroll} visible
*
* @param scrollView parent of {@code scrollableContent}
* @param scrollableContent a child of {@code scrollView} whitch holds the scrollable content (fills the viewport).
* @param viewToScroll a child of {@code scrollableContent} to whitch will scroll the the {@code scrollView}
*/
private void scrollToView(final ScrollView scrollView, final ViewGroup scrollableContent, final View viewToScroll) {
long delay = 100; //delay to let finish with possible modifications to ScrollView
scrollView.postDelayed(new Runnable() {
public void run() {
Rect viewToScrollRect = new Rect(); //coordinates to scroll to
viewToScroll.getHitRect(viewToScrollRect); //fills viewToScrollRect with coordinates of viewToScroll relative to its parent (LinearLayout)
scrollView.requestChildRectangleOnScreen(scrollableContent, viewToScrollRect, false); //ScrollView will make sure, the given viewToScrollRect is visible
}
}, delay);
}
参照:https ://stackoverflow.com/a/6438240/2624806
フォローははるかにうまくいきました。
mObservableScrollView.post(new Runnable() {
public void run() {
mObservableScrollView.fullScroll([View_FOCUS][1]);
}
});
Androidのソースコードを調べると、要求されたとおりに実行するScrollView
– –のメンバー関数がすでに存在することがわかります。scrollToChild(View)
残念ながら、この関数は、マークされたいくつかのあいまいな理由のためprivate
です。その関数に基づいて、パラメーターとして指定されたもののScrollView
上にある最初の関数を見つけて、 :View
内に表示されるようにスクロールする次の関数を作成しました。ScrollView
private void make_visible(View view)
{
int vt = view.getTop();
int vb = view.getBottom();
View v = view;
for(;;)
{
ViewParent vp = v.getParent();
if(vp == null || !(vp instanceof ViewGroup))
break;
ViewGroup parent = (ViewGroup)vp;
if(parent instanceof ScrollView)
{
ScrollView sv = (ScrollView)parent;
// Code based on ScrollView.computeScrollDeltaToGetChildRectOnScreen(Rect rect) (Android v5.1.1):
int height = sv.getHeight();
int screenTop = sv.getScrollY();
int screenBottom = screenTop + height;
int fadingEdge = sv.getVerticalFadingEdgeLength();
// leave room for top fading edge as long as rect isn't at very top
if(vt > 0)
screenTop += fadingEdge;
// leave room for bottom fading edge as long as rect isn't at very bottom
if(vb < sv.getChildAt(0).getHeight())
screenBottom -= fadingEdge;
int scrollYDelta = 0;
if(vb > screenBottom && vt > screenTop)
{
// need to move down to get it in view: move down just enough so
// that the entire rectangle is in view (or at least the first
// screen size chunk).
if(vb-vt > height) // just enough to get screen size chunk on
scrollYDelta += (vt - screenTop);
else // get entire rect at bottom of screen
scrollYDelta += (vb - screenBottom);
// make sure we aren't scrolling beyond the end of our content
int bottom = sv.getChildAt(0).getBottom();
int distanceToBottom = bottom - screenBottom;
scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
}
else if(vt < screenTop && vb < screenBottom)
{
// need to move up to get it in view: move up just enough so that
// entire rectangle is in view (or at least the first screen
// size chunk of it).
if(vb-vt > height) // screen size chunk
scrollYDelta -= (screenBottom - vb);
else // entire rect at top
scrollYDelta -= (screenTop - vt);
// make sure we aren't scrolling any further than the top our content
scrollYDelta = Math.max(scrollYDelta, -sv.getScrollY());
}
sv.smoothScrollBy(0, scrollYDelta);
break;
}
// Transform coordinates to parent:
int dy = parent.getTop()-parent.getScrollY();
vt += dy;
vb += dy;
v = parent;
}
}
私の解決策は次のとおりです。
int[] spinnerLocation = {0,0};
spinner.getLocationOnScreen(spinnerLocation);
int[] scrollLocation = {0, 0};
scrollView.getLocationInWindow(scrollLocation);
int y = scrollView.getScrollY();
scrollView.smoothScrollTo(0, y + spinnerLocation[1] - scrollLocation[1]);
垂直スクロール、フォームに適しています。答えはAhmadalibaloch水平スクロールに基づいています。
private final void focusOnView(final HorizontalScrollView scroll, final View view) {
new Handler().post(new Runnable() {
@Override
public void run() {
int top = view.getTop();
int bottom = view.getBottom();
int sHeight = scroll.getHeight();
scroll.smoothScrollTo(0, ((top + bottom - sHeight) / 2));
}
});
}
ObjectAnimator
次のように使用できます。
ObjectAnimator.ofInt(yourScrollView, "scrollY", yourView.getTop()).setDuration(1500).start();
私の場合、それはそうではありませんEditText
、それはgoogleMap
です。そして、それはこのようにうまく機能します。
private final void focusCenterOnView(final ScrollView scroll, final View view) {
new Handler().post(new Runnable() {
@Override
public void run() {
int centreX=(int) (view.getX() + view.getWidth() / 2);
int centreY= (int) (view.getY() + view.getHeight() / 2);
scrollView.smoothScrollBy(centreX, centreY);
}
});
}
Que:スクロールビューを特定のエディットテキストにプログラムでスクロールする方法はありますか?
回答:recyclerviewのネストされたスクロールビューの最後の位置にレコードデータが追加されました。
adapter.notifyDataSetChanged();
nested_scroll.setScrollY(more Detail Recycler.getBottom());
以下は私が使用しているものです:
int amountToScroll = viewToShow.getBottom() - scrollView.getHeight() + ((LinearLayout.LayoutParams) viewToShow.getLayoutParams()).bottomMargin;
// Check to see if scrolling is necessary to show the view
if (amountToScroll > 0){
scrollView.smoothScrollTo(0, amountToScroll);
}
これにより、ビューの下部の余白を含め、ビューの下部を表示するために必要なスクロール量が取得されます。
Sherifの回答に基づくと、次のことが私のユースケースに最適でした。注目すべき変更は、getTop()
の代わりにありgetBottom()
ます。smoothScrollTo()
scrollTo()
private void scrollToView(final View view){
final ScrollView scrollView = findViewById(R.id.bookmarksScrollView);
if(scrollView == null) return;
scrollView.post(new Runnable() {
@Override
public void run() {
scrollView.smoothScrollTo(0, view.getTop());
}
});
}
ソフトキーボードを開いたときにビューにスクロールしたい場合は、少し注意が必要な場合があります。私がこれまでに得た最善の解決策は、インセットコールバックとrequestRectangleOnScreen
メソッドの組み合わせを使用することです。
まず、インセットコールバックを設定する必要があります。
fun View.doOnApplyWindowInsetsInRoot(block: (View, WindowInsetsCompat, Rect) -> Unit) {
val initialPadding = recordInitialPaddingForView(this)
val root = getRootForView(this)
ViewCompat.setOnApplyWindowInsetsListener(root) { v, insets ->
block(v, insets, initialPadding)
insets
}
requestApplyInsetsWhenAttached()
}
fun View.requestApplyInsetsWhenAttached() {
if (isAttachedToWindow) {
requestApplyInsets()
} else {
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
v.removeOnAttachStateChangeListener(this)
v.requestApplyInsets()
}
override fun onViewDetachedFromWindow(v: View) = Unit
})
}
}
ルートビューにコールバックを設定して、確実に呼び出されるようにします。インセットは、問題のビューが受け取る前に消費される可能性があるため、ここで追加の作業を行う必要があります。
今ではほとんど簡単です:
doOnApplyWindowInsetsInRoot { _, _, _ ->
post {
if (viewInQuestion.hasFocus()) {
requestRectangleOnScreen(Rect(0, 0, width, height))
}
}
}
フォーカスチェックを取り除くことができます。への呼び出しの数を制限するためにありますrequestRectangleOnScreen
。post
スクロール可能な親がフォーカスされたビューにスクロールした後にアクションを実行するために使用します。
誰かがKotlinバージョンを探しているなら、拡張機能でこれを行うことができます
fun ScrollView.scrollToChild(view: View, onScrolled: (() -> Unit)? = null) {
view.requestFocus()
val scrollBounds = Rect()
getHitRect(scrollBounds)
if (!view.getLocalVisibleRect(scrollBounds)) {
findViewTreeLifecycleOwner()?.lifecycleScope?.launch(Dispatchers.Main) {
smoothScrollTo(0, view.bottom - 40)
onScrolled?.invoke()
}
}
}
スクロール後に何かを実行できる小さなコールバックがあります。
scrlMainがNestedScrollViewの場合は、次を使用します。
scrlMain.post(new Runnable() {
@Override
public void run() {
scrlMain.fullScroll(View.FOCUS_UP);
}
});