简体   繁体   English

Android:EditText焦点跳到ScrollView内部

[英]Android: EditText focus jumps to another inside ScrollView

I have multiple fragments horizontally adjacent using a ViewPager. 我使用ViewPager在水平方向相邻多个片段。 Each fragment has a ScrollView that contains a table with many EditText views. 每个片段都有一个ScrollView,其中包含一个包含许多EditText视图的表。 When I click on a EditText, it gains focus and then loses it to some other EditText in the leftmost fragment. 当我单击EditText时,它会获得焦点,然后将其丢失到最左侧片段中的其他某个EditText中。 Sometimes the focus switch happens immediately, sometimes while I am typing into a EditText. 有时焦点切换会立即发生,有时是在我输入EditText时。 Usually, the topmost EditText in the leftmost fragment steals focus. 通常,最左边的片段中最上面的EditText会占据焦点。

I don't see the problem when a EditText in the leftmost fragment is clicked, even if it is not the topmost one. 单击最左边的片段中的EditText时,即使它不是最顶部的片段,我也看不到问题。 Its like Android doesn't like focusing on a TextView that is not along the left edge. 就像Android一样,它不喜欢专注于不在左边缘的TextView。 Does this sound familiar? 这听起来很熟悉吗?

I have unsuccessfully tried overriding the findFocus() method in ScrollView as suggested by Skip in randomly-jumping . 我没有按照Skip在random -jumping中建议的那样尝试在ScrollView中重写findFocus()方法。 The source code attached is huge, but it may be useful to others... 附带的源代码巨大,但对其他人可能有用...

package com.example.slideViewPager;

import java.util.ArrayList;

import com.example.slideViewPager.R;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

public class SlideViewPager extends FragmentActivity {
    private static Context g;
    private static ArrayList<String> pages;
    private static int currentItem;    // restore page position after tilt
    private static int[] scrollStateY;
    private static NotesViewFragment[] fragmentArray;
    private static int id;            // to create unique view ids

    @Override
    public void onCreate(Bundle savedInstanceState) {
        g = getApplicationContext();
        setContentView(R.layout.activity_slide_view_pager);
        pages = new ArrayList<String>();
        for (int i = 0; i < 4; ++i)
            pages.add(Integer.toString(i));

        if (savedInstanceState == null) {
            currentItem = 0;
            scrollStateY = new int[pages.size()];    // assume all 0s
            id = 0;
        }
        else {
            currentItem = savedInstanceState.getInt("currentItem");
            scrollStateY = savedInstanceState.getIntArray("scrollStateY");
            id = savedInstanceState.getInt("id");
            System.out.println("Notes onCreate RESTORE: currentItem="+currentItem+"; id="+id);
        }

        fragmentArray = new NotesViewFragment[pages.size()];
        System.out.println("onCreate: start");
        super.onCreate(savedInstanceState);
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        NotesPagerAdapter pagerAdapter = new NotesPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(pagerAdapter);
        viewPager.setCurrentItem(currentItem);
        viewPager.setOnPageChangeListener(onPageChangeListener);
        System.out.println("onCreate: done");
    }

    @Override
    public void onSaveInstanceState(Bundle bundle) {
        System.out.println("Notes: onSaveInstanceState: currentItem="+currentItem);
        super.onSaveInstanceState(bundle);
        bundle.putInt("currentItem", currentItem);
        bundle.putIntArray("scrollStateY", scrollStateY);
        bundle.putInt("id", id);
    }

    public void onBackPressed() {
        System.out.println("Notes: onBackPressed: currentItem="+currentItem);
        super.onBackPressed();
    }

    /////////////////////////////////////////////////////////////////
    public static class NotesPagerAdapter extends FragmentPagerAdapter {

        public NotesPagerAdapter(FragmentManager fm) {
            super(fm);
            System.out.println("NotesPagerAdapter: this="+this);
        }

        @Override
        public Fragment getItem(int position) {
            String pageNum = pages.get(position);
            NotesViewFragment notesViewFragment = NotesViewFragment.initialize(pageNum, position);        // create new fragment
            System.out.println("NotesPagerAdapter: getItem: CREATED position:"+position+" = page:"+pageNum+"; fragment="+notesViewFragment);
            return notesViewFragment;
        }

        @Override
        public int getCount() {
            return pages.size();
        }

        // nothing being done here
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            System.out.println("NotesPagerAdapter: destroyItem position="+position);
            super.destroyItem(container, position, object);
        }

