Android Layouts → Android GoogleMap-Like BottomSheetBehavior – Camposha

Android Layouts

Android Layouts

Android GoogleMap-Like BottomSheetBehavior

In this class we will look at yet another BottomSheetBehavior implementation.

We learn from and re-create an open source example of BottomSheetBehavior mimicking Google Map Behavior created by @miguelhincapie.

It makes use of a library called CustomBottomSheetBehavior and is maintained by @miguelhincapie.

Demo

Here’s the project demo.

Android CustomBottomSheetBehavior

1. Installation

We start by installing the library as it is third party. We can easily do this via android studio through the gradle build system.

So proceed over to app level build.gradle and add the following under the dependencies closure:

implementation 'com.mahc.custombottomsheetbehavior:googlemaps-like:0.9.1'

Sync your project after that.

2. Create User Interface

Well we will have three layouts.

(a). activity_main.xml

In our activity_main.xml we will have a CoordinatorLayout.
Inside it we include a FrameLayout, a placeholder that can show our dummy map.

Our ToolBar will be placed in our AppBarLayout.

Then followed by a ViewPager for our pages.

We will then have our NestedScrollView, with our BottomSheet content placed inside it.

We then have a FloatingActionButton and lastly a custombottomsheetbehavior.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android_id="@+id/coordinatorlayout"
android_layout_width="match_parent"
android_layout_height="match_parent"
android_fitsSystemWindows="true"
tools_context="com.mahc.custombottomsheet.MainActivity">
<FrameLayout
android_id="@+id/dummy_framelayout_replacing_map"
android_layout_width="match_parent"
android_layout_height="match_parent"
android_background="@android:color/darker_gray"
android_fitsSystemWindows="true"/>
<!--</FrameLayout>-->
<!--<fragment-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--android:id="@+id/support_map"-->
<!--android:name="com.google.android.gms.maps.SupportMapFragment"/>-->
<android.support.design.widget.AppBarLayout
android_id="@+id/appbarlayout"
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_theme="@style/AppTheme.AppBarOverlay"
app_layout_behavior="@string/ScrollingAppBarLayoutBehavior">
<android.support.v7.widget.Toolbar
android_id="@+id/toolbar"
android_layout_width="match_parent"
android_layout_height="?attr/actionBarSize"
app_popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android_id="@+id/pager"
android_layout_width="match_parent"
android_layout_height="@dimen/anchor_point"
android_background="@color/colorAccent"
app_layout_behavior="@string/BackDropBottomSheetBehavior"
android_fitsSystemWindows="true">
</android.support.v4.view.ViewPager>
<android.support.v4.widget.NestedScrollView
android_layout_width="match_parent"
android_layout_height="match_parent"
android_orientation="vertical"
app_behavior_peekHeight="@dimen/bottom_sheet_peek_height"
android_id="@+id/bottom_sheet"
app_layout_behavior="@string/BottomSheetBehaviorGoogleMapsLike"
app_anchorPoint="@dimen/anchor_point"
app_behavior_hideable="true"
android_fitsSystemWindows="true">
<include
layout="@layout/bottom_sheet_content"
android_layout_width="match_parent"
android_layout_height="match_parent"
android_fitsSystemWindows="true"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android_layout_height="wrap_content"
android_layout_width="wrap_content"
app_layout_anchor="@id/bottom_sheet"
app_layout_anchorGravity="top|right|end"
android_src="@drawable/ic_action_go"
android_layout_margin="@dimen/fab_margin"
app_layout_behavior="@string/ScrollAwareFABBehavior"
android_clickable="true"
android_focusable="true"/>
<com.mahc.custombottomsheetbehavior.MergedAppBarLayout
android_id="@+id/mergedappbarlayout"
android_layout_width="match_parent"
android_layout_height="wrap_content"
app_layout_behavior="@string/MergedAppBarLayoutBehavior"/>
</android.support.design.widget.CoordinatorLayout>
(b). bottom_sheet_content.xml

Next we have the bottom_sheet_content layout. At the root we have a LinearLayout.

This layout will be displaying our content which will be rendered in TextViews and ImageViews,so we add those widgets as well as ImageButton.

Here are the xml widgets we make use of in this layout.

  1. TextView.
  2. ImageView.
  3. Button.
  4. ImageButton

as well as Viewgroups like:

  1. LinearLayout.
  2. RelativeLayout.
  3. FrameLayout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="match_parent"
