私が見つけた素晴らしい解決策がない一般的な問題のようです。目標は、ScrollView
がフォーカスを持っているEditText
(またはその問題のビュー)への自動スクロールを停止することです。
Button
にビューの束(TextView
s、ScrollView
sなど)があり、そのうちの1つはEditText
です。 ScrollView
内のボタンをクリックすると、ScrollView
はEditText
(画面外)までスクロールダウンします。画面からスクロールしたくない他の要素があるため、これは望ましくありません。
これで、ScrollView
に他のフォーカス可能な要素を含めることで、画面が最初に表示されるときにこれを防ぐことができます。ただし、一般的な問題は依然として存在します。ユーザーは手動でEditText
までスクロールダウンし、いくつかの数字を入力してから、一番上までスクロールし(EditText
画面外)、ScrollView
のボタンをクリックし、何だと思う? ScrollView
はそのEditText
までスクロールダウンします。
私はScrollView
を拡張し、findFocusableViewInBounds
のようなメソッドのいくつかをオーバーライドすることを考えていますが、もっと苦労することになりそうです。
可能であれば助けてください。
EditText
の上部に0の高さScrollView
があり、ScrollView
内の他のアイテムにNext Focusable要素のプロパティを追加するなどのことをいじりました。仮想キーボードまたは手動キーボードが非表示になったときなどに、EditText
にフォーカスを移すことが1つの「ハック」だと思います。
その問題にかなり長い間苦労した後、私はあまりtooくないことなく動作するように見える解決策を見つけました。まず、ViewGroup
(直接)を含むEditText
にdescendantFocusability
が[Before Descendants]に設定されていること、_focusable
が[true]に設定されていること、およびfocusableInTouchMode
を「true」に設定します。これはScrollView
そのものではなく、さまざまなビューがある内部のレイアウトです。次に、onTouchListener
をScrollView
に追加します。これは、次のように、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
の代わりにフォーカスを取得するため、スクロールが発生しないことです。
Linearlayoutの上部に空のビューを作成するだけです
<View Android:layout_width="fill_parent" Android:id="@+id/focus_view" Android:layout_height="0dp" Android:focusable="true" Android:focusableInTouchMode="true"><requestFocus/></View>
単一行で問題を解決します
同じ問題がありました。この問題に対処するために使用しているトリックが1つあります。
public void onClick(View v) {
button.requestFocusFromTouch(); //prevents from loosing focus and scrolling view down
....
}
問題はJavaコードではなく、マニフェストコードにあります。
AndroidManifest.xmlで、アクティビティに属性を追加します。
<activity Android:name=".MyActivity" Android:windowSoftInputMode="adjustPan"> </activity>
以下に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
はオートフォーカスされません。
これが私がしたことです
<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のたびに機能するはずです
この最も恐ろしいバグに対する私の修正(これは、フリングメソッドをバカにならないように変更した場合にのみ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;
}
}
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;
}
});
カスタムScrollViewを作成し(クラスを作成し、HorizontalScrollViewを拡張します)、スクロール可能なゲッターセッターを作成します。その後、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>
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;
}
});
カスタム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);
}
誰かがそれを試してみたいなら、私はさまざまなソリューションを試すためのテストプロジェクトを作りました。 https://github.com/marchold/EditText-ErrorPopup-Scroll-View
最善の解決策は、スクロールビューの子にフォーカスオプションを追加することです。
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>
私は同様の問題を抱えていましたが、ようやく機能するようになりました。スクロールビューには、一連のカスタマイズされたボタンと、それに続くEditText
(通常はフォーカスがありますが、フォーカスを失いたくない)が含まれています。ボタンがクリックされるたびに、スクロールビューはフォーカスされたEditText
に自動スクロールしました。 public boolean requestChildRectangleOnScreen(final View child, final Rect rectangle, final boolean immediate)
をオーバーライドし、常にfalse
(ViewGroup
のデフォルトの動作)を返すとうまくいきました。それがあなたの状況にも役立つことを願っています。
値descendantFocusability
を持つScrollViewのLinearLayoutにblockDescendants
属性を追加してこの問題を解決しました。
例えば:
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:orientation="vertical"
Android:descendantFocusability="blocksDescendants" >
私にとってはこのコードだけが機能します:
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;
}
});
}
すべてのクレジットは この元の答え に移動します。
これを試してください:)
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;
}
}
私の解決策は以下のとおりです。ソースコードをトレースし、フォーカスされたアイテムによる自動スクロールを停止する機能をオーバーライドします。
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);
}
}
}
私はこの腹立たしい欠乏に対してわずかに異なる異議がありました。 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;
}
});
アプリが向きの変更を処理するときに、この問題がよく発生します。
その場合、次の種類のコードを使用します。
@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);
}
});
...
}
私にとって、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"
たぶん、本当に素敵なソリューションではありませんが、機能しています。