3

I am using Umano/SlidingUpPanel lib in my App where Map is a main content . And a panel is sliding up on map. I am putting overlay attribute false as i want the main content to move upward when panel slide up (Like a WhatsApp chat Window) .

Issue i am facing is : My main content i.e. Map is shaking up or may be re-sizing everytime panel slide up and down .

Please see the link for more understanding of an issue :

https://www.dropbox.com/s/imjlso12xarzf57/Video%2001-12-15%2C%2012%2049%2058%20PM.mov?dl=0

Map is shaking when panel slide up and down.

activity_main :

<com.sothree.slidinguppanel.SlidingUpPanelLayout
    xmlns:sothree="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="bottom"
    sothree:umanoFadeColor="@android:color/transparent"
    sothree:umanoOverlay = "false"
    sothree:umanoDragView="@+id/dragView">


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

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

</FrameLayout>
    <include layout="@layout/sliding_fragment_layout"
        android:background="#ffffff"
        android:id="@+id/dragView"/>
    <!--<FrameLayout-->
            <!--android:id="@+id/list_fragment_container"-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="match_parent"/>-->

</com.sothree.slidinguppanel.SlidingUpPanelLayout>   

Sliding_fragment_layout:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout
    android:id="@+id/verified"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="60dp"
    android:paddingLeft="10dp"
    android:background="#FFFF"
    android:paddingRight="10dp"
    android:paddingTop="10dp"
    xmlns:android="http://schemas.android.com/apk/res/android">
                <!--android:descendantFocusability="beforeDescendants"-->
                <!--android:focusableInTouchMode="true"-->

                <TableRow
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="1dp"
                    android:weightSum="1">

                        <TextView
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:id="@+id/employer_textView_id"
                            android:layout_weight=".4"
                            android:padding="5dp"
                            android:textSize="15sp"
                            android:textColor="@android:color/black"/>




                </TableRow>

                <TableRow
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="0.5dp"
                    android:weightSum="1">

                        <TextView
                            android:layout_width="100dp"
                            android:layout_height="wrap_content"
                            android:padding="2dp"
                            android:textSize="15sp"
                            android:layout_weight="1"
                            android:gravity="fill_horizontal"
                            android:id="@+id/address_textView_id"
                            android:textColor="@android:color/black"/>

                </TableRow>
                <View
                    android:layout_width="fill_parent"
                    android:layout_height="1dp"
                    android:background="#c0c0c0"/>

               <TextView
                   android:layout_width="match_parent"
                   android:layout_height="match_parent"
                   android:text="Hello"
                   android:id="@+id/text"/>
        </TableLayout>

MainActivity.java

public class MainActivity extends FragmentActivity implements LocationListener {

    private static final String LIST_FRAGMENT_TAG = "list_fragment";
    private static final String TAG = "DemoActivity";

    GoogleMap googleMap;
    double currentLat , currentLon;
    LocationManager locationManager;
    Location mlocation;
    double latitude;
    double longitude;
    LatLng latLng,points;
    Boolean flag = false;
    String mLastUpdateTime;
    CameraPosition INIT;
    private SlidingUpPanelLayout mLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (!isGooglePlayServiceAvailable()) {
            finish();
        }
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);


        googleMap = mapFragment.getMap();
        googleMap.setMyLocationEnabled(true);
        googleMap.getUiSettings().setCompassEnabled(false);

        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);


        Criteria criteria = new Criteria();
        String bestProvider = locationManager.getBestProvider(criteria, true);
        mlocation = locationManager.getLastKnownLocation(bestProvider);
        if (mlocation != null) {
            onLocationChanged(mlocation);
        }
        locationManager.requestLocationUpdates(bestProvider, 20000, 0, this);

        /*SlidingPanelUpLayout*/
        mLayout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
//        mLayout.setAnchorPoint(0.3f);
        mLayout.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
        mLayout.setPanelHeight(500);