android_orientation="vertical"
android_paddingBottom="16dp"
android_background="@android:color/white">
<RelativeLayout
android_layout_width="match_parent"
android_layout_height="@dimen/bottom_sheet_peek_height"
android_background="@color/colorPrimary"
android_paddingTop="8dp"
android_paddingStart="16dp"
android_paddingEnd="16dp"
android_paddingBottom="8dp">
<LinearLayout
android_layout_width="wrap_content"
android_layout_height="match_parent"
android_orientation="vertical">
<TextView
android_id="@+id/bottom_sheet_title"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Title dummy"
android_textColor="@android:color/white"
android_textSize="19sp"/>
<TextView
android_id="@+id/text_dummy1"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Text dummy 1234 1234"
android_textColor="@android:color/white"
android_textSize="12sp"
android_layout_marginTop="5dp"/>
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Text dummy qwer asdf zxcv"
android_textColor="@android:color/white"
android_textSize="12sp"
android_layout_marginTop="5dp"/>
</LinearLayout>
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="5 min"
android_textColor="@android:color/white"
android_textSize="14sp"
android_layout_alignParentEnd="true"
android_layout_centerVertical="true"
android_paddingTop="8dp"/>
</RelativeLayout>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_orientation="horizontal"
android_gravity="center_horizontal"
android_paddingStart="16dp"
android_paddingEnd="16dp"
android_paddingBottom="12dp"
android_background="@drawable/border_bottom">
<RelativeLayout
android_layout_width="wrap_content"
android_layout_height="match_parent">
<ImageButton
android_id="@+id/button1"
android_layout_width="70dp"
android_layout_height="60dp"
android_src="@android:drawable/ic_dialog_map"
android_layout_centerHorizontal="true"
android_background="?attr/selectableItemBackgroundBorderless"/>
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="BUTTON 1"
android_textColor="@android:color/black"
android_layout_alignParentBottom="true"/>
</RelativeLayout>
<LinearLayout
android_id="@+id/establecimiento_layout_favoritos_button"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_orientation="vertical"
android_layout_marginEnd="20dp"
android_layout_marginStart="20dp">
<ImageButton
android_id="@+id/button2"
android_layout_width="70dp"
android_layout_height="60dp"
android_src="@android:drawable/ic_dialog_info"
android_layout_centerHorizontal="true"
android_background="?attr/selectableItemBackgroundBorderless"/>
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="BUTTON 2"
android_textColor="@android:color/black"/>
</LinearLayout>
<RelativeLayout
android_layout_width="wrap_content"
android_layout_height="match_parent">
<ImageButton
android_id="@+id/establecimiento_share_button"
android_layout_width="70dp"
android_layout_height="60dp"
android_src="@android:drawable/ic_menu_share"
android_layout_centerHorizontal="true"
android_background="?attr/selectableItemBackgroundBorderless"/>
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="BUTTON 3"
android_textColor="@android:color/black"
android_layout_alignParentBottom="true"/>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_background="@drawable/border_bottom">
<ImageView
android_id="@+id/establecimiento_icon_sucursales"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
app_srcCompat="@android:drawable/ic_secure"
android_layout_marginEnd="16dp"
android_layout_marginTop="12dp"
android_layout_marginStart="16dp"
android_layout_alignParentStart="true"
android_layout_alignParentTop="true"/>
<LinearLayout
android_layout_width="match_parent"
android_layout_height="wrap_content"
android_orientation="vertical"
android_layout_toEndOf="@id/establecimiento_icon_sucursales"
android_layout_marginStart="16dp"
android_layout_marginEnd="16dp">
<FrameLayout
android_layout_width="match_parent"
android_layout_height="48dp">
<Button
android_id="@+id/establecimiento_sucursal_row_button"
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Text dummy 1"
android_layout_gravity="center_vertical"
android_background="?attr/selectableItemBackgroundBorderless"
android_textAllCaps="false"
android_textColor="@android:color/black"/>
</FrameLayout>
<FrameLayout
android_layout_width="match_parent"
android_layout_height="48dp">
<Button
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Text dummy 2"
android_layout_gravity="center_vertical"
android_background="?attr/selectableItemBackgroundBorderless"
android_textAllCaps="false"
android_textColor="@android:color/black"/>
</FrameLayout>
<FrameLayout
android_layout_width="match_parent"
android_layout_height="48dp">
<Button
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="Text dummy 3"
android_layout_gravity="center_vertical"
android_background="?attr/selectableItemBackgroundBorderless"
android_textAllCaps="false"
android_textColor="@android:color/black"/>
</FrameLayout>
</LinearLayout>
</RelativeLayout>
<FrameLayout
android_layout_width="match_parent"
android_layout_height="56dp"
android_layout_marginStart="16dp"
android_layout_marginEnd="16dp">
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_text="APACHE LICENSE"
android_layout_gravity="center_vertical"
android_textSize="18sp"
android_textColor="@android:color/darker_gray"/>
</FrameLayout>
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_layout_margin="16dp"
android_text="@string/dummy_text"
android_textSize="12sp"
android_textColor="@android:color/black"/>
<FrameLayout
android_layout_width="match_parent"
android_layout_height="620dp"
android_background="@color/colorAccent"
android_layout_marginTop="40dp">
<TextView
android_layout_width="wrap_content"
android_layout_height="wrap_content"
android_layout_gravity="center"
android_text="Your remaining content here"
android_textColor="@android:color/white" />
</FrameLayout>
</LinearLayout>
(c). pager_item.xml

Then the pager_item.xml.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
android_orientation="vertical" android_layout_width="match_parent"
android_layout_height="match_parent">
<ImageView
android_layout_width="match_parent"
android_layout_height="match_parent"
android_id="@+id/imageView"
android_scaleType="centerCrop"/>
</LinearLayout>

