Custom Dialog
Source: StackOverflow, StackOverflow, StackOverflow
Answer: Quite complex custom Dialog.
ShuttaDialog.java
public class ShuttaDialog extends Dialog {
public ShuttaDialog(Context context) {
super(context);
}
public ShuttaDialog(Context context, int theme) {
super(context, theme);
}
public ShuttaDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
/**
* Helper class for creating a custom dialog
*/
public static class Builder {
public static final int CHECK = R.drawable.img_check_48dp;
public static final int WARNING = R.drawable.img_warning_48dp;
private Context context;
private int iconID = -1;
private String title;
private String message;
private String option1Text;
private String option2Text;
private String option3Text;
private String option4Text;
private ImageView iconImageView;
private TextView titleTextView;
private TextView messageTextView;
private TextView option1TextView;
private TextView option2TextView;
private TextView option3TextView;
private TextView option4TextView;
private OnClickListener option1ClickListener;
private OnClickListener option2ClickListener;
private OnClickListener option3ClickListener;
private OnClickListener option4ClickListener;
private boolean isDismissedOnTouchOutside;
public Builder(Context context) {
this.context = context;
}
public TextView getOption1TextView() {
return option1TextView;
}
public TextView getOption2TextView() {
return option2TextView;
}
public TextView getOption3TextView() {
return option3TextView;
}
public TextView getOption4TextView() {
return option4TextView;
}
public Builder setIcon(int iconID){
try{
this.iconID = iconID;
}catch (Exception e){
e.printStackTrace();
}
return this;
}
public Builder setTitle(String title){
try{
this.title = title;
}catch (Exception e){
e.printStackTrace();
}
return this;
}
public Builder setMessage(String message){
try{
this.message = message;
}catch (Exception e){
e.printStackTrace();
}
return this;
}
public Builder setOption1(String option1Text, OnClickListener listener) {
try {
this.option1Text = option1Text;
this.option1ClickListener = listener;
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
public Builder setOption2(String option2Text, OnClickListener listener) {
try {
this.option2Text = option2Text;
this.option2ClickListener = listener;
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
public Builder setOption3(String option3Text, OnClickListener listener) {
try {
this.option3Text = option3Text;
this.option3ClickListener = listener;
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
public Builder setOption4(String option3Text, OnClickListener listener) {
try {
this.option4Text = option3Text;
this.option4ClickListener = listener;
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
public Builder setDismissOnTouchOutside(boolean isDismissedOnTouchOutside){
try{
this.isDismissedOnTouchOutside = isDismissedOnTouchOutside;
}catch (Exception e){
e.printStackTrace();
}
return this;
}
public ShuttaDialog create() {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final ShuttaDialog dialog;
dialog = new ShuttaDialog(context);
//In Android 5 a dialogFragment seems to have a background as well. See http://stackoverflow.com/a/31206393/1602807
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(context, R.color.transparent)));
//see http://stackoverflow.com/a/26108336/1602807, even though without this, it still run just fine
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
dialog.getWindow().setWindowAnimations(R.style.DialogAnimation_2);
View layout = inflater.inflate(R.layout.dialog_shutta, null);
iconImageView = ((ImageView) layout.findViewById(R.id.dialog_shutta_icon));
titleTextView = ((TextView) layout.findViewById(R.id.dialog_shutta_title));
messageTextView = ((TextView) layout.findViewById(R.id.dialog_shutta_message));
option1TextView = ((TextView) layout.findViewById(R.id.dialog_shutta_option_1));
option2TextView = ((TextView) layout.findViewById(R.id.dialog_shutta_option_2));
option3TextView = ((TextView) layout.findViewById(R.id.dialog_shutta_option_3));
option4TextView = ((TextView) layout.findViewById(R.id.dialog_shutta_option_4));
if(iconID != -1){
iconImageView.setImageDrawable(ContextCompat.getDrawable(context, iconID));
}
if(titleTextView != null){
titleTextView.setText(title);
}
if(messageTextView != null){
messageTextView.setText(message);
}
if (option1Text != null) {
option1TextView.setText(option1Text);
option1TextView.setVisibility(View.VISIBLE);
if (option1ClickListener != null) {
option1TextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
option1ClickListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
}
});
}
}
if (option2Text != null) {
option2TextView.setText(option2Text);
option2TextView.setVisibility(View.VISIBLE);
if (option2ClickListener != null) {
option2TextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
option2ClickListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
}
});
}
}
if (option3Text != null) {
option3TextView.setText(option3Text);
option3TextView.setVisibility(View.VISIBLE);
if (option3ClickListener != null) {
option3TextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
option3ClickListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
}
});
}
}
if (option4Text != null) {
option4TextView.setText(option4Text);
option4TextView.setVisibility(View.VISIBLE);
if (option4ClickListener != null) {
option4TextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
option4ClickListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
}
});
}
}
if(isDismissedOnTouchOutside){
dialog.setCanceledOnTouchOutside(true);
}
dialog.setContentView(layout);
//dialog seem to have their own size, can't resize in xml
//according to http://stackoverflow.com/a/6631310/1602807 and http://stackoverflow.com/a/24264884/1602807
//attribute should be set after dialog.show() but setting this after setContentView runs just fine
dialog.getWindow().setLayout(Util.dpToPx(context, 300), WindowManager.LayoutParams.WRAP_CONTENT);
return dialog;
}
}
}
public static int dpToPx(Context context, int dp) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
res/values/styles.xml
<style name="DialogAnimation_2">
<item name="android:windowEnterAnimation">@anim/slide_down</item>
<item name="android:windowExitAnimation">@anim/slide_up</item>
</style>
res/anim/slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="250"
android:fromXScale="1.0"
android:fromYScale="0.0"
android:interpolator="@android:anim/linear_interpolator"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
res/anim/slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
android:duration="250"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@android:anim/linear_interpolator"
android:toXScale="1.0"
android:toYScale="0.0" />
</set>
dialog_shutta.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/shutta_dialog_margin_top"
android:background="@drawable/shape_shutta_dialog_white_rectangle"
android:orientation="vertical"
android:padding="@dimen/shutta_dialog_inside_padding">
<TextView
android:id="@+id/dialog_shutta_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/shutta_dialog_title_margin_top"
android:gravity="center"
android:textSize="@dimen/shutta_dialog_text_size"
android:textStyle="bold"/>
<TextView
android:id="@+id/dialog_shutta_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/shutta_dialog_message_margin_bottom"
android:gravity="center"
android:textSize="@dimen/shutta_dialog_text_size"/>
<TextView
android:id="@+id/dialog_shutta_option_1"
android:layout_width="match_parent"
android:layout_height="@dimen/shutta_dialog_option_height"
android:layout_margin="@dimen/shutta_dialog_option_margin"
android:background="@drawable/shape_light_blue_rectangle_with_ripple"
android:clickable="true"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/shutta_dialog_text_size"
android:visibility="gone"/>
<TextView
android:id="@+id/dialog_shutta_option_2"
android:layout_width="match_parent"
android:layout_height="@dimen/shutta_dialog_option_height"
android:layout_margin="@dimen/shutta_dialog_option_margin"
android:background="@drawable/shape_light_blue_rectangle_with_ripple"
android:clickable="true"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/shutta_dialog_text_size"
android:visibility="gone"/>
<TextView
android:id="@+id/dialog_shutta_option_3"
android:layout_width="match_parent"
android:layout_height="@dimen/shutta_dialog_option_height"
android:layout_margin="@dimen/shutta_dialog_option_margin"
android:background="@drawable/shape_light_blue_rectangle_with_ripple"
android:clickable="true"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/shutta_dialog_text_size"
android:visibility="gone"/>
<TextView
android:id="@+id/dialog_shutta_option_4"
android:layout_width="match_parent"
android:layout_height="@dimen/shutta_dialog_option_height"
android:layout_margin="@dimen/shutta_dialog_option_margin"
android:background="@drawable/shape_light_gray_rectangle_with_ripple"
android:clickable="true"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/shutta_dialog_text_size"
android:textStyle="bold"
android:visibility="gone"/>
</LinearLayout>
<FrameLayout
android:layout_width="@dimen/shutta_dialog_circle_bg_size"
android:layout_height="@dimen/shutta_dialog_circle_bg_size"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="@drawable/shape_shutta_dialog_white_circle"/>
<ImageView
android:id="@+id/dialog_shutta_icon"
android:layout_width="@dimen/shutta_dialog_circle_size"
android:layout_height="@dimen/shutta_dialog_circle_size"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/shutta_dialog_circle_margin_top"
android:src="@drawable/img_check_48dp"/>
</RelativeLayout>
res/values/dimens.xml
<!--Shutta dialog's stuff-->
<dimen name="shutta_dialog_width">300dp</dimen>
<dimen name="shutta_dialog_inside_padding">8dp</dimen>
<dimen name="shutta_dialog_margin_top">25dp</dimen>
<dimen name="shutta_dialog_option_height">48dp</dimen>
<dimen name="shutta_dialog_option_margin">2dp</dimen>
<dimen name="shutta_dialog_title_margin_top">24dp</dimen>
<dimen name="shutta_dialog_message_margin_bottom">16dp</dimen>
<dimen name="shutta_dialog_text_size">18sp</dimen>
<dimen name="shutta_dialog_circle_bg_size">58dp</dimen>
<dimen name="shutta_dialog_circle_size">48dp</dimen>
<dimen name="shutta_dialog_circle_margin_top">5dp</dimen>
<!--End Shutta dialog's stuff-->
res/drawable/shape_shutta_dialog_white_rectangle.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="@color/white_tint_1" />
</shape>
See ripple effect gist for more detail, basically we need a ripple effect in drawable-v21
folder for devices running API >= 21, and one in normal drawable
to support lower devices.
res/drawable-v21/shape_light_blue_rectangle_with_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/shutta_blue_light_lighter">
<item>
<shape android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="@color/shutta_blue_light" />
</shape>
</item>
</ripple>
res/drawable/shape_light_blue_rectangle_with_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/shutta_blue_light_lighter" />
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/shutta_blue_light" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/shutta_blue_light" />
</shape>
</item>
</selector>
drawable-v21/shape_light_gray_rectangle_with_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/white_tint_1">
<item>
<shape android:shape="rectangle">
<corners android:radius="2dp" />
<solid android:color="@color/black_tint_8" />
</shape>
</item>
</ripple>
drawable/shape_light_gray_rectangle_with_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/white_tint_1" />
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/black_tint_8" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="3dp" />
<solid android:color="@color/black_tint_8" />
</shape>
</item>
</selector>
drawable/shape_shutta_dialog_white_circle.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/white_tint_1" />
</shape>
res/values/colors.xml
<color name="white_tint_1">#e5e5e5</color>
<color name="black_tint_8">#b2b2b2</color>
<color name="transparent">#00000000</color>
<color name="shutta_blue">#4c7eed</color>
<color name="shutta_blue_85">#d94c7eed</color>
<color name="shutta_blue_light">#86B3F7</color>
<color name="shutta_blue_light_lighter">#b7d2fa</color>
To use it do something like this:
ShuttaDialog test;
test = new ShuttaDialog.Builder(this)
.setIcon(ShuttaDialog.Builder.WARNING) //default is check icon
.setTitle("Test Title")
.setMessage("Test message, this is a very frikkin long long long long long long long long long long long long asssssssss mezzzeageeeee")
.setOption1("test", null)
.setOption2("test", null)
.setOption3("test", null)
.setOption4("test", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
test.dismiss();
}
})
.setDismissOnTouchOutside(true)
.create();
test.show();