it-swarm.com.de

Beendet das automatische Scrollen von ScrollView zu einem EditText

Scheint ein häufiges Problem zu sein, ohne eine großartige Lösung, die ich gefunden habe. Ziel ist es, ein ScrollView daran zu hindern, automatisch zu einer EditText (oder einer anderen Ansicht) zu scrollen, die den Fokus hat.

Sie haben eine Reihe von Ansichten (Button, TextView usw.) in einer ScrollView, von denen eine eine EditText ist. Wenn Sie auf eine Schaltfläche in der ScrollView klicken, scrollt die ScrollView nach unten zur EditText (außerhalb des Bildschirms). Dies ist nicht erwünscht, da es andere Elemente gibt, die nicht vom Bildschirm gescrollt werden sollen.

Jetzt kann ich verhindern, dass dies geschieht, wenn der Bildschirm zum ersten Mal angezeigt wird, indem andere fokussierbare Elemente in ScrollView enthalten sind. Das allgemeine Problem besteht jedoch weiterhin. Der Benutzer scrollt manuell nach unten zu EditText, gibt einige Zahlen ein und scrollt dann nach oben (EditText wird ausgeblendet). Er klickt auf eine Schaltfläche in ScrollView und erraten Sie, was? Die ScrollView scrollt runter zu diesem Stopf EditText.

Ich denke darüber nach, die ScrollView zu erweitern und einige der dortigen Methoden wie findFocusableViewInBounds außer Kraft zu setzen, aber ich habe das Gefühl, ich werde mich nur in größere Schwierigkeiten bringen.

Bitte helfen Sie, wenn Sie können.

Ich habe mit Dingen wie der Höhe 0 EditText am oberen Rand meiner ScrollView herumgespielt und den anderen Elementen in der ScrollView die Eigenschaften "Nächstes fokussierbares Element" hinzugefügt. Ich nehme an, ein "Hack" besteht darin, dass die EditText den Fokus verliert, wenn die virtuelle oder manuelle Tastatur versteckt wird oder so.

41
Fraggle

Nachdem ich lange mit diesem Problem gekämpft hatte, habe ich eine Lösung gefunden, die zu funktionieren scheint, ohne zu hässlich zu sein. Stellen Sie zunächst sicher, dass für ViewGroup (direkt) Ihre EditTextdescendantFocusability auf "Before Descendants" gesetzt ist, focusable auf "true" und focusableInTouchMode auf "true" gesetzt ist. Dies ist nicht die ScrollView selbst, sondern das Layout, in dem Sie Ihre verschiedenen Ansichten haben. Fügen Sie als Nächstes eine onTouchListener zu Ihrer ScrollView hinzu, die bei jeder Berührung den Fokus von der EditText entfernt, wie dies zutrifft:

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;
    }
});

Sag mir, wenn das nicht klappt. Was passieren sollte, ist, dass das Layout den Fokus anstelle der EditText erhält, sodass kein Bildlauf erfolgen sollte.

36
thomas88wp

Erstellen Sie einfach eine leere Ansicht am oberen Rand des linearen Layouts

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

Single Line löst das Problem

19
KeT4yn

Ich hatte das gleiche Problem. Es gibt einen Trick, mit dem ich mich mit diesem Problem beschäftige:

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

Das Problem betrifft nicht den Java-Code, sondern den Manifestcode.

Fügen Sie in Ihrer AndroidManifest.xml der Aktivität ein Attribut hinzu:

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

Durch Hinzufügen von 2 Parametern in:

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

In welchem ​​Hauptlayout gibt es.

<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">

Dadurch wird EditText nicht automatisch fokussiert.

6
Rohan Mistry

Hier ist was ich getan habe

 <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>

In solchen Fällen müssen Sie alle anderen Ansichten durch einen expliziten Android:focusable="true" und dann <requestFocus></requestFocus> in der Bildlaufansicht fokussierbar machen. Dies sollte immer IMO funktionieren

4
SaKet

Mein Fix zu diesem schrecklichsten Fehler (ich muss beachten, dass dies vor API11 nur dann gilt, wenn sie die Fling-Methode geändert haben, um nicht dumm zu sein).

Die alte Fling-Methode findet den nächsten Fokus, den sie erreichen wird ... was nicht wirklich hilfreich ist. Andere Versionen dieser Klasse funktionieren nicht wirklich, da sie den Fokus stoppen, wenn der Benutzer das Formular wirklich über die Tastatur durchquert.

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

antwort von thomas88wp, https://stackoverflow.com/a/6486348/528746 arbeitete für mich . Ich hatte jedoch zwei Probleme: 1. Beim Scrollen wollte ich die Tastatur ausblenden
2. Ich hatte viele EditText-Ansichten und wollte sie nicht für jeden von ihnen schreiben
(Ich bekomme getActivity (), da ich dies in einem Fragment und nicht in einer Aktivität schreibe)

    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