Our Java Code

(a). ItemPagerAdapter.java

First create a new class. Call it a ItemPagerAdapter and let’s make it derive from android.support.v4.view.PagerAdapter.

public class ItemPagerAdapter extends android.support.v4.view.PagerAdapter {..}

PagerAdapter acts as the base class for providing the adapter to populate pages inside of that ViewPager.

Add three instance fields in our class:

    Context mContext;
LayoutInflater mLayoutInflater;
final int[] mItems;

First the Context is an interface to global information about an application environment. Normally Context allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

In this case it will allow us inflate our layout into a view.

Then we have a LayoutInflater, a class that allows us instantiates a layout XML file into its corresponding View objects.

The int array will hold us image ids.

Then our constructor is responsible for first receiving a context object which will be required during inflation of our pager_item layout and secondly the int items which will hold our image ids.

We will also perform the inflation inside that constructor.

    public ItemPagerAdapter(Context context, int[] items) {
this.mContext = context;
this.mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.mItems = items;
}

We then override the other methods.

Here’s the full code for this class:

package com.mahc.custombottomsheet;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.mahc.custombottomsheet.R;
public class ItemPagerAdapter extends android.support.v4.view.PagerAdapter {
Context mContext;
LayoutInflater mLayoutInflater;
final int[] mItems;
public ItemPagerAdapter(Context context, int[] items) {
this.mContext = context;
this.mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.mItems = items;
}
@Override
public int getCount() {
return mItems.length;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.pager_item, container, false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView);
imageView.setImageResource(mItems[position]);
container.addView(itemView);
return itemView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
(b). MainActivity.java

Finally here’s our main activity. Maybe it’s been generate to you by android studio.

So go ahead and as instance fields add an int array to hold our Drawables.

Also we’ll have a TextView:

    int[] mDrawables = {
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3
};
TextView bottomSheetTextView;

We start by referencing our widgets CoordinatorLayout as well as View to represent the bottom sheet.

        CoordinatorLayout coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorlayout);
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);

Then we reference the BottomSheetBehaviorGoogleMapsLike. This is the library we are using to create a bottom sheet behavior like that in Google maps.

        final BottomSheetBehaviorGoogleMapsLike behavior = BottomSheetBehaviorGoogleMapsLike.from(bottomSheet);

Then listen to various callbacks:

        behavior.addBottomSheetCallback(new BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});

We are interested especially in the state change event where we get various state of our BottomSheetBehaviorGoogleMapsLike.

Here’s the code:

package com.mahc.custombottomsheet;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike;
import com.mahc.custombottomsheetbehavior.MergedAppBarLayout;
import com.mahc.custombottomsheetbehavior.MergedAppBarLayoutBehavior;
public class MainActivity extends AppCompatActivity {
int[] mDrawables = {
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3,
R.drawable.cheese_3
};
TextView bottomSheetTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle(" ");
}
/**
* If we want to listen for states callback
*/
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinatorlayout);
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
final BottomSheetBehaviorGoogleMapsLike behavior = BottomSheetBehaviorGoogleMapsLike.from(bottomSheet);
behavior.addBottomSheetCallback(new BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED:
Log.d("bottomsheet-", "STATE_COLLAPSED");
break;
case BottomSheetBehaviorGoogleMapsLike.STATE_DRAGGING:
Log.d("bottomsheet-", "STATE_DRAGGING");
break;
case BottomSheetBehaviorGoogleMapsLike.STATE_EXPANDED:
Log.d("bottomsheet-", "STATE_EXPANDED");
break;
case BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT:
Log.d("bottomsheet-", "STATE_ANCHOR_POINT");
break;
case BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN:
Log.d("bottomsheet-", "STATE_HIDDEN");
break;
default:
Log.d("bottomsheet-", "STATE_SETTLING");
break;
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
MergedAppBarLayout mergedAppBarLayout = findViewById(R.id.mergedappbarlayout);
MergedAppBarLayoutBehavior mergedAppBarLayoutBehavior = MergedAppBarLayoutBehavior.from(mergedAppBarLayout);
mergedAppBarLayoutBehavior.setToolbarTitle("Title Dummy");
mergedAppBarLayoutBehavior.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
behavior.setState(BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT);
}
});
bottomSheetTextView = (TextView) bottomSheet.findViewById(R.id.bottom_sheet_title);
ItemPagerAdapter adapter = new ItemPagerAdapter(this,mDrawables);
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(adapter);
behavior.setState(BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT);
//behavior.setCollapsible(false);
}
}
(d). How to Run.

The download contains both a library as well as code. Just go to the app folder and you find the code for the sample.

Go over to your app level build.gradle and add statement via the implementation statement just as we did in the Installation section.

No. Resource Action
1. GitHub Download
2. GitHub Browse
3. Project Thanks to @miguelhincapie

Leave a Reply

Your email address will not be published. Required fields are marked *

Price

Free

Rating

Not enough ratings to display
X