Android - Custom Dialog with List Of Items (Java & Kotlin) !!
Often we might have a requirement to create a custom dialog with title, list of items and OK and Cancel buttons. Here I have implemented this very efficiently by using components like RecyclerView, CardView and Custom Layout.
Please follow the below steps to achieve this requirement.
Topics Covered
- RecyclerView
- CustomDialog
- CardView
- RecyclerView — OnItemClickListener
Dependencies needed
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
1. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dot.ga.gov.customdialog">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".HomeActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
2. activity_home.xml
This layout consists of one button, where user can click to display the alert.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeActivity">
<Button android:id="@+id/button"
android:text="Click"
android:onClick="clickHere"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</android.support.constraint.ConstraintLayout>
3. HomeActivity.java
Instantiating custom dialog when clicked on button.
public class HomeActivity extends AppCompatActivity implements DataAdapter.RecyclerViewItemClickListener {
Button clickButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
clickButton = (Button) findViewById(R.id.button);
}
CustomListViewDialog customDialog;
public void clickHere(View view) {
final String[] items = {"Apple Apple Apple ", "Banana", "Orange", "Grapes", "Apple", "Banana", "Orange", "Grapes", "Apple", "Banana", "Orange", "Grapes", "Apple", "Banana", "Orange", "Grapes", "Apple", "Banana", "Orange", "Grapes"};
// final String[] items = { "Apple", "Banana", "Orange", "Grapes", "Apple", "Banana", "Orange", "Grapes"};
DataAdapter dataAdapter = new DataAdapter(items, this);
customDialog = new CustomListViewDialog(HomeActivity.this, dataAdapter);
customDialog.show();
customDialog.setCanceledOnTouchOutside(false);
}
@Override
public void clickOnItem(String data) {
clickButton.setText(data);
if (customDialog != null){
customDialog.dismiss();
}
}
}
4. fruit_item.xml
This layout file is to display each fruit in list’s each row. We can implements this with any kind of view like including check box for selecting multiple items…
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:cardElevation="6dp"
android:layout_margin="2dp"
>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="match_parent"
android:text="text"
android:padding="5dp"
android:gravity="center"
android:layout_height="wrap_content"/>
</android.support.v7.widget.CardView>
5. custom_dialog_layout.xml
This layout is for whole alert view, how alert looks like with custom components.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:padding="5dp"
android:gravity="center"
android:layout_height="match_parent">
<TextView android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Good Morning"
android:background="#ff7e22"
android:textColor="#fff"
android:textSize="30sp"
android:gravity="center"
/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_below="@+id/title"
android:layout_gravity="center_horizontal"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/recycler_view"
android:orientation="horizontal">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:text="Cancel"
android:background="#046907"
android:textColor="#fff"
android:id="@+id/yes"
/>
<View android:layout_width="2dp" android:layout_height="match_parent"
android:background="#fff"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:text="Done"
android:background="#046907"
android:textColor="#fff"
android:id="@+id/no"
/>
</LinearLayout>
</RelativeLayout>
6. CustomListViewDialog.java
This is the class where we extend the Dialog class and we can customize the things required.
public class CustomListViewDialog extends Dialog implements View.OnClickListener{
public CustomListViewDialog(Context context, int themeResId) {
super(context, themeResId);
}
public CustomListViewDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
public Activity activity;
public Dialog dialog;
public Button yes, no;
TextView title;
RecyclerView recyclerView;
private RecyclerView.LayoutManager mLayoutManager;
RecyclerView.Adapter adapter;
public CustomListViewDialog(Activity a, RecyclerView.Adapter adapter) {
super(a);
this.activity = a;
this.adapter = adapter;
setupLayout();
}
private void setupLayout() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.custom_dialog_layout);
yes = (Button) findViewById(R.id.yes);
no = (Button) findViewById(R.id.no);
title = findViewById(R.id.title);
recyclerView = findViewById(R.id.recycler_view);
mLayoutManager = new LinearLayoutManager(activity);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(adapter);
yes.setOnClickListener(this);
no.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.yes:
//Do Something
break;
case R.id.no:
dismiss();
break;
default:
break;
}
dismiss();
}
7. DataAdapter.java
This class handles fruit_item layout creation and assigning data to each row and each Row Clicks.
package dot.ga.gov.customdialog;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.FruitViewHolder> {
private String[] mDataset;
RecyclerViewItemClickListener recyclerViewItemClickListener;
public DataAdapter(String[] myDataset, RecyclerViewItemClickListener listener) {
mDataset = myDataset;
this.recyclerViewItemClickListener = listener;
}
@NonNull
@Override
public FruitViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
FruitViewHolder vh = new FruitViewHolder(v);
return vh;
}
@Override
public void onBindViewHolder(@NonNull FruitViewHolder fruitViewHolder, int i) {
fruitViewHolder.mTextView.setText(mDataset[i]);
}
@Override
public int getItemCount() {
return mDataset.length;
}
public class FruitViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView mTextView;
public FruitViewHolder(View v) {
super(v);
mTextView = (TextView) v.findViewById(R.id.textView);
v.setOnClickListener(this);
}
@Override
public void onClick(View v) {
recyclerViewItemClickListener.clickOnItem(mDataset[this.getAdapterPosition()]);
}
}
public interface RecyclerViewItemClickListener {
void clickOnItem(String data);
}
}
The above image is the output of the above source code. Here you can change the layout for the recycler-view to make it more customize.
Kotlin:
Main advantage using Kotlin over Java:
kotlin-android extension plugin :
To support view binding and view caching
View binding means we don’t need to call findViewById() method to identify the views. So that decreases lot of boiler plate code.
View caching means findViewById() method calls only once at the first time and it will save the instance in the cache memory such that decreases the count of calling findViewById() implies better performance.
We can identify the view using their ID’s (Example, In the below class, we are updating the button text without instantiating the button with findViewById().
- HomeActivity.kt
class HomeActivity : AppCompatActivity(), DataAdapter.RecyclerViewItemClickListener {
internal var customDialog: CustomListViewDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
}
fun clickHere(view: View) {
val items = arrayOf(
"Apple Apple Apple ",
"Banana",
"Orange",
"Grapes",
"Apple",
"Banana",
"Orange",
"Grapes",
"Apple",
"Banana",
"Orange",
"Grapes",
"Apple",
"Banana",
"Orange",
"Grapes",
"Apple",
"Banana",
"Orange",
"Grapes"
)
val dataAdapter = DataAdapter(items, this)
customDialog = CustomListViewDialog(this@HomeActivity, dataAdapter)
//if we know that the particular variable not null any time ,we can assign !! (not null operator ), then it won't check for null, if it becomes null, it willthrow exception
customDialog!!.show()
customDialog!!.setCanceledOnTouchOutside(false)
}
override fun clickOnItem(data: String) {
//Synthetic property without calling findViewById() method and supports view caching to improve performance.
button.text = data
if (customDialog != null) {
customDialog!!.dismiss()
}
}
}
2.
DataAdapter.kt
class DataAdapter(
private val mDataset: Array<String>,
internal var recyclerViewItemClickListener: RecyclerViewItemClickListener
) : RecyclerView.Adapter<DataAdapter.FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, i: Int): FruitViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
return FruitViewHolder(v)
}
override fun onBindViewHolder(fruitViewHolder: FruitViewHolder, i: Int) {
fruitViewHolder.mTextView.text = mDataset[i]
}
override fun getItemCount(): Int {
return mDataset.size
}
inner class FruitViewHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener {
var mTextView: TextView
init {
mTextView = v.textView
v.setOnClickListener(this)
}
override fun onClick(v: View) {
recyclerViewItemClickListener.clickOnItem(mDataset[this.adapterPosition])
}
}
interface RecyclerViewItemClickListener {
fun clickOnItem(data: String)
}
}
3. CustomListViewDialog.kt
class CustomListViewDialog(var activity: Activity, internal var adapter: RecyclerView.Adapter<*>) : Dialog(activity),
View.OnClickListener {
var dialog: Dialog? = null
internal var recyclerView: RecyclerView? = null
private var mLayoutManager: RecyclerView.LayoutManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.custom_dialog_layout)
recyclerView = recycler_view
mLayoutManager = LinearLayoutManager(activity)
recyclerView?.layoutManager = mLayoutManager
recyclerView?.adapter = adapter
yes.setOnClickListener(this)
no.setOnClickListener(this)
}
override fun onClick(v: View) {
when (v.id) {
R.id.yes -> {
}
R.id.no -> dismiss()
else -> {
}
}//Do Something
dismiss()
}
GitHub :
- Kotlin
2. Java
Thanks & Cheers,
Srinivasa Rao Makkena