//        mLayout.setParallaxOffset(10);
        mLayout.setPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() {
            @Override
            public void onPanelSlide(View panel, float slideOffset) {
                Log.i(TAG, "onPanelSlide, offset " + slideOffset);
                googleMap.getUiSettings().setAllGesturesEnabled(true);
            }

            @Override
            public void onPanelExpanded(View panel) {
                Log.i(TAG, "onPanelExpanded");
                googleMap.getUiSettings().setAllGesturesEnabled(false);

            }

            @Override
            public void onPanelCollapsed(View panel) {
                flag=true;
                Log.i(TAG, "onPanelCollapsed");
                googleMap.getUiSettings().setAllGesturesEnabled(false);

            }

            @Override
            public void onPanelAnchored(View panel) {
                Log.i(TAG, "onPanelAnchored");

            }

            @Override
            public void onPanelHidden(View panel) {
                flag=false;
                Log.i(TAG, "onPanelHidden");
            }
        });
    }

    public Boolean isGooglePlayServiceAvailable() {
        int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (ConnectionResult.SUCCESS == status) {
            return true;
        } else {
            GooglePlayServicesUtil.getErrorDialog(status, this, 0).show();
            return false;
        }

    }


    @Override
    public void onLocationChanged(Location location) {
        mlocation = location;
        latitude = location.getLatitude();
        longitude = location.getLongitude();
        latLng = new LatLng(latitude, longitude);
        long atTime = mlocation.getTime();
        mLastUpdateTime = DateFormat.getTimeInstance().format(new Date(atTime));

//        mapView.setCenterCoordinate(latLng1);

        MarkerOptions options = new MarkerOptions();
        options.position(latLng).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
//        Marker marker = googleMap.addMarker(options);
        googleMap.addMarker(options.title(getAdress()));
        googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
//        View view = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.custom_marker_layout, null);

        INIT =
                new CameraPosition.Builder()
                        .target(latLng)
                        .zoom(17.5F)
                        .bearing(300F) // orientation
                        .tilt(50F) // viewing angle
                        .build();
        googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(INIT));
        googleMap.getUiSettings().setCompassEnabled(false);
//
//        googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 13));
        int Radius = 100;
        googleMap.addMarker(new MarkerOptions()
                .position(latLng)
//                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
                .title(getAdress())
                .snippet("Radius: " + Radius))
                .showInfoWindow();

        googleMap.addMarker(new MarkerOptions().position(new LatLng(28.7494720, 77.0565330)));

        googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {

            @Override
            public boolean onMarkerClick(Marker marker) {

//                flag = true;
                Log.i("Marker","Click");
                points = new LatLng(marker.getPosition().latitude,marker.getPosition().longitude);
                if(flag==false){
                if(mLayout!=null){

                        Log.i("Marker", "collapsed");
                    mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
                    animateLatLngZoom(points, 0, -10, 10);

                }}else{
                    Log.i("Marker","Hidden");                        mLayout.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
                }


                return true;
            }

        });


