Android - Custom Dialog with List Of Items (Java & Kotlin) !!

Srinivasa Rao Makkena
5 min readFeb 12, 2019

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

  1. RecyclerView
  2. CustomDialog
  3. CardView
  4. 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().

  1. 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 :

  1. Kotlin

2. Java

Thanks & Cheers,

Srinivasa Rao Makkena

--

--