        @Override
        public float getPageWidth(int position) {
            System.out.println("NotesPagerAdapter: getPageWidth position="+position);
            return (1.0f/2);    // vary # pages/view
        }
    }

    //////////////////////////////////////////////////////
    public static class NotesViewFragment extends Fragment {
        private int position;    // needed to save scrollY into scrollStateY[]
        private String pageNum;    // index
        private ScrollView itemScrollView;

        public NotesViewFragment() {
            super();
            System.out.println("NotesViewFragment: new this:"+this);
        }

        public static NotesViewFragment initialize(String page, int position) {
            NotesViewFragment notesViewFragment = new NotesViewFragment();
            System.out.println("NotesViewFragment: initialize: this:"+notesViewFragment+"; page: "+page+"; position="+position);
            Bundle args = new Bundle();
            args.putString("pageNum", page);
            args.putInt("position", position);
            scrollStateY[position] = 0;        // start at top
            notesViewFragment.setArguments(args);
            return notesViewFragment;
        }

        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            System.out.println("NotesViewFragment: onSaveInstanceState this:"+this+"; page: "+pageNum+"; scrollView="+itemScrollView);
            // itemScrollView will be null occasionally (attached but not createViewed)
            if (itemScrollView != null)
                scrollStateY[position] = itemScrollView.getScrollY();
            outState.putString("pageNum", pageNum);
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Bundle args = getArguments();
            if (args != null) {
                pageNum = args.getString("pageNum");
                position = args.getInt("position");
                fragmentArray[position] = this;
                System.out.println("NotesViewFragment: onCreate: this:"+this+"; page: "+pageNum+"; position="+position+"; yPos="+scrollStateY[position]);
            }
            else    // should not happen
                System.out.println("NotesViewFragment: onCreate: NO ARGS !!! this:"+this);            
        }

        public void onDestroyView() {
            super.onDestroyView();
            if (itemScrollView != null)        // could be null if not createViewed
                scrollStateY[position] = itemScrollView.getScrollY();

            System.out.println("NotesViewFragment: onDestroyView: "+pageNum+" this="+this+"; scrollY="+scrollStateY[position]);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            if (savedInstanceState != null) {    // config change happened
                pageNum = savedInstanceState.getString("pageNum");
                System.out.println("NotesViewFragment: onCreateView: RESTORED "+pageNum+" this="+this+"; yPos="+scrollStateY[position]);
            }
            else
                System.out.println("NotesViewFragment: onCreateView: "+pageNum+"; this="+this);

            // Inflate the layout containing a title and body text.
            ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
            TableLayout itemNotesTable = (TableLayout) rootView.findViewById(R.id.tableLayoutNotes);
            TextView itemTitle = (TextView) rootView.findViewById(R.id.textViewTitle);
            itemScrollView = (ScrollView) rootView.findViewById(R.id.scroll_form);
            itemTitle.setText("Team "+pageNum);

            for (int i = 0; i < 12; ++i) {
                TableRow tr = new TableRow(g);
                tr.setId(++id);

                ImageView itemThumbnail = new ImageView(g);
                itemThumbnail.setId(++id);
                itemThumbnail.setImageResource(R.drawable.notes);    // set icon
                tr.addView(itemThumbnail);

                LinearLayout ll = new LinearLayout(g);
                ll.setId(++id);
                ll.setOrientation(LinearLayout.VERTICAL);
                String text = " Team "+pageNum;
                ll.addView(makeTextItem(text, false));

                TextView textView;
                if ((id % 2) == 0) {    // make some entries editable
                    textView = new EditText(g);
                    textView.setFocusable(true);
                    textView.setFocusableInTouchMode(true);
                    textView.setImeOptions(EditorInfo.IME_FLAG_NO_ENTER_ACTION);
                    textView.setOnFocusChangeListener(onFocusChangeListener);
                }
                else
                    textView = new TextView(g);

                textView.setId(++id);
                String str = "Line "+i+" is blah blah blah and more blah blah blah";
                textView.setText(str);
                textView.setTextColor(Color.BLACK);
                textView.setTextSize(16);
                textView.setPadding(2, 2,  2,  2);        // start, top, end, bottom
                ll.addView(textView);
                tr.addView(ll);
                itemNotesTable.addView(tr);
                tr.setOnClickListener(noteClicked);        // make everything in table row clickable
            }

            if (scrollStateY[position] != 0) {
                itemScrollView.post(new Runnable() {
                    public void run() {
                        itemScrollView.scrollTo(0, scrollStateY[position]);
                    }
                });
            }
            return rootView;
        }

        // called from fillNotes
        private static TextView makeTextItem(String label, boolean bold) {
            TextView item = new TextView(g);
            item.setId(++id);
            item.setText(label);
            item.setTextColor(Color.BLACK);
            item.setTextSize(16);
            if (bold)
                item.setTypeface(null, Typeface.BOLD);
            return item;
        }
    }

    // listens for horizontal page scrolls
    ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            System.out.println("onPageChangeListener: leaving page:"+currentItem+"; for page: "+position);
            currentItem = position;
        }
    };

    static View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {

        @Override
        public void onFocusChange(View view, boolean hasFocus) {
            System.out.println("onFocusChangeListener: "+hasFocus+"; "+view);
            if (! hasFocus) {
                EditText editText = (EditText) view;
                String text = editText.getText().toString();
                System.out.println("onFocusChangeListener: "+text);
            }
        }
    };

    static OnClickListener noteClicked = new OnClickListener() {
        public void onClick(View tr) {
            for (View parent = tr; parent != null; parent = (View) parent.getParent()) {
                for (NotesViewFragment fragment : fragmentArray)
                    if (fragment != null  &&  fragment.itemScrollView == parent) {
                        System.out.println("noteClicked: page: "+fragment.pageNum);
                        return;
                    }
            }
        }
    };
}