//        //Instantiates a new CircleOptions object +  center/radius 28.6328° N, 77.2197°
//        CircleOptions circleOptions = new CircleOptions()
//                .center(new LatLng(latitude, longitude))
//                .radius(Radius)
//                .fillColor(0x40ff0000)
//                .strokeColor(Color.GREEN)
//                .strokeWidth(2);
//
//// Get back the mutable Circle
//        Circle circle = googleMap.addCircle(circleOptions);
//        Circle circle1 = googleMap.addCircle(new CircleOptions().center(new LatLng(28.633908, 77.221094))
//                .radius(100)
//                .fillColor(0x40ff0000)
//                .strokeColor(Color.BLUE)
//                .strokeWidth(5));
//// more operations on the circle...
//        // =
//
//
//        googleMap.animateCamera(CameraUpdateFactory.zoomTo(15));

    }

    private void animateLatLngZoom(LatLng latlng, int reqZoom, int offsetX, int offsetY) {
        /*calculate the offset's center in the required zoom from original's offset, and then animate the map's camera.
        For this, first move the map's camera to the desired zoom, calculate the offset for that zoom level, and then restore
        the original zoom.After calculating the new center we can make the animation with CameraUpdateFactory.newLatLngZoom.*/



        // Save current zoom
//      float originalZoom = googleMap.getCameraPosition().zoom;

        // Move temporarily camera zoom
//      googleMap.moveCamera(CameraUpdateFactory.zoomTo(reqZoom));

        Point pointInScreen = googleMap.getProjection().toScreenLocation(latlng);

        Point newPoint = new Point();
        newPoint.x = pointInScreen.x + offsetX;
        newPoint.y = pointInScreen.y + offsetY;

        LatLng newCenterLatLng = googleMap.getProjection().fromScreenLocation(newPoint);

        // Restore original zoom
//      googleMap.moveCamera(CameraUpdateFactory.zoomTo(originalZoom));

        // Animate a camera with new latlng center and required zoom.
//      googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(newCenterLatLng, reqZoom));
        googleMap.animateCamera(CameraUpdateFactory.newLatLng(newCenterLatLng));

    }

    public String getAdress() {

        Geocoder geocoder;
        String result = null;
        List<Address> addresses;
        geocoder = new Geocoder(this, Locale.getDefault());
        try {
            addresses = geocoder.getFromLocation(latitude, longitude, 1);

            if (addresses != null && addresses.size() > 0) {
                Address address = addresses.get(0);
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < address.getMaxAddressLineIndex(); i++) {
                    sb.append(address.getAddressLine(i)).append("\n");
                }
//                sb.append(address.getLocality()).append("\n");
//                sb.append(address.getPostalCode()).append("\n");
                sb.append(address.getCountryName().toUpperCase());
                result = sb.toString();
            }
        } catch (IOException e) {
            Log.e("Loaction Address", "Unable connect to Geocoder", e);
        }
        return result;

    }


    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_show_list) {
//            toggleList();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

Please help me in resolving it . Thank you

Lingviston
  • 5,479
  • 5
  • 35
  • 67
young_08
  • 1,196
  • 2
  • 13
  • 35

4 Answers4

3

I'm not sure that this issue can be solved directly. However I can imagine two possible workarounds:

  1. Try to display map in lite mode. Also you can try to have 2 maps: the first one working in default mode and the second one working in lite mode then switch among them when you need to display sligin up panel.
  2. You can attach PanelSlideListener to the SligingUpPanel, set overlay attribute to true and in PanelSlideListener.onPanelSlide apply changes to the Camera object of the map using moveCamera/animateCamera methods combined with setPadding method (to also shift map controls). This will definitely solve the problem cause map view won't get resized instead you'll manually control the Camera object. However this approach requires more work to be done.

UPDATE Here's an example of what I suggest. Hope I understood your problem the right way. You just need to create an empty project, add there provided Activity code, it's layout and necessary dependencies + create appropriate AndroidManifest with necessary permissions.

Lingviston
  • 5,479
  • 5
  • 35
  • 67
  • So, the problem with overlay attribute set to true is : the menu comes up on Map . I want it to move main content upward without shaking or re-sizing map . – young_08 Dec 07 '15 at 10:12
  • Read my second point: it will come up on map but you can manually shift Map controls/Map camera using setPadding/moveCamera/animateCamera methods combination. Map view is based on SurfaceView/TextureView which are sensitive to resizing. So my opinion is that you can't use overlay=false but can workaround your issue. – Lingviston Dec 07 '15 at 10:30
  • I am not getting , the way to do this . can you please explain me with some code or adding lines to above code according to your way . Please . – young_08 Dec 07 '15 at 10:53
  • I solved similar problem in the past. http://stackoverflow.com/questions/20395345/map-behavior-like-in-google-maps-application – Lingviston Dec 07 '15 at 13:40
  • No , my problem is not that . – young_08 Dec 08 '15 at 06:26
  • Thanx alot for the update . really greatful . i will apply the update or code u suggest in an example and see if it work . if not then will get back to you . – young_08 Dec 08 '15 at 12:30
  • @young_08 FYI demo can be easily changed to expand SlidingUpPanel only by marker click. Draggable bottom panel added just to show correct map movement behavior. – Lingviston Dec 08 '15 at 15:03
  • i have added a query oon github . Did u recieve? – young_08 Dec 09 '15 at 07:21
  • or should i ask here ? Thank you – young_08 Dec 09 '15 at 07:21
  • @young_08 answered you on github. Don't forget to accept the answer if it helps. Also feel free to modify the code. I think it's quite simple and actually it's rather flexible. – Lingviston Dec 09 '15 at 08:36
  • @young_08 Also it's better to ask your questions here so SO users can see the full dialog. At least ask your questions using SO PM. Finally: I don't receive any notifications about comments on gist. – Lingviston Dec 09 '15 at 08:39
  • i have changed my code according to yours but the issue i am getting is same as yours http://stackoverflow.com/questions/20395345/map-behavior-like-in-google-maps-application . – young_08 Dec 09 '15 at 08:50
  • now if i click marker to open panel in kind a zoom state , then technically marker should move upward to latlon position automatically but its not . Marker gets hide instead . – young_08 Dec 09 '15 at 08:51
  • @young_08 hmm, actually in my demo it moves up as expected. Did you check it? – Lingviston Dec 09 '15 at 08:52
  • @young_08 then actually I answered your question;) But you can update it with recent code so I could take a look. – Lingviston Dec 09 '15 at 08:57
  • can you please come on github ? – young_08 Dec 09 '15 at 09:09
  • i have pasted the updated code there . and soon be uploading video showing issue i am facing – young_08 Dec 09 '15 at 09:12
  • the problem i am facing after an changes : https://www.dropbox.com/s/p6l1dlvx4780552/Video%2009-12-15%2C%202%2022%2025%20PM.mov?dl=0 – young_08 Dec 09 '15 at 09:13
  • Thanks alot . You saved me . – young_08 Dec 09 '15 at 09:35
  • I need your little help in an issue i am facing reagarding above context . can i ? – young_08 Dec 10 '15 at 11:57
0

You can use android-transition library to apply Y translation to the map View:

