web-dev-qa-db-ja.com

ScrollViewがEditTextに自動スクロールするのを停止します

私が見つけた素晴らしい解決策がない一般的な問題のようです。目標は、ScrollViewがフォーカスを持っているEditText(またはその問題のビュー)への自動スクロールを停止することです。

Buttonにビューの束(TextViews、ScrollViewsなど)があり、そのうちの1つはEditTextです。 ScrollView内のボタンをクリックすると、ScrollViewEditText(画面外)までスクロールダウンします。画面からスクロールしたくない他の要素があるため、これは望ましくありません。

これで、ScrollViewに他のフォーカス可能な要素を含めることで、画面が最初に表示されるときにこれを防ぐことができます。ただし、一般的な問題は依然として存在します。ユーザーは手動でEditTextまでスクロールダウンし、いくつかの数字を入力してから、一番上までスクロールし(EditText画面外)、ScrollViewのボタンをクリックし、何だと思う? ScrollViewはそのEditTextまでスクロールダウンします。

私はScrollViewを拡張し、findFocusableViewInBoundsのようなメソッドのいくつかをオーバーライドすることを考えていますが、もっと苦労することになりそうです。

可能であれば助けてください。

EditTextの上部に0の高さScrollViewがあり、ScrollView内の他のアイテムにNext Focusable要素のプロパティを追加するなどのことをいじりました。仮想キーボードまたは手動キーボードが非表示になったときなどに、EditTextにフォーカスを移すことが1つの「ハック」だと思います。

41
Fraggle

その問題にかなり長い間苦労した後、私はあまりtooくないことなく動作するように見える解決策を見つけました。まず、ViewGroup(直接)を含むEditTextdescendantFocusabilityが[Before Descendants]に設定されていること、_focusableが[true]に設定されていること、およびfocusableInTouchModeを「true」に設定します。これはScrollViewそのものではなく、さまざまなビューがある内部のレイアウトです。次に、onTouchListenerScrollViewに追加します。これは、次のように、EditTextに触れるたびにフォーカスを削除します。

ScrollView scroll = (ScrollView)findViewById(R.id.scrollView1);
scroll.setOnTouchListener(new OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (myEditText.hasFocus()) {
            myEditText.clearFocus();
        }
        return false;
    }
});

それでも解決しない場合は教えてください。起こるべきことは、レイアウトがEditTextの代わりにフォーカスを取得するため、スクロールが発生しないことです。

35
thomas88wp

Linearlayoutの上部に空のビューを作成するだけです

<View Android:layout_width="fill_parent" Android:id="@+id/focus_view" Android:layout_height="0dp" Android:focusable="true" Android:focusableInTouchMode="true"><requestFocus/></View>

単一行で問題を解決します

21
KeT4yn

同じ問題がありました。この問題に対処するために使用しているトリックが1つあります。

public void onClick(View v) {
    button.requestFocusFromTouch(); //prevents from loosing focus and scrolling view down
    ....
}
9
pawelzieba

問題はJavaコードではなく、マニフェストコードにあります。

AndroidManifest.xmlで、アクティビティに属性を追加します。

        <activity Android:name=".MyActivity" Android:windowSoftInputMode="adjustPan"> </activity>
8
Corbella

以下に2つのパラメーターを追加します。

Android:focusable="true"
Android:focusableInTouchMode="true"

どのメインレイアウトがあります。

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/layMain"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@color/background"
    Android:focusable="true"
    Android:focusableInTouchMode="true">

これにより、EditTextはオートフォーカスされません。

7
Rohan Mistry

これが私がしたことです

 <LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent" style="@style/measurementTableRowStyle"
    Android:focusable="true" Android:layout_height="fill_parent">
    <requestFocus></requestFocus>
    <LinearLayout Android:id="@+id/linearLayout1"
        Android:orientation="horizontal" Android:layout_width="fill_parent"
        Android:layout_height="wrap_content">
        <TextView Android:id="@+id/desc_text" Android:text="Value : "
            style="@style/attributeNameTextStyle" Android:layout_width="wrap_content"
            Android:focusable="true" Android:layout_height="wrap_content">
            <requestFocus></requestFocus>
        </TextView>

        <TextView style="@style/attributeValueStyle" Android:id="@+id/value_text"
            Android:text="TextView" Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"></TextView>
    </LinearLayout>

その理由は、このような場合、明示的なAndroid:focusable="true" その後 <requestFocus></requestFocus>。これはIMOのたびに機能するはずです

