vxh.viet
8/10/2016 - 3:26 AM

Custom Dialog

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();