What is the correct way to implement a GridViewPag

2020-02-28 10:56发布

问题:

I'm trying to implement a GridViewPager so I can switch between 2 unique views. So far I haven't been able to add any views at all. Below is my code

public class Gridder extends Activity {

private TextView mTextView;
GridViewPager gridViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_gridder);
    final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
    final LayoutInflater inflater = getLayoutInflater();
    stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
        @Override
        public void onLayoutInflated(WatchViewStub stub) {
            gridViewPager = (GridViewPager) findViewById(R.id.pager);
            gridViewPager.setAdapter(new MyGridViewPagerAdapter());
            gridViewPager.addView(inflater.inflate(R.layout.selector_generic, stub));;
        }
    });
}

private class MyGridViewPagerAdapter extends GridPagerAdapter {
    @Override
    protected void destroyItem(ViewGroup arg0, int arg1, int arg2, Object arg3) {
    }

    @Override
    public int getColumnCount(int arg0) {
        return 1;
    }

    @Override
    public int getRowCount() {
        return 1;
    }

    @Override
    protected Object instantiateItem(ViewGroup arg0, int arg1, int arg2) {
        return null;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return false;
    }
}

In the rectangle child of the main view stub, I have a GridViewPager with the id pager, exactly as I referenced above:

<android.support.wearable.view.GridViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:keepScreenOn="true" />

This code throws an illegal state exception and reports that my child view already has a parent. See below

07-16 14:44:40.847  12940-12940/com.larvalabs.weartest E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.larvalabs.weartest, PID: 12940
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.view.ViewGroup.addViewInner(ViewGroup.java:3561)
        at android.view.ViewGroup.addView(ViewGroup.java:3414)
        at android.support.wearable.view.GridViewPager.addView(GridViewPager.java:1088)
        at android.view.ViewGroup.addView(ViewGroup.java:3359)
        at android.view.ViewGroup.addView(ViewGroup.java:3335)
        at com.larvalabs.weartest.Gridder$1.onLayoutInflated(Gridder.java:30)
        at android.support.wearable.view.WatchViewStub.inflate(WatchViewStub.java:133)
        at android.support.wearable.view.WatchViewStub.onMeasure(WatchViewStub.java:141)
        at android.view.View.measure(View.java:16648)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
        at android.view.View.measure(View.java:16648)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2321)
        at android.view.View.measure(View.java:16648)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1959)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1145)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1340)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1032)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5657)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
        at android.view.Choreographer.doCallbacks(Choreographer.java:574)
        at android.view.Choreographer.doFrame(Choreographer.java:544)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5026)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
        at dalvik.system.NativeStart.main(Native Method)

回答1:

Stacktrace explanation:

First of all - the following line is wrong:

gridViewPager.addView(inflater.inflate(R.layout.selector_generic, stub));

inflate(int resource, ViewGroup root)
with root!=null is the same as
inflate(int resource, ViewGroup root, boolean attachToRoot) with last argument as true.

So according to the documentation it will inflate view from resource and attach it to root -> then return a root.

this will try to attach newly inflated view to stub, and then try to attach stub (result from inflate() with specified root) to the gridViewPager. But stub already has a parent (it's already attached to the view hierarchy in your activity). So this is an explanation of stacktrace:)
Please note that you should NOT add views to gridViewPager via .addView() methods - you should do it via adapter instead and that line should be removed.

GridPagerAdapter example:

Here is simple implementation for GridPagerAdapter;

private class MyGridViewPagerAdapter extends GridPagerAdapter {
    @Override
    public int getColumnCount(int arg0) {
        return 2;
    }

    @Override
    public int getRowCount() {
        return 2;
    }

    @Override
    protected Object instantiateItem(ViewGroup container, int row, int col) {
        final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.grid_view_pager_item, container, false);
        final TextView textView = (TextView) view.findViewById(R.id.textView);
        textView.setText(String.format("Page:\n%1$s, %2$s", row, col));
        container.addView(view);
        return view;
    }

    @Override
    protected void destroyItem(ViewGroup container, int row, int col, Object view) {
        container.removeView((View)view);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==object;
    }
}

Content of grid_view_pager_item.xml:

<FrameLayout 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:padding="10dp">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_gravity="center_vertical"
        android:gravity="center"
        android:background="#4400ff00"
        android:textColor="#000000"
        android:textSize="26sp"/> 

</FrameLayout>

Here is a final result of 2x2 grid. Two screens are in still state and two are pictured during the pages being half-scrolled:)



Looking for more?

This is just an example of using standard GridPagerAdapter. If you want more advanced one, with usage of FragmentGridPagerAdapter, CardFragments and pages backgrounds - please see the GridViewPager example located in platform samples:
{sdk_root}/samples/android-20/wearable/GridViewPager