Android Create Bottom Sheet Basic

1. Project Structure
Package name : basic.bottomsheet.fyprojects.com.bottomsheetbasic

2. Gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "basic.bottomsheet.fyprojects.com.bottomsheetbasic"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v13:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'com.balysv:material-ripple:1.0.2'
}
3. Res Folder Dtructure


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bgLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/toolbar" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:scrollingCache="true" />
</LinearLayout>
<!--bottom sheet container-->
<FrameLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/bottom_sheet_behavior" />
</android.support.design.widget.CoordinatorLayout>
item_people.xml
<?xml version="1.0" encoding="utf-8"?>
<com.balysv.materialripple.MaterialRippleLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/RippleStyleBlack"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/lyt_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="?attr/actionBarSize"
android:orientation="horizontal"
android:paddingBottom="@dimen/spacing_medium"
android:paddingLeft="@dimen/spacing_large"
android:paddingRight="@dimen/spacing_large"
android:paddingTop="@dimen/spacing_medium">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="@dimen/spacing_small"
android:layout_marginRight="@dimen/spacing_small"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textColor="@color/grey_90" />
<ImageView
android:id="@+id/image"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:paddingTop="@dimen/spacing_medium"
android:paddingBottom="@dimen/spacing_medium"
android:src="@drawable/photo_female_1" />
</LinearLayout>
</com.balysv.materialripple.MaterialRippleLayout>
sheet_basic.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="wrap_content"
android:layout_gravity="bottom"
android:background="@color/grey_5"
android:orientation="vertical"
android:padding="@dimen/spacing_large">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/spacing_middle"
android:layout_marginTop="@dimen/spacing_middle"
android:text="Name"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
android:textColor="@color/grey_90" />
<TextView
android:id="@+id/address"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/spacing_medium"
android:text="Details Content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/grey_60" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_large"
android:gravity="right"
android:orientation="horizontal">
<Button
android:id="@+id/bt_close"
style="@style/Base.Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLOSE" />
<Button
android:id="@+id/bt_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DETAILS"
android:theme="@style/Button.Primary" />
</LinearLayout>
</LinearLayout>
toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:contentInsetStartWithNavigation="0dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
menu_basic.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:orderInCategory="100"
android:title="Search"
app:showAsAction="always" />
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="Settings"
app:showAsAction="never" />
</menu>
array.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer-array name="people_images">
<item>@drawable/photo_male_1</item>
<item>@drawable/photo_male_2</item>
<item>@drawable/photo_female_1</item>
<item>@drawable/photo_female_2</item>
<item>@drawable/photo_male_3</item>
<item>@drawable/photo_female_3</item>
<item>@drawable/photo_male_4</item>
<item>@drawable/photo_female_4</item>
<item>@drawable/photo_female_5</item>
<item>@drawable/photo_male_5</item>
<item>@drawable/photo_female_6</item>
<item>@drawable/photo_male_6</item>
<item>@drawable/photo_male_7</item>
<item>@drawable/photo_female_7</item>
<item>@drawable/photo_female_8</item>
</integer-array>
<string-array name="people_names">
<item>Anderson Thomas</item>
<item>Adams Green</item>
<item>Laura Michelle</item>
<item>Betty L</item>
<item>Miller Wilson</item>
<item>Garcia Lewis</item>
<item>Roberts Turner</item>
<item>Mary Jackson</item>
<item>Sarah Scott</item>
<item>Kevin John</item>
<item>Elizabeth</item>
<item>Evans Collins</item>
<item>Roberts</item>
<item>Betty C</item>
<item>Susan Lee</item>
</string-array>
</resources>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#1976D2</color>
<color name="colorPrimaryDark">#1565C0</color>
<color name="colorPrimaryLight">#1E88E5</color>
<color name="colorAccent">#FF4081</color>
<color name="colorAccentDark">#F50057</color>
<color name="colorAccentLight">#FF80AB</color>
<color name="grey_3">#f7f7f7</color>
<color name="grey_5">#f2f2f2</color>
<color name="grey_10">#e6e6e6</color>
<color name="grey_20">#cccccc</color>
<color name="grey_40">#999999</color>
<color name="grey_60">#666666</color>
<color name="grey_80">#37474F</color>
<color name="grey_90">#263238</color>
<color name="grey_95">#1a1a1a</color>
<color name="grey_100_">#0d0d0d</color>
</resources>
dimen.xml
<resources>
<!--genaral spacing-->
<dimen name="spacing_xsmall">2dp</dimen>
<dimen name="spacing_small">3dp</dimen>
<dimen name="spacing_medium">5dp</dimen>
<dimen name="spacing_xmedium">7dp</dimen>
<dimen name="spacing_middle">10dp</dimen>
<dimen name="spacing_large">15dp</dimen>
<dimen name="spacing_smlarge">18dp</dimen>
<dimen name="spacing_mlarge">20dp</dimen>
<dimen name="spacing_mxlarge">25dp</dimen>
<dimen name="spacing_xlarge">35dp</dimen>
<dimen name="spacing_xmlarge">40dp</dimen>
<dimen name="spacing_xxlarge">50dp</dimen>
<dimen name="spacing_xxxlarge">55dp</dimen>
<dimen name="appbar_padding_top">8dp</dimen>
</resources>
strings-data.xml
<resources>
<string name="middle_lorem_ipsum">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam efficitur ipsum in placerat molestie. Fusce quis mauris a enim sollicitudin</string>
</resources>
strings.xml
<resources>
<string name="app_name">BottomSheetBasic</string>
<string name="bottom_sheet_behavior" translatable="false">android.support.design.widget.BottomSheetBehavior</string>
</resources>
styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!--style for ripple library-->
<style name="RippleStyleWhite">
<item name="mrl_rippleOverlay">true</item>
<item name="mrl_rippleColor">#80FFFFFF</item>
<item name="mrl_rippleHover">true</item>
<item name="mrl_rippleAlpha">0.2</item>
</style>
<style name="RippleStyleBlack" parent="RippleStyleWhite">
<item name="mrl_rippleColor">#8096989A</item>
</style>
<style name="Button.Primary" parent="@style/Widget.AppCompat.Button.Colored">
<item name="colorButtonNormal">@color/colorPrimary</item>
<item name="android:textColor">@android:color/white</item>
</style>
</resources>
4. Adapter
AdapterPeople
package basic.bottomsheet.fyprojects.com.bottomsheetbasic.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.R;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.model.People;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.utils.Tools;
public class AdapterPeople extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<People> items = new ArrayList<>();
private Context ctx;
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public AdapterPeople(Context context, List<People> items) {
this.items = items;
ctx = context;
}
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public ImageView image;
public TextView name;
public LinearLayout lyt_parent;
public ViewHolder(View v) {
super(v);
image = (ImageView) v.findViewById(R.id.image);
name = (TextView) v.findViewById(R.id.name);
lyt_parent = (LinearLayout) v.findViewById(R.id.lyt_parent);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh;
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_people, parent, false);
vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof ViewHolder) {
ViewHolder view = (ViewHolder) holder;
final People o = items.get(position);
view.name.setText(o.name);
Tools.displayImageRound(ctx, view.image, o.image);
view.lyt_parent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(view, o, position);
}
}
});
}
}
@Override
public int getItemCount() {
return items.size();
}
public People getItem(int position) {
return items.get(position);
}
public interface OnItemClickListener {
void onItemClick(View view, People obj, int pos);
}
}
5. Data
DataGenerator
package basic.bottomsheet.fyprojects.com.bottomsheetbasic.data;
import android.content.Context;
import android.content.res.TypedArray;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.R;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.model.People;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.utils.Tools;
@SuppressWarnings("ResourceType")
public class DataGenerator {
/**
* Generate dummy data people
*
* @param ctx android context
* @return list of object
*/
public static List<People> getPeopleData(Context ctx) {
List<People> items = new ArrayList<>();
TypedArray drw_arr = ctx.getResources().obtainTypedArray(R.array.people_images);
String name_arr[] = ctx.getResources().getStringArray(R.array.people_names);
for (int i = 0; i < drw_arr.length(); i++) {
People obj = new People();
obj.image = drw_arr.getResourceId(i, -1);
obj.name = name_arr[i];
obj.email = Tools.getEmailFromName(obj.name);
obj.imageDrw = ctx.getResources().getDrawable(obj.image);
items.add(obj);
}
Collections.shuffle(items);
return items;
}
}
6. Model
People
package basic.bottomsheet.fyprojects.com.bottomsheetbasic.model;
import android.graphics.drawable.Drawable;
public class People {
public int image;
public Drawable imageDrw;
public String name;
public String email;
public boolean section = false;
public People() {
}
public People(String name, boolean section) {
this.name = name;
this.section = section;
}
}
7. Utils
Tools
package basic.bottomsheet.fyprojects.com.bottomsheetbasic.utils;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.R;
public class Tools {
public static void displayImageRound(final Context ctx, final ImageView img, @DrawableRes int drawable) {
try {
Glide.with(ctx).load(drawable).asBitmap().centerCrop().into(new BitmapImageViewTarget(img) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory.create(ctx.getResources(), resource);
circularBitmapDrawable.setCircular(true);
img.setImageDrawable(circularBitmapDrawable);
}
});
} catch (Exception e) {
}
}
public static void setSystemBarColor(Activity act) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = act.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(act.getResources().getColor(R.color.colorPrimaryDark));
}
}
public static String getEmailFromName(String name) {
if (name != null && !name.equals("")) {
String email = name.replaceAll(" ", ".").toLowerCase().concat("@mail.com");
return email;
}
return name;
}
}
8. Activity
MainActivity
package basic.bottomsheet.fyprojects.com.bottomsheetbasic;
import android.content.DialogInterface;
import android.os.Build;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialog;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.adapter.AdapterPeople;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.data.DataGenerator;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.model.People;
import basic.bottomsheet.fyprojects.com.bottomsheetbasic.utils.Tools;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private AdapterPeople adapter;
private BottomSheetBehavior mBehavior;
private BottomSheetDialog mBottomSheetDialog;
private View bottom_sheet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initComponent();
initToolbar();
showBottomSheetDialog(adapter.getItem(0));
}
private void initComponent() {
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
//set data and list adapter
adapter = new AdapterPeople(this, DataGenerator.getPeopleData(this));
recyclerView.setAdapter(adapter);
adapter.setOnItemClickListener(new AdapterPeople.OnItemClickListener() {
@Override
public void onItemClick(View view, People obj, int pos) {
showBottomSheetDialog(obj);
}
});
bottom_sheet = findViewById(R.id.bottom_sheet);
mBehavior = BottomSheetBehavior.from(bottom_sheet);
}
private void initToolbar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.drawable.ic_menu);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
actionBar.setTitle("Basic");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Tools.setSystemBarColor(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_basic, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
} else {
Toast.makeText(getApplicationContext(), item.getTitle(), Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
private void showBottomSheetDialog(final People people) {
if (mBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
final View view = getLayoutInflater().inflate(R.layout.sheet_basic, null);
((TextView) view.findViewById(R.id.name)).setText(people.name);
((TextView) view.findViewById(R.id.address)).setText(R.string.middle_lorem_ipsum);
(view.findViewById(R.id.bt_close)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mBottomSheetDialog.dismiss();
}
});
(view.findViewById(R.id.bt_details)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Details clicked", Toast.LENGTH_SHORT).show();
}
});
mBottomSheetDialog = new BottomSheetDialog(this);
mBottomSheetDialog.setContentView(view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBottomSheetDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
mBottomSheetDialog.show();
mBottomSheetDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
mBottomSheetDialog = null;
}
});
}
}