4
SaKet

この最も恐ろしいバグに対する私の修正(これは、フリングメソッドをバカにならないように変更した場合にのみAPI11より前のものであることに注意する価値があります).

古いflingメソッドは、到達する次のフォーカスを見つけます..これはあまり役に立ちません。このクラスの他のバージョンは、ユーザーがキーボードからフォームを真に走査するとフォーカスの動作を停止するため、実際には機能しません。

public class NonFocusingScrollView extends ScrollView {

    private boolean mBlockRequestFocusOnFling = false;

    public NonFocusingScrollView(Context context) {
        super(context);
    }

    public NonFocusingScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NonFocusingScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public ArrayList<View> getFocusables(int direction) {
        if(mBlockRequestFocusOnFling)
            return new ArrayList<View>();
        return super.getFocusables(direction);
    }

    @Override
    public void requestChildFocus(View child, View focused) {
        if(!mBlockRequestFocusOnFling)
        super.requestChildFocus(child, focused);
    }


    @Override
    public void fling(int velocityY) {
        mBlockRequestFocusOnFling = true;
        super.fling(velocityY);
        mBlockRequestFocusOnFling = false;
    }
}
2
Chris.Jenkins

thomas88wp answer、 https://stackoverflow.com/a/6486348/528746 私のために働いた。しかし、2つの問題がありました。1。スクロールするとき、キーボードを非表示にしたかった
2。多くのEditTextビューがあり、それぞれのビューを作成したくありませんでした。
(アクティビティではなくフラグメント内でこれを書いているので、getActivity()を実行します)

    ScrollView scroll = (ScrollView)view.findViewById(R.id.layout_scroll);
    scroll.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // Check if the view with focus is EditText
            if (getActivity().getCurrentFocus() instanceof EditText)
            {
                EditText ed = (EditText)getActivity().getCurrentFocus();
                if (ed.hasFocus()) {

                    // Hide the keyboard
                    InputMethodManager inputManager = (InputMethodManager)
                            getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); 
                    inputManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),
                            InputMethodManager.HIDE_NOT_ALWAYS);
                    // Clear the focus
                    ed.clearFocus();
                }
            }
            return false;
        }

    });
2
andydev

カスタムScrollViewを作成し(クラスを作成し、Horizo​​ntalScrollViewを拡張します)、スクロール可能なゲッターセッターを作成します。その後、computeScrollDeltaToGetChildRectOnScreenをオーバーライドします。

仕組み:Android編集テキストまたは画面外のフォーカスがあるものがあるたびに、メソッドcomputeScrollDeltaToGetChildRectOnScreenを呼び出して表示します。無効にし、0を返す場合スクロールしません...

そのため、次のようなカスタムスクロールビューがあります。

    public class TrackableHorizontalScrollView extends HorizontalScrollView {


    // true if we can scroll (not locked)
    // false if we cannot scroll (locked)
    private boolean mScrollable = true;

    public TrackableHorizontalScrollView(Context context) {
        super(context);
    }

    public TrackableHorizontalScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TrackableHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setScrollingEnabled(boolean enabled) {
        mScrollable = enabled;
    }

    public boolean isScrollable() {
        return mScrollable;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // if we can scroll pass the event to the superclass
                if (mScrollable) return super.onTouchEvent(ev);
                // only continue to handle the touch event if scrolling enabled
                return mScrollable; // mScrollable is always false at this point
            default:
                return super.onTouchEvent(ev);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // Don't do anything with intercepted touch events if
        // we are not scrollable
        if (!mScrollable) return false;
        else return super.onInterceptTouchEvent(ev);
    }

    @Override
    public void scrollTo(int x, int y){
        if (!mScrollable) return;
        super.scrollTo(x, y);
    }


    //Custom smooth scroll method since norm is final and cannot be overridden
    public final void smooothScrollToIfEnabled(int x, int y){
         if (!mScrollable) return;
         smoothScrollTo(x, y);
    }

    @Override
    protected int computeScrollDeltaToGetChildRectOnScreen(Android.graphics.Rect rect){
        if (!mScrollable) return 0;
        return super.computeScrollDeltaToGetChildRectOnScreen(rect);
    }


}

次のようにXML内でこれを使用できます。

<com.your.package.ui.widget.TrackableHorizontalScrollView
            Android:id="@+id/wi_et_credit_scroller"
            Android:layout_toRightOf="@id/wi_et_credit_iv"
            Android:layout_width="fill_parent"
            Android:scrollbars="none"
            Android:layout_height="wrap_content"
            Android:paddingRight="5dp"
            Android:layout_gravity="center_vertical">

