Question

I have a map in a gallery, together with other pages. When I am sliding the gallery pages with touch gestures everything works fine. But when I move the map away using a button on a dialog, and then slide back to the map, the map does no longer smoothely follow the panning gesture. It rather follows with a noticeable delay. As soon as I hide the map once again with a dialog and close the dialog, e.g. with the back button, the behaviour returns to normal.

To reproduce it, I have stripped down the code as much as possible.

The gallery contains a DummyView, then a MapView, and finally again a DummyView as pages. DummyView and GalleryView are added as local classes to the Activity.

public class MainActivity extends FragmentActivity {

private static final int REQUEST_CODE_RECOVER_PLAY_SERVICES = 0;
private Gallery gallery;
private MapView mapView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_map_in_gallery);
    final View[] views = new View[3];
    views[0] = new DummyView(this, "Page 1");
    views[1] = mapView = new MapView(this);
    views[2] = new DummyView(this, "Page 3");
    gallery = (Gallery) findViewById(R.id.gallery);
    BaseAdapter adapter = new ArrayAdapter<View>(this, 0, views) {
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return views[position];
        }
    };
    gallery.setAdapter(adapter);
    // Let every page cover the whole screen
    gallery.setUnselectedAlpha(1);
    gallery.setSpacing(30);
}

@Override
protected void onResume() {
    super.onResume();
    checkPlayServices();
};

@Override
/** Define the dialog, which appears, when the Page-Button is pressed and
 * which allows to page in the gallery instead of swiping.
 */
protected Dialog onCreateDialog(int id) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Page");
    builder.setPositiveButton("->", new OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            page(+1);
        }
    });
    builder.setNegativeButton("<-", new OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            page(-1);
        }
    });
    return builder.create();
}

private void page(int increment) {
    int count = gallery.getAdapter().getCount();
    int nextPos = gallery.getSelectedItemPosition() + increment;
    if (nextPos >= count) {
        nextPos = 0;
    } else if (nextPos < 0) {
        nextPos = count - 1;
    }
    gallery.setSelection(nextPos);
}

private boolean checkPlayServices() {
    int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    if (status != ConnectionResult.SUCCESS) {
        if (GooglePlayServicesUtil.isUserRecoverableError(status)) {
            showErrorDialog(status);
        } else {
            Toast.makeText(this, "This device is not supported.",
                    Toast.LENGTH_LONG).show();
            finish();
        }
        return false;
    }
    return true;
}

void showErrorDialog(int code) {
    GooglePlayServicesUtil.getErrorDialog(code, this,
            REQUEST_CODE_RECOVER_PLAY_SERVICES).show();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case REQUEST_CODE_RECOVER_PLAY_SERVICES:
        if (resultCode == RESULT_CANCELED) {
            Toast.makeText(this, "Google Play Services must be installed.",
                    Toast.LENGTH_SHORT).show();
            finish();
        }
        return;
    }
    super.onActivityResult(requestCode, resultCode, data);
}

public void onButtonClick1(View v) {
    showDialog(0); // Only one dialog defined
}

public void onButtonClick2(View v) {
    mapView.togglePanOrSlide();
}
}

class DummyView extends View {

private String text;
private Paint paint;

/** Simple view which draws a text in the center of the screen. */
public DummyView(Context context, String text) {
    super(context);
    this.text = text;
    paint = new Paint();
    paint.setColor(Color.WHITE);
    paint.setTextSize(20);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawText(text, getWidth() / 2, getHeight() / 2, paint);
}

}

class MapView extends RelativeLayout {

/** Switches the map gestures off, to allow sliding the map in the gallery. */
private boolean panAllowed;

public MapView(Context context) {
    super(context);
    inflate(context, R.layout.map_fragment, this);
}

public void togglePanOrSlide() {
    panAllowed = !panAllowed;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    if (!panAllowed) {
        // Catch the event from the map, so the gallery will slide the page
        // instead.
        return true;
    }
    return super.onInterceptTouchEvent(event);
}

}

Here are the two layouts inflated in the coding:

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

<Button
    android:id="@+id/button1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onButtonClick1"
    android:text="Open Paging Dialog" />

<Button
    android:id="@+id/button2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="onButtonClick2"
    android:text="Pan Map &lt;-> Slide Page" />

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="0dip"
    android:layout_weight="1" >

    <Gallery
        android:id="@+id/gallery"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center"
        android:background="@android:drawable/alert_dark_frame" />
</RelativeLayout>

</LinearLayout>

and map_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- Merged into a RelativeLayout-Extension -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<fragment
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.SupportMapFragment" />

</merge>

The layout adds two buttons on top of the gallery. The first opens a dialog, which allows to page back and forth in the gallery. The second button switches the maps behaviour. It can be either panned, or its full page can be slid to the left or right. When the map is paged away using the first button, and then slid back using a slide gesture the problem appears.

The second button allows to switch off the maps panning, so the map reacts on usual gallery sliding. When the map is paged back and forth using sliding gestures, the panning works afterwards without problem (after the second button was pressed again.)

Any idea, why this happens, and how to avoid it?

Was it helpful?

Solution

The fragment transaction is performed before the dialog is closed, this causes the weird behavior.

Instead of directly performing the fragment transaction, post it on the Handler. Once the dialog is closed, then the fragment transaction will be performed.

Try following code. I've applied same logic as done in one of my SO answer.

    private void page(int increment) {
        int count = gallery.getAdapter().getCount();
        int nextPos = gallery.getSelectedItemPosition() + increment;
        if (nextPos >= count) {
            nextPos = 0;
        } else if (nextPos < 0) {
            nextPos = count - 1;
        }
        final int finalNextPos = nextPos;

        Handler handler = new Handler();
        handler.post(new Runnable() {

            @Override
            public void run() {
                gallery.setSelection(finalNextPos);
            }

        });
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top