Android Create Chips Like Gmail

1. Project Structure

2. Gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "chips.androidtutorials.fyprojects.basic"
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.mikhaellopez:circularimageview:3.2.0'
implementation 'com.github.pchmn:MaterialChipsInput:1.0.5' // material chip
implementation 'com.balysv:material-ripple:1.0.2' // ripple effect
implementation 'com.github.bumptech.glide:glide:3.7.0' // image loader
}
Project
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
3. Activity
MainActivity
package chips.androidtutorials.fyprojects.basic.activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.PorterDuff;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
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.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.pchmn.materialchips.ChipsInput;
import com.pchmn.materialchips.model.ChipInterface;
import java.util.ArrayList;
import java.util.List;
import chips.androidtutorials.fyprojects.basic.adapter.AdapterContacts;
import chips.androidtutorials.fyprojects.basic.data.DataGenerator;
import chips.androidtutorials.fyprojects.basic.model.People;
import chips.androidtutorials.fyprojects.basic.model.PeopleChip;
import chips.androidtutorials.fyprojects.basic.utils.Tools;
public class MainActivity extends AppCompatActivity {
private ChipsInput mChipsInput;
private List<PeopleChip> items = new ArrayList<>();
private List<ChipInterface> items_added = new ArrayList<>();
private List<People> items_people = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initToolbar();
iniComponent();
}
private void initToolbar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.drawable.ic_menu);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Basic");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Tools.setSystemBarColor(this);
}
private void iniComponent() {
items_people = DataGenerator.getPeopleData(this);
((ImageButton) findViewById(R.id.contacts)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialogContacts();
}
});
mChipsInput = (ChipsInput) findViewById(R.id.chips_input);
getPeopleChipList();
// chips listener
mChipsInput.addChipsListener(new ChipsInput.ChipsListener() {
@Override
public void onChipAdded(ChipInterface chip, int newSize) {
items_added.add(chip);
}
@Override
public void onChipRemoved(ChipInterface chip, int newSize) {
items_added.remove(chip);
}
@Override
public void onTextChanged(CharSequence text) {
//Log.e(TAG, "text changed: " + text.toString());
}
});
}
private void getPeopleChipList() {
Integer id = 0;
for (People p : items_people) {
PeopleChip contactChip = new PeopleChip(id.toString(), p.imageDrw, p.name, p.email);
// add contact to the list
items.add(contactChip);
id++;
}
// pass contact list to chips input
mChipsInput.setFilterableList(items);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_chips, 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 dialogContacts() {
final Dialog dialog = new Dialog(this);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // before
dialog.setContentView(R.layout.dialog_contacts);
dialog.setCancelable(true);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
RecyclerView recyclerView = (RecyclerView) dialog.findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
AdapterContacts _adapter = new AdapterContacts(this, items_people);
recyclerView.setAdapter(_adapter);
_adapter.setOnItemClickListener(new AdapterContacts.OnItemClickListener() {
@Override
public void onItemClick(View view, People obj, int position) {
mChipsInput.addChip(obj.imageDrw, obj.name, obj.email);
dialog.hide();
}
});
dialog.show();
dialog.getWindow().setAttributes(lp);
}
}
4. Adapter
AdapterContacts
package chips.androidtutorials.fyprojects.basic.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.TextView;
import java.util.ArrayList;
import java.util.List;
import chips.androidtutorials.fyprojects.basic.activity.R;
import chips.androidtutorials.fyprojects.basic.model.People;
import chips.androidtutorials.fyprojects.basic.utils.Tools;
public class AdapterContacts extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<People> items = new ArrayList<>();
private OnLoadMoreListener onLoadMoreListener;
private Context ctx;
private OnItemClickListener mOnItemClickListener;
public interface OnItemClickListener {
void onItemClick(View view, People obj, int position);
}
public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
this.mOnItemClickListener = mItemClickListener;
}
public AdapterContacts(Context context, List<People> items) {
this.items = items;
ctx = context;
}
public class OriginalViewHolder extends RecyclerView.ViewHolder {
public ImageView image;
public TextView name;
public TextView email;
public View lyt_parent;
public OriginalViewHolder(View v) {
super(v);
image = (ImageView) v.findViewById(R.id.image);
name = (TextView) v.findViewById(R.id.name);
email = (TextView) v.findViewById(R.id.email);
lyt_parent = (View) 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_contacts, parent, false);
vh = new OriginalViewHolder(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 OriginalViewHolder) {
OriginalViewHolder view = (OriginalViewHolder) holder;
People p = items.get(position);
view.name.setText(p.name);
view.email.setText(p.email);
Tools.displayImageRound(ctx, view.image, p.image);
view.lyt_parent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(view, items.get(position), position);
}
}
});
}
}
@Override
public int getItemCount() {
return items.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public interface OnLoadMoreListener {
void onLoadMore(int current_page);
}
}
5. Data
data
package chips.androidtutorials.fyprojects.basic.data;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.content.res.AppCompatResources;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import chips.androidtutorials.fyprojects.basic.activity.R;
import chips.androidtutorials.fyprojects.basic.model.People;
import chips.androidtutorials.fyprojects.basic.utils.Tools;
@SuppressWarnings("ResourceType")
public class DataGenerator {
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 chips.androidtutorials.fyprojects.basic.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;
}
}
PeopleChip
package chips.androidtutorials.fyprojects.basic.model;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import com.pchmn.materialchips.model.ChipInterface;
public class PeopleChip implements ChipInterface {
private String id;
private Drawable image;
private String name;
private String info;
public PeopleChip(String id, Drawable image, String name, String info) {
this.id = id;
this.image = image;
this.name = name;
this.info = info;
}
@Override
public Object getId() {
return id;
}
@Override
public Uri getAvatarUri() {
return null;
}
@Override
public Drawable getAvatarDrawable() {
return image;
}
@Override
public String getLabel() {
return name;
}
@Override
public String getInfo() {
return info;
}
}
7. Utils
Tools
package chips.androidtutorials.fyprojects.basic.utils;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.view.View;
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 chips.androidtutorials.fyprojects.basic.activity.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. Layout
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:fitsSystemWindows="true"
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>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/spacing_smlarge"
android:layout_marginRight="@dimen/spacing_smlarge"
android:gravity="center"
android:minHeight="?attr/actionBarSize"
android:text="To"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/grey_60" />
<com.pchmn.materialchips.ChipsInput
android:id="@+id/chips_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:chip_deletable="true"
app:maxRows="3"
app:showChipDetailed="false" />
<ImageButton
android:id="@+id/contacts"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="@color/colorAccent"
app:srcCompat="@drawable/ic_contacts" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/grey_10" />
<EditText
android:id="@+id/subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/spacing_smlarge"
android:layout_marginRight="@dimen/spacing_smlarge"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:hint="Subject"
android:minHeight="?attr/actionBarSize"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/grey_80" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/grey_10" />
<EditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/spacing_smlarge"
android:background="@android:color/transparent"
android:gravity="top"
android:hint="Compose Mail"
android:minHeight="?attr/actionBarSize"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/grey_80" />
</LinearLayout>
dialog_contacts.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:gravity="center_vertical"
android:padding="@dimen/spacing_large"
android:text="Contact List"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Title" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/spacing_middle"
android:scrollbars="vertical"
android:scrollingCache="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout>
item_people_contacts.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:minHeight="?attr/actionBarSize"
android:orientation="horizontal">
<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_middle"
android:src="@drawable/photo_female_1" />
<View
android:layout_width="@dimen/spacing_medium"
android:layout_height="0dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/spacing_middle"
android:paddingTop="@dimen/spacing_middle">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="People Name"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
android:textColor="@color/grey_90" />
<TextView
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_small"
android:maxLines="2"
android:text="email.address@mail.com"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@color/grey_40" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/grey_10" />
</LinearLayout>
</LinearLayout>
</com.balysv.materialripple.MaterialRippleLayout>
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"/>
9. Values
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="light_green_600">#7CB342</color>
<color name="overlay_light_80">#CCFFFFFF</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>
</resources>
styles.xml
<resources>
<style name="AppTheme" parent="BaseTheme" />
<style name="BaseTheme" 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>
<item name="windowActionModeOverlay">true</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>
</resources>