There are a couple of XML files: fragment_screen_slide_page.xml .......... 有几个XML文件:fragment_screen_slide_page.xml ..........

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textViewTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <View
        android:id="@+id/separator"
        android:layout_width="fill_parent"
        android:layout_height="1dip"
        android:layout_alignParentRight="true"
        android:layout_marginTop="4dp"
        android:layout_below="@+id/textViewTitle"
        android:background="#888888" />

    <ScrollView
        android:id="@+id/scroll_form"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/separator" >

        <TableLayout
            android:id="@+id/tableLayoutNotes"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:stretchColumns="1"  >
        </TableLayout>
    </ScrollView>
</RelativeLayout>

and activity_slide_view_pager.xml ---------- 和activity_slide_view_pager.xml ----------

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    android:descendantFocusability="beforeDescendants"
    android:focusableInTouchMode="true"
    tools:context=".Page" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

Focus is changing to the leftmost fragment because that fragment is the current item of the ViewPager . 焦点正在更改到最左边的片段,因为该片段是ViewPager的当前项。 You might try to use setCurrentItem(item) to set the current item to the other fragment (when the user clicks on it) so the left one doesn't steal focus. 您可以尝试使用setCurrentItem(item)将当前项目设置为另一个片段(当用户单击该片段时),以便左边的片段不会占据焦点。

This is happening because at the end of the populate() function of the ViewPager it will always give focus to a view that is in the current item. 之所以发生这种情况,是因为在ViewPagerpopulate()函数的ViewPager ,它将始终将焦点放在当前项中的视图上。 You could also fix this issue by copying the ViewPager source and changing this code to allow any fragment which is on the screen to have a child with focus. 您还可以通过复制ViewPager源并更改此代码以允许屏幕上的任何片段都具有焦点的子对象来解决此问题。

Change this: 更改此:

if (hasFocus()) {
    View currentFocused = findFocus();
    ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
    if (ii == null || ii.position != mCurItem) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            ii = infoForChild(child);
            if (ii != null && ii.position == mCurItem) {
                if (child.requestFocus(FOCUS_FORWARD)) {
                    break;
                }
            }
        }
    }
}

To something like this: 对于这样的事情:

if (hasFocus()) {
    View currentFocused = findFocus();
    ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;
    if (ii == null || !infoIsOnScreen(ii)) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            ii = infoForChild(child);
            if (ii != null && infoIsOnScreen(ii)) {
                if (child.requestFocus(FOCUS_FORWARD)) {
                    break;
                }
            }
        }
    }
}

public boolean infoIsOnScreen(ItemInfo info) {
    // Code to determine if the view is on the screen using ii.offset etc.
}

I found the problem, it was the lines in the main activity XML: activity_slide_view_pager.xml 我发现了问题,它是主要活动XML中的行:activity_slide_view_pager.xml

    android:focusableInTouchMode="true"
    android:descendantFocusability="beforeDescendants"

This was causing the RelativeLayout to grab focus. 这导致RelativeLayout抢占焦点。

As an addition to @Cameron Ketcham answer, the best fix of the issue is Creating your own CustomViewpager class that extends from ViewPager and overriding the RequestChildFocus method with null so as to disable the automatic focus behavior. 作为@Cameron Ketcham答案的补充,此问题的最佳解决方案是创建从ViewPager扩展的自己的CustomViewpager类,并使用null覆盖RequestChildFocus方法,以禁用自动聚焦行为。

Your customeViewPager class will look something like this 您的customeViewPager类将如下所示

public class CustomeViewPager extends ViewPager {

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

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

   @Override
   public void requestChildFocus(View child, View focused) {
       //Do nothing, disables automatic focus behaviour

   }

}

You can now use this class in your xml and continue from there. 现在,您可以在xml中使用此类,然后从那里继续。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM