I believe you’re looking for Androidslidinguppanel, is a library providing a Widget
with the functionality you are looking for.
Integration with the Google Maps is simple, in my example I made the access to Google Places API for Android. Where I searched some places and created Markers
, synchronized with the SlidingUpPanelLayout
.
My layout got:
<?xml version="1.0" encoding="utf-8"?>
<com.sothree.slidinguppanel.SlidingUpPanelLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:sothree="http://schemas.android.com/apk/res-auto"
android:id="@+id/sliding_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
sothree:umanoPanelHeight="72dp"
sothree:umanoShadowHeight="4dp"
sothree:umanoAnchorPoint="0.5"
tools:context=".MapsActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment" />
<include layout="@layout/sliding_panel" />
</com.sothree.slidinguppanel.SlidingUpPanelLayout>
My Activity became:
public class MapsActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, ViewPager.OnPageChangeListener,
PlacesListAdapter.ListCallback, GoogleMap.OnMarkerClickListener {
private GoogleMap mMap; // Might be null if Google Play services APK is not available.
private GoogleApiClient mGoogleApiClient;
private RecyclerView mRecyclerView;
private ViewPager mPager;
private PlacesListAdapter mAdapter;
private PlacesPagerAdater mPagerAdapter;
private PlacesInfoWindowAdapter mInfoWindowAdapter;
private List<PlaceLikelihood> mPlaces;
private List<Marker> mMarkers;
private GoogleMap.OnMyLocationChangeListener mListener = new GoogleMap.OnMyLocationChangeListener(){
@Override
public void onMyLocationChange(Location location) {
moveToLocationOneShot(location);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
setUpMapIfNeeded();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
configureViewPager();
configureRecyclerView();
queryForNearbyPlaces();
}
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
mGoogleApiClient.connect();
}
@Override
protected void onPause() {
super.onPause();
mGoogleApiClient.disconnect();
}
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
setUpMap();
}
}
}
private void configureViewPager() {
mPager = (ViewPager) findViewById(R.id.view_pager);
mPager.setAdapter(mPagerAdapter = new PlacesPagerAdater(this));
mPager.setOnPageChangeListener(this);
}
private void configureRecyclerView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter = new PlacesListAdapter(this).setListCallback(this));
mRecyclerView.setHasFixedSize(true);
}
private void queryForNearbyPlaces() {
PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi.getCurrentPlace(mGoogleApiClient, null);
result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
@Override
public void onResult(PlaceLikelihoodBuffer likelyPlaces) {
mPlaces = new ArrayList<>(likelyPlaces.getCount());
mMarkers = new ArrayList<>(likelyPlaces.getCount());
mInfoWindowAdapter = new PlacesInfoWindowAdapter(MapsActivity.this);
int i = 0;
for (PlaceLikelihood placeLikelihood : likelyPlaces) {
mPlaces.add(placeLikelihood.freeze());
mMarkers.add(mMap.addMarker(buildMarkerForPlace(placeLikelihood.getPlace(), i++)));
}
mAdapter.setData(mPlaces);
mPagerAdapter.setData(mPlaces);
mInfoWindowAdapter.setData(mPlaces);
likelyPlaces.release();
mMap.setInfoWindowAdapter(mInfoWindowAdapter);
}
});
}
MarkerOptions buildMarkerForPlace(Place place, int position) {
MarkerOptions mo = new MarkerOptions();
mo.position(place.getLatLng());
mo.title(place.getName().toString());
mo.snippet(Integer.toString(position));
return mo;
}
private void setUpMap() {
mMap.setMyLocationEnabled(true);
mMap.setBuildingsEnabled(true);
mMap.setOnMyLocationChangeListener(mListener);
mMap.setInfoWindowAdapter(mInfoWindowAdapter);
mMap.setOnMarkerClickListener(this);
}
private void moveToLocationOneShot(Location location) {
moveToLocation(location);
mMap.setOnMyLocationChangeListener(null);
}
private void moveToLocation(Location location) {
moveToLocation(getLatLng(location));
}
private void moveToLocation(LatLng latLng) {
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(latLng, 16f, 0f, 0f)));
}
private LatLng getLatLng(Location loc) {
return new LatLng(loc.getLatitude(), loc.getLongitude());
}
@Override
public void onConnected(Bundle bundle) {
Log.d("TAG", "onConnected");
}
@Override
public void onConnectionSuspended(int i) {
Log.d("TAG", "onConnectionSuspended");
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("TAG", "onConnectionFailed");
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
@Override
public void onPageSelected(int position) {
mRecyclerView.smoothScrollToPosition(position);
showMarker(mMarkers.get(position));
}
void showMarker(Marker marker) {
moveToLocation(marker.getPosition());
marker.showInfoWindow();
}
@Override
public void onPageScrollStateChanged(int state) {}
@Override
public void onItemSelected(View item) {
int position = mRecyclerView.getChildLayoutPosition(item);
mPager.setCurrentItem(position, true);
showMarker(mMarkers.get(position));
}
@Override
public boolean onMarkerClick(Marker marker) {
int position = mMarkers.indexOf(marker);
mPager.setCurrentItem(position, true);
mRecyclerView.smoothScrollToPosition(position);
return false;
}
}
And not forgetting the dependencies:
repositories {
mavenCentral()
}
compile 'com.android.support:support-v4:22.0.0'
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.google.android.gms:play-services-maps:7.0.0'
compile 'com.google.android.gms:play-services-location:7.0.0'
compile 'com.android.support:recyclerview-v7:22.0.0'
compile 'com.sothree.slidinguppanel:library:3.0.0'
Of course some details are still missing, but you have a good code to get you started.
The other artifacts, like Adapters
which were used, are in the code repository.
In guidelines of Material Design has nothing so specific, but it gives some pretty generic navigation patterns. For the standard of "Sliding Panel", Similar to Google Maps, I recommend looking at this answer that provides an alternative to implementing it: http://answall.com/a/28086/6436.
– Wakim
This was exactly the desired effect, @Wakim, thank you! Just an Obs: in the post mentioned, you provide a gist that is no longer accessible. You could send me the code that implements together with Maps to get an idea of how to do?
– Atoyansk
Ah, I’ll take a look at why it’s not available, anything I take the code and put there again.
– Wakim
I really appreciate if you make this code available. It will help me a lot!
– Atoyansk
I really didn’t find the repository with the code. I can mount a repository on github with an example and I’ll let you know later.
– Wakim