Erstellen Sie eine benutzerdefinierte ScrollView (erstellen Sie eine Klasse und lassen Sie HorizontalScrollView erweitern), und erstellen Sie einen Getter-Setter für den Bildlauf. Überschreiben Sie dann computeScrollDeltaToGetChildRectOnScreen.

So funktioniert es: Jedes Mal, wenn Android einen Bearbeitungstext oder etwas außerhalb des Bildschirms fokussiert hat, ruft es die Methode computeScrollDeltaToGetChildRectOnScreen auf, um es anzuzeigen. Wenn Sie es außer Kraft setzen und 0 zurückgeben, wenn es deaktiviert ist, wird es nicht scrollen ...

So haben Sie eine benutzerdefinierte Ansicht wie folgt:

    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);
    }


}

Sie können dies in Ihrem XML folgendermaßen verwenden:

<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

Nur dieser Code funktioniert für mich:

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;
        }
    });
}

Alle Credits gehen an diese ursprüngliche Antwort .

1
Oleksandr

Eine andere Version des Codes von 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

Wir können eine benutzerdefinierte ScrollView schreiben und die onScrollChanged -Methode überschreiben, den Fokus aus der fokussierten Ansicht löschen und optional die Tastatur ausblenden.

@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

Ich habe ein Testprojekt gemacht, um mit den verschiedenen Lösungen zu experimentieren, wenn jemand damit spielen möchte. https://github.com/marchold/EditText-ErrorPopup-Scroll-View

1
Marc

Ich hatte ein ähnliches Problem und bekam es endlich zum Laufen. Meine Bildlaufansicht enthält eine Reihe von benutzerdefinierten Schaltflächen, gefolgt von einer EditText (die normalerweise den Fokus hat, aber ich möchte nicht, dass sie den Fokus verliert). Jedes Mal, wenn auf die Schaltflächen geklickt wurde, wurde die Bildlaufansicht automatisch zur fokussierten EditText gescrollt. Das Überschreiben von public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate) und das wiederholte Zurückgeben von false (Standardverhalten einer ViewGroup) hat den Trick bewirkt. Ich hoffe, es hilft auch in Ihrer Situation.

1
dynamic_knight

Ich habe dieses Problem gelöst, indem ich das descendantFocusability-Attribut mit dem Wert blockDescendants zu dem ScrollLayout-Element mit ScrollView hinzugefügt hat.

Zum Beispiel:

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

Probier diese :)

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

Im Folgenden finden Sie eine Lösung, um den Quellcode zu verfolgen und einige Funktionen zu überschreiben, um das automatische Scrollen nach fokussierten Elementen zu stoppen.

Sie können überprüfen, ob focusedView TextView ist oder dessen untergeordnetes Element TextView ist Verwenden Sie focusedView.findViewById(R.id.textview_id_you_defined) != null oder focusedView instanceof TextView == true.

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

Die beste Lösung ist das Hinzufügen von Fokusoptionen für das untergeordnete Element Ihrer Bildlaufansicht: 

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

Dann sieht Ihre XML-Datei folgendermaßen aus:

<?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>
0
Aj 27

Ich hatte etwas andere Einwände gegen diesen ärgerlichen Mangel. Immer, wenn ich einen der RadioButtons unterhalb der EditTexts angetippt habe, sprang die Bildlaufposition an das, was Android als sichtbaren und fokussierten EditText feststellte.

Alle Versuche, die aktuelle gewünschte Bildlaufposition über eine Runnable, die ScrollView.scrollTo(x,y) ausgegeben hat, beizubehalten, wurden von Android pflichtgemäß ignoriert!

Ich teile meine Lösung in der Hoffnung, dass dadurch 8 (acht) Stunden verschwendet werden könnten. 

/* 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

Dieses Problem tritt häufig auf, wenn meine Apps die Orientierungsänderung durchführen.

In diesem Fall verwende ich folgende Art von Code:

@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

Für mich funktionierte es nicht, ScrollView onTouch zu überschreiben. Funktionierte auch nicht für Android: descendantFocusability = "beforeDescendants" Android: focusableInTouchMode = "true" Diese und eine andere erwähnte Lösung funktionierte nur zum ersten Mal - nur wenn EditText nicht ausgewählt wurde Autoscrolls wieder.

Da ich bereits einen Code geschrieben habe, um eine Tastatur beim Berühren anderer Ansichten auszublenden, habe ich nur zwei Codezeilen hinzugefügt, und es funktionierte wie ein Champion:

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);
}

fügte dies auch in der root-Ansicht hinzu:

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

Vielleicht ist es nicht wirklich eine schöne Lösung, aber es funktioniert.

0