Android Create Chips Like Gmail

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>