<!--Whatever you have inside the scrollview-->

</com.your.package.ui.widget.TrackableHorizontalScrollView>
1
aV1rus

Thomas88wpのコードの別のバージョン:

ScrollView scroll = (ScrollView)getActivity().findViewById(R.id.scrollView_addNewBill);
    scroll.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View arg0, MotionEvent arg1) {        
            View focussedView = getCurrentFocus(); 
            if( focussedView != null ) focussedView.clearFocus();
                
            return false;
    }
});
1
Adil Malik

カスタムScrollViewを記述し、onScrollChangedメソッドをオーバーライドして、フォーカスされたビューからフォーカスをクリアし、オプションでキーボードを非表示にすることができます。

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    View v = getFocusedChild();
    if (v != null) {
        InputMethodManager imm = (InputMethodManager) getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
        v.clearFocus();
    }
    super.onScrollChanged(l, t, oldl, oldt);
}
1
Prasad Korhale

誰かがそれを試してみたいなら、私はさまざまなソリューションを試すためのテストプロジェクトを作りました。 https://github.com/marchold/EditText-ErrorPopup-Scroll-View

1
Marc

最善の解決策は、スクロールビューの子にフォーカスオプションを追加することです。

Android:descendantFocusability="beforeDescendants"
Android:focusable="true"
Android:focusableInTouchMode="true"

Xmlファイルは次のようになります。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/scrollView"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent" >

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_marginRight="50dp"
        Android:descendantFocusability="beforeDescendants"
        Android:focusable="true"
        Android:focusableInTouchMode="true"
        Android:orientation="vertical" >

        <EditText
            Android:id="@+id/editText_one"
            Android:layout_width="match_parent"
            Android:layout_height="100dp"
            Android:text="TestApp 1" />

        <EditText
            Android:id="@+id/editText_two"
            Android:layout_width="match_parent"
            Android:layout_height="100dp"
            Android:text="TestApp 2" />

       <EditText
            Android:id="@+id/editText_three"
            Android:layout_width="match_parent"
            Android:layout_height="100dp"
            Android:text="TestApp 3" />


    </LinearLayout>

</ScrollView>
1
Aj 27

私は同様の問題を抱えていましたが、ようやく機能するようになりました。スクロールビューには、一連のカスタマイズされたボタンと、それに続くEditText(通常はフォーカスがありますが、フォーカスを失いたくない)が含まれています。ボタンがクリックされるたびに、スクロールビューはフォーカスされたEditTextに自動スクロールしました。 public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)をオーバーライドし、常にfalseViewGroupのデフォルトの動作)を返すとうまくいきました。それがあなたの状況にも役立つことを願っています。

1
dynamic_knight

descendantFocusabilityを持つScrollViewのLinearLayoutにblockDescendants属性を追加してこの問題を解決しました。

例えば:

<LinearLayout
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:orientation="vertical"
    Android:descendantFocusability="blocksDescendants" >
0
madx

私にとってはこのコードだけが機能します:

public static void preventScrollViewFromScrollingToEdiText(ScrollView view) {
    view.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
    view.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            v.requestFocusFromTouch();
            return false;
        }
    });
}

すべてのクレジットは この元の答え に移動します。

0
Oleksandr

これを試してください:)

public class CustomHorizontalScrollView extends HorizontalScrollView {

    public CustomHorizontalScrollView(Context context) {
    super(context);
    }

    public CustomHorizontalScrollView(Context context, AttributeSet attrs) {
    super(context, attrs);
    }

    public CustomHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
    return super.onTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    return super.onInterceptTouchEvent(ev);
    }

    @Override
    public void scrollTo(int x, int y) {
    super.scrollTo(x, y);
    }


    //Custom smooth scroll method since norm is final and cannot be overridden
    public final void smooothScrollToIfEnabled(int x, int y) {
    smoothScrollTo(x, y);
    }

    @Override
    protected int computeScrollDeltaToGetChildRectOnScreen(Android.graphics.Rect rect) {
/*  if (getContext() != null && getContext() instanceof Activity) {
        Activity activity = (Activity) getContext();
        if (!activity.isFinishing()) {
        View view = activity.getCurrentFocus();
        if (view != null) {
            if (view instanceof EditText) {
            return 0;
            }
        }
        }
    }
    return super.computeScrollDeltaToGetChildRectOnScreen(rect);

*/
return 0;
    }

}
0
Soo Chun Jung