Gradle:

compile 'com.github.kaichunlin.transition:core:0.9.4'
compile 'com.github.kaichunlin.transition:slidinguppanel:0.9.1'

Code:

SlidingUpPanelLayoutAdapter adapter=new SlidingUpPanelLayoutAdapter();     
mLayout.setPanelSlideListener(adapter);         
adapter.addTransition(ViewTransitionBuilder.transit(findViewById(R.id.map)).interpolator(new LinearInterpolator()).translationY(-250));

Note in order for this to work, you have to change the panel state from HIDDEN to COLLAPSED, and set panel height as 0:

mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
mLayout.setPanelHeight(0);

Set the panel state to EXPANDED instead of COLLAPSED:

mLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);

And also set TableLayout's layout_height to 500px in sliding_fragment_layout.xml, (which should be changed to use dp, but using px is suffice for demo purpose)

Effect demonstration: https://www.dropbox.com/s/gcozdrol8ed8zcc/test.mp4?dl=0

Kai
  • 15,284
  • 6
  • 51
  • 82
  • Hey Kai , i want exactly what was you shown in video . But after the changes u suggested , still i unable to get the thing . – young_08 Dec 08 '15 at 07:03
  • Let me share the changes i did to my file – young_08 Dec 08 '15 at 08:49
  • Link of Main Activity file after changes acc to your suggestion : https://www.dropbox.com/s/lvqha217l2qz0qz/MainActivity.java?dl=0 – young_08 Dec 08 '15 at 09:19
  • You haven't modify the panel state to COLLAPSED and other changes required, in any case you here's a working version: https://www.dropbox.com/s/vz3e9mjm5d6xos6/MyApplication.rar?dl=0 – Kai Dec 08 '15 at 14:07
0

I think the best solution is using parallax effect.

You should set umanoOverlay="true" to be sure that map keeps its size. Then, give an anchor point (ex: umanoAnchorPoint="0.60") and parallax effect (ex: umanoParalaxOffset="250dp") the sliding up panel. When clicking on a marker, animate camera to this marker to be sure it is in the middle of the map. You should also give umanoPanelHeight="0dp" because you want to show panel by just clicking marker if I get it true.

So it will something like

    sothree:umanoAnchorPoint="0.60"
    sothree:umanoDragView="@+id/dragView"
    sothree:umanoParalaxOffset="250dp"
    sothree:umanoPanelHeight="0dp"
    sothree:umanoShadowHeight="0dp"
    sothree:umanoFadeColor="@android:color/transparent"
    sothree:umanoOverlay="true"

in your marker click listener

@Override
public boolean onMarkerClick(Marker marker) {

    points = new LatLng(marker.getPosition().latitude,marker.getPosition().longitude);

    // if panel is collapsed
    if (mLayout.getPanelState().equals(
            SlidingUpPanelLayout.PanelState.COLLAPSED)) {
            // then show the panel content
            mLayout.setPanelState(SlidingUpPanelLayout.PanelState.ANCHORED);
            // animate your camera somehow
            animateLatLngZoom(points, 0, -10, 10);
        } else { // if panel is anchored or expanded
            // collapse it
            mLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
        }

    return true;
}
sembozdemir
  • 4,137
  • 3
  • 21
  • 30
  • i did the changes , but it didn't work for me . It's overlaying the sliding panel on map instead sliding up panel upward the map . Like i have shown in video . – young_08 Dec 08 '15 at 17:44
0

I suppose that it will be really problematic to get rid of this unwanted effect with sothree:umanoOverlay="false" as having that option set to false requires Android to layout your map every time height of your table is changed.

So, possible solution for you is to make your sliding panel to be overlay and track it's movements up and down, adjusting your map translationY accordingly.

Here is a brief example:

@Override
public void onPanelSlide(View panel, float slidingOffset) {
    adjustViews(slidingOffset, false);
}

@Override
public void onPanelCollapsed(View panel) {
}

@Override
public void onPanelExpanded(View panel) {
}

@Override
public void onPanelAnchored(View panel) {
    adjustViews(mSlidingPanelLayout.getAnchorPoint(), true);
}

@Override
public void onPanelHidden(View view) {
}

private void adjustViews(final float slideOffset, boolean anchor) {

    float height = mSlidingPanelLayout.getHeight();
    float panelHeight = mSlidingPanelLayout.getPanelHeight();   

    mMapFragment.setMapTranslationY(-(int) ((height - panelHeight) * slideOffset) - mSlidingPanelLayout.getCurrentParalaxOffset());
}

You will need to do some other things I suppose: implement setMapTranslationY and saving/restoring state during rotation.

GregoryK
  • 3,011
  • 1
  • 27
  • 26