Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two ViewPager fragments side by side will not focus EditText

I've implemented a ViewPager that have different fragments on each page. In Portrait mode, the ViewPager displays a single fragment. In Landscape mode, the ViewPager displays two fragments side by side. I've achieved this by using the getPageWidth() override in the ViewPagerAdapter. Like this:

@Override
public float getPageWidth(int position) {
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    if ((metrics.widthPixels / metrics.density) > 900) {
        return (0.5f);
    }
    return super.getPageWidth(position);
}

In both fragments I have EditText fields.

When in Landscape mode and the two fragments are sitting side by side, I can click and give focus to the EditText on the left fragment. When trying to click the EditText on the right fragment, I cannot and get the following warning in my log:

W/IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection

MainActivity.java

public class MainActivity extends AppCompatActivity /*implements PageLoader*/ {
    MyPagerAdapter adapter;
    DirectionalViewPager pager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get the ViewPager and set it's PagerAdapter so that it can display items
        pager = (DirectionalViewPager) findViewById(R.id.vpPager);
        adapter = new MyPagerAdapter(getSupportFragmentManager());
        pager.setOffscreenPageLimit(8);
        pager.setAdapter(adapter);
    }

    public class MyPagerAdapter extends SmartFragmentStatePagerAdapter {
        private static final int NUM_ITEMS = 4;

        public MyPagerAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
        }

        // Returns total number of pages
        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        // Returns the fragment to display for that page
        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0: // Fragment # 0 - This will show Frag1
                    return Frag1.newInstance(position, Frag1.class.getSimpleName());
                case 1: // Fragment # 0 - This will show Frag1 different title
                    return Frag1.newInstance(position, Frag1.class.getSimpleName());
                case 2: // Fragment # 1 - This will show Frag2
                    return Frag2.newInstance(position, Frag2.class.getSimpleName());
                default:
                    return Frag3.newInstance(position, Frag3.class.getSimpleName());
            }
        }

        // Returns the page title for the top indicator
        @Override
        public CharSequence getPageTitle(int position) {
            return "Page " + position;

        }

        @Override
        public float getPageWidth(int position) {
            DisplayMetrics metrics = getResources().getDisplayMetrics();
            if ((metrics.widthPixels / metrics.density) > 900) {
                return (0.5f);
            }
            return super.getPageWidth(position);
        }
    }
}

Frag1.java, Frag2.java, and Frag3.java

public class Frag1 extends Fragment {
    // newInstance constructor for creating fragment with arguments
    public static Frag1 newInstance(int page, String title) {
        Frag1 fragmentFirst = new Frag1();
        Bundle args = new Bundle();
        args.putInt("someInt", page);
        args.putString("someTitle", title);
        fragmentFirst.setArguments(args);
        return fragmentFirst;
    }
    // Inflate the view for the fragment based on layout XML
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag1, container, false);
        return view;
    }
}

frag1.xml, frag2.xml, and frag3.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#cc2">

    <TextView
        android:id="@+id/txt_frag1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="txt_frag1"
        />
    <Button
        android:id="@+id/btn_frag1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="btn_frag1"
        android:textSize="26dp" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/et_frag1"
        android:ems="10"
        android:text="ET_FRAG1"/>

</LinearLayout>

Why is it happening and how do I get around it?

like image 809
chaser Avatar asked Oct 30 '22 01:10

chaser


1 Answers

After countless hours scouring the internet, I found this SO answer which explains that this problem is innately a ViewPager problem. Therefore the only way to truly fix it is to modify the ViewPager source itself.

void populate(int newCurrentItem) {

    // ... CODES OMITTED

    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) {
    float curItemOffset = mCurItem * info.widthFactor;  // find the offset value for curr item
    float offsetUpperBound = curItemOffset + 1;         // +1 to get the upper bound offset value
    if ((info.offset >= curItemOffset) && (info.offset < offsetUpperBound))
        return true; // curr item offset value is within bound, hence it is on the screen
    return false;
}

Please up-vote this or the other SO answer if you find this useful, because it was really hard to dig it up due to the unpopularity of this problem. Although I don't understand why that would be considering ViewPager is itself popular, so surely people have used EditText before (perhaps just not many use them in multiple pages on the same screen)

like image 165
chaser Avatar answered Nov 15 '22 05:11

chaser