私の解決策は以下のとおりです。ソースコードをトレースし、フォーカスされたアイテムによる自動スクロールを停止する機能をオーバーライドします。

focusedView.findViewById(R.id.textview_id_you_defined) != nullまたはfocusedView instanceof TextView == trueを使用して、focusedViewがTextViewであるか、その子がTextViewであるかを確認できます。

public class StopAutoFocusScrollView extends ScrollView {

    private View focusedView;
    private ScrollMonitorListener listener;

    public interface ScrollMonitorListener {
        public boolean enableScroll(View view);
    }
    public StopAutoFocusScrollView(Context context) {
        super(context);
    }

    public StopAutoFocusScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public StopAutoFocusScrollView(Context context, AttributeSet attrs, 
           int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setScrollMonitorListener(ScrollMonitorListener listener) {
        this.listener = listener;
    }

    @Override
    public void requestChildFocus(View child, View focused) {
        focusedView = focused
        super.requestChildFocus(child, focused);
    }
    //flow : requestChildFocus -> scrollToChild -> scrollBy
    //Therefore, you can give listener to determine you want scroll to or not
    @Override
    public void scrollBy(int x, int y) {
        if (listener == null || listener.enableScroll(focusedView)) {
            super.scrollBy(x, y);
        }
    }
}
0
Hsiao-Ting

私はこの腹立たしい欠乏に対してわずかに異なる異議がありました。 EditTextsの下にある複数のRadioButtonのいずれかをタップすると、スクロール位置がジャンプして、Androidが表示されフォーカスされたEditTextであると判断されました。

ScrollView.scrollTo(x,y)を発行したRunnableを介して現在の望ましいスクロール位置を保持しようとする試みはすべて、Androidによって忠実に無視されました!

私は、他の誰かが8時間の無駄な時間を節約できることを期待して、私のソリューションを共有します。

/* This interesting little 'hack' prevents unwanted scroll 'jump' occurring when
 user touches a RadioButton for example
[ Causes focus to change - but maybe this is a lesser evil! ] */
mScrollView.setOnTouchListener(new View.OnTouchListener() {
    @Override public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() != MotionEvent.ACTION_UP) 
            return false;

        mScrollView.clearFocus();
        return false;
    }
});
0
Bad Loser

アプリが向きの変更を処理するときに、この問題がよく発生します。

その場合、次の種類のコードを使用します。

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // to avoid the scrollview to scroll to this element automatically
    mEditTextSearch.setFocusable(false);
    // Get the saved scroll position
    final int scrolly = savedInstanceState.getInt("scrolly");
    mScrollView.post(new Runnable() {
        @Override
        public void run() {
            mScrollView.scrollTo(0, scrolly);
            // Restore the initial state of the EditText
            mEditTextSearch.setFocusable(true);
            mEditTextSearch.setFocusableInTouchMode(true);
            mEditTextSearch.setClickable(true);
        }
    });
    ...
}
0
ol_v_er

私にとって、ScrollView onTouchをオーバーライドすることはできませんでした。 Android:descendantFocusability = "beforeDescendants" Android:focusableInTouchMode = "true"も機能しませんでしたこれと他の言及されたソリューションは初めて機能しました。

他のビューに触れるときにキーボードを非表示にするコードを既に作成しているため、2行のコードを追加しただけで、まるでチャンピオンのように機能しました。

public static void setupUI(final Activity activity, final View view) {
    //view is the parent view in your layout
    OnTouchListener mTouchListener = new OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            try {
                View vFocused = null;
                vFocused = activity.getCurrentFocus();

                if (vFocused != null) {
                    hideSoftKeyboard(activity, v);
                    if (vFocused instanceof EditText) {
                        vFocused.clearFocus();//this is the trick to avoid ScrollView autoscroll
                    }
                }
            } catch (Exception e) {
            }
            return false;
        }
    };

    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText) && !(view instanceof ViewGroup)) {
        view.setOnTouchListener(mTouchListener);
    }

    // If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) {
        view.setOnTouchListener(mTouchListener);
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(activity, innerView);
        }
    }
}
public static void hideSoftKeyboard(Context context, View v) {
    InputMethodManager inputMethodManager = (InputMethodManager) context
            .getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}

ルートビューにもこれを追加しました:

Android:descendantFocusability="beforeDescendants" Android:focusableInTouchMode="true"

たぶん、本当に素敵なソリューションではありませんが、機能しています。

0