package com.forfan.bigbang.view;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.CardView;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.forfan.bigbang.R;
import com.forfan.bigbang.util.ThemeUtil;
import com.forfan.bigbang.util.ViewUtil;
/**
* Created by Rey on 12/10/2014.
*/
public class Dialog extends android.app.Dialog{
private ContainerFrameLayout mContainer;
private int mLayoutWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mLayoutHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mMaxWidth;
private int mMaxHeight;
protected TextView mTitle;
protected Button mPositiveAction;
protected Button mNegativeAction;
protected Button mNeutralAction;
private View mContent;
private DialogCardView mCardView;
protected int mContentPadding;
protected int mActionHeight;
protected int mActionOuterHeight;
protected int mActionOuterPadding;
protected int mActionMinWidth;
protected int mActionPadding_w;
protected int mActionPadding_h;
protected int mDialogHorizontalPadding;
protected int mDialogVerticalPadding;
protected int mInAnimationId;
protected int mOutAnimationId;
private final Handler mHandler = new Handler();
private final Runnable mDismissAction = new Runnable() {
public void run() {
//dirty fix for java.lang.IllegalArgumentException: View not attached to window manager
try {
Dialog.super.dismiss();
}
catch(IllegalArgumentException ex){}
}
};
private boolean mLayoutActionVertical = false;
private boolean mCancelable = true;
private boolean mCanceledOnTouchOutside = true;
private boolean mDismissPending = false;
/**
* The viewId of title view.
*/
public static final int TITLE = ViewUtil.generateViewId();
/**
* The viewId of positive action button.
*/
public static final int ACTION_POSITIVE = ViewUtil.generateViewId();
/**
* The viewId of negative action button.
*/
public static final int ACTION_NEGATIVE = ViewUtil.generateViewId();
/**
* The viewId of neutral action button.
*/
public static final int ACTION_NEUTRAL = ViewUtil.generateViewId();
public Dialog(Context context) {
this(context, R.style.Material_App_Dialog_Light);
}
public Dialog(Context context, int style) {
super(context, style);
//Override style to ensure not show window's title or background.
//TODO: find a way to ensure windowIsFloating attribute is false.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setBackgroundDrawable(BlankDrawable.getInstance());
WindowManager.LayoutParams layout = getWindow().getAttributes();
layout.width = ViewGroup.LayoutParams.MATCH_PARENT;
layout.height = ViewGroup.LayoutParams.MATCH_PARENT;
layout.windowAnimations = R.style.DialogNoAnimation;
getWindow().setAttributes(layout);
init(context, style);
}
private void init(Context context, int style){
mContentPadding = ThemeUtil.dpToPx(context, 24);
mActionMinWidth = ThemeUtil.dpToPx(context, 64);
mActionHeight = ThemeUtil.dpToPx(context, 36);
mActionOuterHeight = ThemeUtil.dpToPx(context, 48);
mActionPadding_h = ThemeUtil.dpToPx(context, 8);
mActionPadding_w = ThemeUtil.dpToPx(context, 8);
mActionOuterPadding = ThemeUtil.dpToPx(context, 8);
mDialogHorizontalPadding = ThemeUtil.dpToPx(context, 40);
mDialogVerticalPadding = ThemeUtil.dpToPx(context, 24);
mCardView = new DialogCardView(context);
mContainer = new ContainerFrameLayout(context);
mTitle = new TextView(context);
mPositiveAction = new Button(context);
mNegativeAction = new Button(context);
mNeutralAction = new Button(context);
mCardView.setPreventCornerOverlap(false);
mCardView.setUseCompatPadding(true);
mTitle.setId(TITLE);
mTitle.setGravity(Gravity.START);
// mTitle.setPadding(mContentPadding, mContentPadding, mContentPadding, mContentPadding - mActionPadding);
mTitle.setPadding(mContentPadding, mContentPadding, 0, mContentPadding /2);
mPositiveAction.setId(ACTION_POSITIVE);
mPositiveAction.setPadding(mActionPadding_w, mActionPadding_h, mActionPadding_w, mActionPadding_h);
//设置背景
mPositiveAction.setBackgroundResource(R.drawable.button_white_stroke_green);
mNegativeAction.setId(ACTION_NEGATIVE);
mNegativeAction.setPadding(mActionPadding_w, mActionPadding_h, mActionPadding_w, mActionPadding_h);
mNegativeAction.setBackgroundResource(0);
mNeutralAction.setId(ACTION_NEUTRAL);
mNeutralAction.setPadding(mActionPadding_w, mActionPadding_h, mActionPadding_w, mActionPadding_h);
mNeutralAction.setBackgroundResource(0);
mContainer.addView(mCardView);
mCardView.addView(mTitle);
mCardView.addView(mPositiveAction);
mCardView.addView(mNegativeAction);
mCardView.addView(mNeutralAction);
backgroundColor(ThemeUtil.windowBackground(context, 0xFFFFFFFF));
elevation(ThemeUtil.dpToPx(context, 4));
cornerRadius(ThemeUtil.dpToPx(context, 2));
dimAmount(0.5f);
layoutDirection(View.LAYOUT_DIRECTION_LOCALE);
titleTextAppearance(R.style.TextAppearance_AppCompat_Title);
actionTextAppearance(R.style.TextAppearance_AppCompat_Button);
dividerColor(0x1E000000);
dividerHeight(ThemeUtil.dpToPx(context, 1));
cancelable(true);
canceledOnTouchOutside(true);
clearContent();
onCreate();
applyStyle(style);
super.setContentView(mContainer);
}
protected void onCreate(){
}
public Dialog applyStyle(int resId){
Context context = getContext();
TypedArray a = context.obtainStyledAttributes(resId, R.styleable.Dialog);
int layout_width = mLayoutWidth;
int layout_height = mLayoutHeight;
boolean layoutParamsDefined = false;
int titleTextAppearance = 0;
int titleTextColor = 0;
boolean titleTextColorDefined = false;
int actionBackground = 0;
int actionTextAppearance = 0;
ColorStateList actionTextColors = null;
int positiveActionBackground = 0;
int positiveActionTextAppearance = 0;
ColorStateList positiveActionTextColors = null;
int negativeActionBackground = 0;
int negativeActionTextAppearance = 0;
ColorStateList negativeActionTextColors = null;
int neutralActionBackground = 0;
int neutralActionTextAppearance = 0;
ColorStateList neutralActionTextColors = null;
for(int i = 0, count = a.getIndexCount(); i < count; i++){
int attr = a.getIndex(i);
if(attr == R.styleable.Dialog_android_layout_width) {
layout_width = a.getLayoutDimension(attr, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParamsDefined = true;
}
else if(attr == R.styleable.Dialog_android_layout_height) {
layout_height = a.getLayoutDimension(attr, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParamsDefined = true;
}
else if(attr == R.styleable.Dialog_di_maxWidth)
maxWidth(a.getDimensionPixelOffset(attr, 0));
else if(attr == R.styleable.Dialog_di_maxHeight)
maxHeight(a.getDimensionPixelOffset(attr, 0));
else if(attr == R.styleable.Dialog_di_dimAmount)
dimAmount(a.getFloat(attr, 0));
else if(attr == R.styleable.Dialog_di_backgroundColor)
backgroundColor(a.getColor(attr, 0));
else if(attr == R.styleable.Dialog_di_maxElevation)
maxElevation(a.getDimensionPixelOffset(attr, 0));
else if(attr == R.styleable.Dialog_di_elevation)
elevation(a.getDimensionPixelOffset(attr, 0));
else if(attr == R.styleable.Dialog_di_cornerRadius)
cornerRadius(a.getDimensionPixelOffset(attr, 0));
else if(attr == R.styleable.Dialog_di_layoutDirection)
layoutDirection(a.getInteger(attr, 0));
else if(attr == R.styleable.Dialog_di_titleTextAppearance)
titleTextAppearance = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_titleTextColor) {
titleTextColor = a.getColor(attr, 0);
titleTextColorDefined = true;
}
else if(attr == R.styleable.Dialog_di_actionBackground)
actionBackground = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_actionTextAppearance)
actionTextAppearance = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_actionTextColor)
actionTextColors = a.getColorStateList(attr);
else if(attr == R.styleable.Dialog_di_positiveActionBackground)
positiveActionBackground = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_positiveActionTextAppearance)
positiveActionTextAppearance = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_positiveActionTextColor)
positiveActionTextColors = a.getColorStateList(attr);
else if(attr == R.styleable.Dialog_di_negativeActionBackground)
negativeActionBackground = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_negativeActionTextAppearance)
negativeActionTextAppearance = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_negativeActionTextColor)
negativeActionTextColors = a.getColorStateList(attr);
else if(attr == R.styleable.Dialog_di_neutralActionBackground)
neutralActionBackground = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_neutralActionTextAppearance)
neutralActionTextAppearance = a.getResourceId(attr, 0);
else if(attr == R.styleable.Dialog_di_neutralActionTextColor)
neutralActionTextColors = a.getColorStateList(attr);
else if(attr == R.styleable.Dialog_di_inAnimation)
inAnimation(a.getResourceId(attr, 0));
else if(attr == R.styleable.Dialog_di_outAnimation)
outAnimation(a.getResourceId(attr, 0));
else if(attr == R.styleable.Dialog_di_dividerColor)
dividerColor(a.getColor(attr, 0));
else if(attr == R.styleable.Dialog_di_dividerHeight)
dividerHeight(a.getDimensionPixelOffset(attr, 0));
else if(attr == R.styleable.Dialog_di_cancelable)
cancelable(a.getBoolean(attr, true));
else if(attr == R.styleable.Dialog_di_canceledOnTouchOutside)
canceledOnTouchOutside(a.getBoolean(attr, true));
}
a.recycle();
if(layoutParamsDefined)
layoutParams(layout_width, layout_height);
if(titleTextAppearance != 0)
titleTextAppearance(titleTextAppearance);
if(titleTextColorDefined)
titleColor(titleTextColor);
if(actionBackground != 0)
actionBackground(actionBackground);
if(actionTextAppearance != 0)
actionTextAppearance(actionTextAppearance);
if(actionTextColors != null)
actionTextColor(actionTextColors);
if(positiveActionBackground != 0)
positiveActionBackground(positiveActionBackground);
if(positiveActionTextAppearance!= 0)
positiveActionTextAppearance(positiveActionTextAppearance);
if(positiveActionTextColors != null)
positiveActionTextColor(positiveActionTextColors);
if(negativeActionBackground != 0)
negativeActionBackground(negativeActionBackground);
if(negativeActionTextAppearance!= 0)
negativeActionTextAppearance(negativeActionTextAppearance);
if(negativeActionTextColors != null)
negativeActionTextColor(negativeActionTextColors);
if(neutralActionBackground != 0)
neutralActionBackground(neutralActionBackground);
if(neutralActionTextAppearance!= 0)
neutralActionTextAppearance(neutralActionTextAppearance);
if(neutralActionTextColors != null)
neutralActionTextColor(neutralActionTextColors);
return this;
}
/**
* Clear the content of this Dialog.
* @return The Dialog for chaining methods.
*/
public Dialog clearContent(){
title(0);
positiveAction(0);
positiveActionClickListener(null);
negativeAction(0);
negativeActionClickListener(null);
neutralAction(0);
neutralActionClickListener(null);
contentView(null);
return this;
}
/**
* Set the params of this Dialog layout.
* @param width The width param. Can be the size in pixels, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
* @param height The height param. Can be the size in pixels, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
* @return The Dialog for chaining methods.
*/
public Dialog layoutParams(int width, int height){
mLayoutWidth = width;
mLayoutHeight = height;
return this;
}
/**
* Set the maximum width of this Dialog layout.
* @param width The maximum width in pixels.
* @return The Dialog for chaining methods.
*/
public Dialog maxWidth(int width){
mMaxWidth = width;
return this;
}
/**
* Set the maximum height of this Dialog layout.
* @param height The maximum height in pixels.
* @return The Dialog for chaining methods.
*/
public Dialog maxHeight(int height){
mMaxHeight = height;
return this;
}
/**
* Set the dim amount of the region outside this Dialog.
* @param amount The dim amount in [0..1].
* @return The Dialog for chaining methods.
*/
public Dialog dimAmount(float amount){
Window window = getWindow();
if(amount > 0f){
window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
WindowManager.LayoutParams lp = window.getAttributes();
lp.dimAmount = amount;
window.setAttributes(lp);
}
else
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
return this;
}
/**
* Set the background color of this Dialog
* @param color The color value.
* @return The Dialog for chaining methods.
*/
public Dialog backgroundColor(int color){
mCardView.setCardBackgroundColor(color);
return this;
}
/**
* Set the elevation value of this Dialog.
* @param elevation
* @return The Dialog for chaining methods.
*/
public Dialog elevation(float elevation){
if(mCardView.getMaxCardElevation() < elevation)
mCardView.setMaxCardElevation(elevation);
mCardView.setCardElevation(elevation);
return this;
}
/**
* Set the maximum elevation value of this Dialog.
* @param elevation
* @return The Dialog for chaining methods.
*/
public Dialog maxElevation(float elevation){
mCardView.setMaxCardElevation(elevation);
return this;
}
/**
* Set the corner radius of this Dialog.
* @param radius The corner radius.
* @return The Dialog for chaining methods.
*/
public Dialog cornerRadius(float radius){
mCardView.setRadius(radius);
return this;
}
/**
* Set the divider's color of this Dialog.
* @param color The color value.
* @return The Dialog for chaining methods.
*/
public Dialog dividerColor(int color){
mCardView.setDividerColor(color);
return this;
}
/**
* Set the height of divider of this Dialog.
* @param height The size value in pixels.
* @return The Dialog for chaining methods.
*/
public Dialog dividerHeight(int height){
mCardView.setDividerHeight(height);
return this;
}
/**
* Set the title of this Dialog.
* @param title The title text.
* @return The Dialog for chaining methods.
*/
public Dialog title(CharSequence title){
mTitle.setText(title);
mTitle.setVisibility(TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE);
return this;
}
/**
* Set the title of this Dialog.
* @param id The resourceId of text.
* @return The Dialog for chaining methods.
*/
public Dialog title(int id){
return title(id == 0 ? null : getContext().getResources().getString(id));
}
@Override
public void setTitle(CharSequence title) {
title(title);
}
@Override
public void setTitle(int titleId) {
title(titleId);
}
/**
* Set the text's color of Dialog's title.
* @param color The color value.
* @return The Dialog for chaining methods.
*/
public Dialog titleColor(int color){
mTitle.setTextColor(color);
return this;
}
public TextView getTitle(){
return mTitle;
}
/**
* Sets the text color, size, style of the title view from the specified TextAppearance resource.
* @param resId The resourceId value.
* @return The Dialog for chaining methods.
*/
public Dialog titleTextAppearance(int resId){
mTitle.setTextAppearance(getContext(), resId);
return this;
}
/**
* Set the background drawable of all action buttons.
* @param id The resourceId of drawable.
* @return The Dialog for chaining methods.
*/
public Dialog actionBackground(int id){
positiveActionBackground(id);
negativeActionBackground(id);
neutralActionBackground(id);
return this;
}
/**
* Set the background drawable of all action buttons.
* @param drawable The background drawable.
* @return The Dialog for chaining methods.
*/
public Dialog actionBackground(Drawable drawable){
positiveActionBackground(drawable);
negativeActionBackground(drawable);
neutralActionBackground(drawable);
return this;
}
/**
* Sets the text color, size, style of all action buttons from the specified TextAppearance resource.
* @param resId The resourceId value.
* @return The Dialog for chaining methods.
*/
public Dialog actionTextAppearance(int resId){
positiveActionTextAppearance(resId);
negativeActionTextAppearance(resId);
neutralActionTextAppearance(resId);
return this;
}
/**
* Sets the text color of all action buttons.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog actionTextColor(ColorStateList color){
positiveActionTextColor(color);
negativeActionTextColor(color);
neutralActionTextColor(color);
return this;
}
/**
* Sets the text color of all action buttons.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog actionTextColor(int color){
positiveActionTextColor(color);
negativeActionTextColor(color);
neutralActionTextColor(color);
return this;
}
/**
* Set the text of positive action button.
* @param action
* @return The Dialog for chaining methods.
*/
public Dialog positiveAction(CharSequence action){
mPositiveAction.setText(action);
mPositiveAction.setVisibility(TextUtils.isEmpty(action) ? View.GONE : View.VISIBLE);
return this;
}
/**
* Set the text of positive action button.
* @param id The resourceId of text.
* @return The Dialog for chaining methods.
*/
public Dialog positiveAction(int id){
return positiveAction(id == 0 ? null : getContext().getResources().getString(id));
}
/**
* Set the background drawable of positive action button.
* @param drawable The background drawable.
* @return The Dialog for chaining methods.
*/
public Dialog positiveActionBackground(Drawable drawable){
ViewUtil.setBackground(mPositiveAction, drawable);
return this;
}
/**
* Set the background drawable of positive action button.
* @param id The resourceId of drawable.
* @return The Dialog for chaining methods.
*/
public Dialog positiveActionBackground(int id){
return positiveActionBackground(id == 0 ? null : getContext().getResources().getDrawable(id));
}
/**
* Sets the text color, size, style of positive action button from the specified TextAppearance resource.
* @param resId The resourceId value.
* @return The Dialog for chaining methods.
*/
public Dialog positiveActionTextAppearance(int resId){
mPositiveAction.setTextAppearance(getContext(), resId);
return this;
}
/**
* Sets the text color of positive action button.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog positiveActionTextColor(ColorStateList color){
mPositiveAction.setTextColor(color);
return this;
}
/**
* Sets the text color of positive action button.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog positiveActionTextColor(int color){
mPositiveAction.setTextColor(color);
return this;
}
/**
* Set a listener will be called when positive action button is clicked.
* @param listener The {@link View.OnClickListener} will be called.
* @return The Dialog for chaining methods.
*/
public Dialog positiveActionClickListener(View.OnClickListener listener){
mPositiveAction.setOnClickListener(listener);
return this;
}
/**
* Set the text of negative action button.
* @param action
* @return The Dialog for chaining methods.
*/
public Dialog negativeAction(CharSequence action){
mNegativeAction.setText(action);
mNegativeAction.setVisibility(TextUtils.isEmpty(action) ? View.GONE : View.VISIBLE);
return this;
}
/**
* Set the text of negative action button.
* @param id The resourceId of text.
* @return The Dialog for chaining methods.
*/
public Dialog negativeAction(int id){
return negativeAction(id == 0 ? null : getContext().getResources().getString(id));
}
/**
* Set the background drawable of negative action button.
* @param drawable The background drawable.
* @return The Dialog for chaining methods.
*/
public Dialog negativeActionBackground(Drawable drawable){
ViewUtil.setBackground(mNegativeAction, drawable);
return this;
}
/**
* Set the background drawable of neagtive action button.
* @param id The resourceId of drawable.
* @return The Dialog for chaining methods.
*/
public Dialog negativeActionBackground(int id){
return negativeActionBackground(id == 0 ? null : getContext().getResources().getDrawable(id));
}
/**
* Sets the text color, size, style of negative action button from the specified TextAppearance resource.
* @param resId The resourceId value.
* @return The Dialog for chaining methods.
*/
public Dialog negativeActionTextAppearance(int resId){
mNegativeAction.setTextAppearance(getContext(), resId);
return this;
}
/**
* Sets the text color of negative action button.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog negativeActionTextColor(ColorStateList color){
mNegativeAction.setTextColor(color);
return this;
}
/**
* Sets the text color of negative action button.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog negativeActionTextColor(int color){
mNegativeAction.setTextColor(color);
return this;
}
/**
* Set a listener will be called when negative action button is clicked.
* @param listener The {@link View.OnClickListener} will be called.
* @return The Dialog for chaining methods.
*/
public Dialog negativeActionClickListener(View.OnClickListener listener){
mNegativeAction.setOnClickListener(listener);
return this;
}
/**
* Set the text of neutral action button.
* @param action
* @return The Dialog for chaining methods.
*/
public Dialog neutralAction(CharSequence action){
mNeutralAction.setText(action);
mNeutralAction.setVisibility(TextUtils.isEmpty(action) ? View.GONE : View.VISIBLE);
return this;
}
/**
* Set the text of neutral action button.
* @param id The resourceId of text.
* @return The Dialog for chaining methods.
*/
public Dialog neutralAction(int id){
return neutralAction(id == 0 ? null : getContext().getResources().getString(id));
}
/**
* Set the background drawable of neutral action button.
* @param drawable The background drawable.
* @return The Dialog for chaining methods.
*/
public Dialog neutralActionBackground(Drawable drawable){
ViewUtil.setBackground(mNeutralAction, drawable);
return this;
}
/**
* Set the background drawable of neutral action button.
* @param id The resourceId of drawable.
* @return The Dialog for chaining methods.
*/
public Dialog neutralActionBackground(int id){
return neutralActionBackground(id == 0 ? null : getContext().getResources().getDrawable(id));
}
/**
* Sets the text color, size, style of neutral action button from the specified TextAppearance resource.
* @param resId The resourceId value.
* @return The Dialog for chaining methods.
*/
public Dialog neutralActionTextAppearance(int resId){
mNeutralAction.setTextAppearance(getContext(), resId);
return this;
}
/**
* Sets the text color of neutral action button.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog neutralActionTextColor(ColorStateList color){
mNeutralAction.setTextColor(color);
return this;
}
/**
* Sets the text color of neutral action button.
* @param color
* @return The Dialog for chaining methods.
*/
public Dialog neutralActionTextColor(int color){
mNeutralAction.setTextColor(color);
return this;
}
/**
* Set a listener will be called when neutral action button is clicked.
* @param listener The {@link View.OnClickListener} will be called.
* @return The Dialog for chaining methods.
*/
public Dialog neutralActionClickListener(View.OnClickListener listener){
mNeutralAction.setOnClickListener(listener);
return this;
}
/**
* Set the layout direction of this Dialog
* @param direction The layout direction value. Can be {@link View#LAYOUT_DIRECTION_LTR}, {@link View#LAYOUT_DIRECTION_RTL} or {@link View#LAYOUT_DIRECTION_LOCALE}
* @return The Dialog for chaining methods.
*/
public Dialog layoutDirection(int direction){
ViewCompat.setLayoutDirection(mCardView, direction);
return this;
}
/**
* Set the animation when Dialog enter the screen.
* @param resId The resourceId of animation.
* @return The Dialog for chaining methods.
*/
public Dialog inAnimation(int resId){
mInAnimationId = resId;
return this;
}
/**
* Set the animation when Dialog exit the screen.
* @param resId The resourceId of animation.
* @return The Dialog for chaining methods.
*/
public Dialog outAnimation(int resId){
mOutAnimationId = resId;
return this;
}
/**
* Indicate that Dialog should show divider when the content is longer than container view.
* @param show
* @return The Dialog for chaining methods.
*/
public Dialog showDivider(boolean show){
mCardView.setShowDivider(show);
return this;
}
/**
* Set the content view of this Dialog.
* @param v The content view.
* @return The Dialog for chaining methods.
*/
public Dialog contentView(View v){
if(mContent != v) {
if(mContent != null)
mCardView.removeView(mContent);
mContent = v;
}
if(mContent != null)
mCardView.addView(mContent);
return this;
}
/**
* Set the content view of this Dialog.
* @param layoutId The reourceId of layout.
* @return The Dialog for chaining methods.
*/
public Dialog contentView(int layoutId){
if(layoutId == 0)
return this;
View v = LayoutInflater.from(getContext()).inflate(layoutId, null);
return contentView(v);
}
/**
* Sets whether this dialog is cancelable with the
* {@link android.view.KeyEvent#KEYCODE_BACK BACK} key.
* @return The Dialog for chaining methods.
*/
public Dialog cancelable(boolean cancelable){
super.setCancelable(cancelable);
mCancelable = cancelable;
return this;
}
/**
* Sets whether this dialog is canceled when touched outside the window's
* bounds. If setting to true, the dialog is set to be cancelable if not
* already set.
*
* @param cancel Whether the dialog should be canceled when touched outside
* @return The Dialog for chaining methods.
*/
public Dialog canceledOnTouchOutside(boolean cancel){
super.setCanceledOnTouchOutside(cancel);
mCanceledOnTouchOutside = cancel;
return this;
}
/**
* Set the margin between content view and Dialog border.
* @param margin The margin size in pixels.
* @return The Dialog for chaining methods.
*/
public Dialog contentMargin(int margin){
mCardView.setContentMargin(margin);
return this;
}
/**
* Set the margin between content view and Dialog border.
* @param left The left margin size in pixels.
* @param top The top margin size in pixels.
* @param right The right margin size in pixels.
* @param bottom The bottom margin size in pixels.
* @return The Dialog for chaining methods.
*/
public Dialog contentMargin(int left, int top, int right, int bottom){
mCardView.setContentMargin(left, top, right, bottom);
return this;
}
@Override
public void setCancelable(boolean flag) {
cancelable(flag);
}
@Override
public void setCanceledOnTouchOutside(boolean cancel) {
canceledOnTouchOutside(cancel);
}
@Override
public void setContentView(View v){
contentView(v);
}
@Override
public void setContentView(int layoutId){
contentView(layoutId);
}
@Override
public void setContentView(View v, ViewGroup.LayoutParams params) {
contentView(v);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
contentView(view);
}
@Override
protected void onStart() {
super.onStart();
mCardView.setVisibility(View.VISIBLE);
if(mInAnimationId != 0)
mCardView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mCardView.getViewTreeObserver().removeOnPreDrawListener(this);
Animation anim = AnimationUtils.loadAnimation(mCardView.getContext(), mInAnimationId);
mCardView.startAnimation(anim);
return false;
}
});
}
/**
* Dismiss Dialog immediately without showing out animation.
*/
public void dismissImmediately(){
super.dismiss();
if(mHandler != null)
mHandler.removeCallbacks(mDismissAction);
}
@Override
public void dismiss() {
if(!isShowing() || mDismissPending)
return;
if(mOutAnimationId != 0){
Animation anim = AnimationUtils.loadAnimation(mContainer.getContext(), mOutAnimationId);
anim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mDismissPending = true;
}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
mDismissPending = false;
mCardView.setVisibility(View.GONE);
mHandler.post(mDismissAction);
}
});
mCardView.startAnimation(anim);
}
else
mHandler.post(mDismissAction);
}
private class ContainerFrameLayout extends FrameLayout {
private boolean mClickOutside = false;
public ContainerFrameLayout(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
mCardView.measure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = (right - left - mCardView.getMeasuredWidth()) / 2;
int childTop = (bottom - top - mCardView.getMeasuredHeight()) / 2;
int childRight = childLeft + mCardView.getMeasuredWidth();
int childBottom = childTop + mCardView.getMeasuredHeight();
mCardView.layout(childLeft, childTop, childRight, childBottom);
}
private boolean isOutsideDialog(float x, float y){
return x < mCardView.getLeft() + mCardView.getPaddingLeft() || x > mCardView.getRight() - mCardView.getPaddingRight() || y < mCardView.getTop() + mCardView.getPaddingTop() || y > mCardView.getBottom() - mCardView.getPaddingBottom();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = super.onTouchEvent(event);
if(handled)
return true;
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
if(isOutsideDialog(event.getX(), event.getY())){
mClickOutside = true;
return true;
}
return false;
case MotionEvent.ACTION_MOVE:
return mClickOutside;
case MotionEvent.ACTION_CANCEL:
mClickOutside = false;
return false;
case MotionEvent.ACTION_UP:
if(mClickOutside && isOutsideDialog(event.getX(), event.getY())){
mClickOutside = false;
if(mCancelable && mCanceledOnTouchOutside)
dismiss();
return true;
}
return false;
}
return false;
}
}
private class DialogCardView extends CardView {
private Paint mDividerPaint;
private float mDividerPos = -1f;
private boolean mShowDivider = false;
private int mContentMarginLeft;
private int mContentMarginTop;
private int mContentMarginRight;
private int mContentMarginBottom;
private boolean mIsRtl = false;
public DialogCardView(Context context) {
super(context);
mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDividerPaint.setStyle(Paint.Style.STROKE);
setWillNotDraw(false);
}
public void setContentMargin(int margin){
setContentMargin(margin, margin, margin, margin);
}
public void setContentMargin(int left, int top, int right, int bottom){
mContentMarginLeft = left;
mContentMarginTop = top;
mContentMarginRight = right;
mContentMarginBottom = bottom;
}
public void setDividerColor(int color){
mDividerPaint.setColor(color);
invalidate();
}
public void setDividerHeight(int height){
mDividerPaint.setStrokeWidth(height);
invalidate();
}
public void setShowDivider(boolean show){
if(mShowDivider != show) {
mShowDivider = show;
invalidate();
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
boolean rtl = layoutDirection == LAYOUT_DIRECTION_RTL;
if(mIsRtl != rtl) {
mIsRtl = rtl;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){
int direction = mIsRtl ? TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR;
mTitle.setTextDirection(direction);
mPositiveAction.setTextDirection(direction);
mNegativeAction.setTextDirection(direction);
mNeutralAction.setTextDirection(direction);
}
requestLayout();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int paddingLeft = Math.max(mDialogHorizontalPadding, mCardView.getPaddingLeft());
int paddingRight = Math.max(mDialogHorizontalPadding, mCardView.getPaddingRight());
int paddingTop = Math.max(mDialogVerticalPadding, mCardView.getPaddingTop());
int paddingBottom = Math.max(mDialogVerticalPadding, mCardView.getPaddingBottom());
int maxWidth = widthSize - paddingLeft - paddingRight;
if(mMaxWidth > 0)
maxWidth = Math.min(maxWidth, mMaxWidth);
int maxHeight = heightSize - paddingTop - paddingBottom;
if(mMaxHeight > 0)
maxHeight = Math.min(maxHeight, mMaxHeight);
int width = mLayoutWidth == ViewGroup.LayoutParams.MATCH_PARENT ? maxWidth : mLayoutWidth;
int height = mLayoutHeight == ViewGroup.LayoutParams.MATCH_PARENT ? maxHeight : mLayoutHeight;
int widthMs;
int heightMs;
int titleWidth = 0;
int titleHeight = 0;
if(mTitle.getVisibility() == View.VISIBLE){
widthMs = MeasureSpec.makeMeasureSpec(width == ViewGroup.LayoutParams.WRAP_CONTENT ? maxWidth : width, MeasureSpec.AT_MOST);
heightMs = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
mTitle.measure(widthMs, heightMs);
titleWidth = mTitle.getMeasuredWidth();
titleHeight = mTitle.getMeasuredHeight();
}
int contentWidth = 0;
int contentHeight = 0;
if(mContent != null){
widthMs = MeasureSpec.makeMeasureSpec((width == ViewGroup.LayoutParams.WRAP_CONTENT ? maxWidth : width) - mContentMarginLeft - mContentMarginRight, MeasureSpec.AT_MOST);
heightMs = MeasureSpec.makeMeasureSpec(maxHeight - mContentMarginTop - mContentMarginBottom, MeasureSpec.AT_MOST);
mContent.measure(widthMs, heightMs);
contentWidth = mContent.getMeasuredWidth();
contentHeight = mContent.getMeasuredHeight();
}
int visibleActions = 0;
int positiveActionWidth = 0;
if(mPositiveAction.getVisibility() == View.VISIBLE){
widthMs = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
heightMs = MeasureSpec.makeMeasureSpec(mActionHeight, MeasureSpec.EXACTLY);
mPositiveAction.measure(widthMs, heightMs);
positiveActionWidth = mPositiveAction.getMeasuredWidth();
if(positiveActionWidth < mActionMinWidth){
mPositiveAction.measure(MeasureSpec.makeMeasureSpec(mActionMinWidth, MeasureSpec.EXACTLY), heightMs);
positiveActionWidth = mActionMinWidth;
}
visibleActions++;
}
int negativeActionWidth = 0;
if(mNegativeAction.getVisibility() == View.VISIBLE){
widthMs = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
heightMs = MeasureSpec.makeMeasureSpec(mActionHeight, MeasureSpec.EXACTLY);
mNegativeAction.measure(widthMs, heightMs);
negativeActionWidth = mNegativeAction.getMeasuredWidth();
if(negativeActionWidth < mActionMinWidth){
mNegativeAction.measure(MeasureSpec.makeMeasureSpec(mActionMinWidth, MeasureSpec.EXACTLY), heightMs);
negativeActionWidth = mActionMinWidth;
}
visibleActions++;
}
int neutralActionWidth = 0;
if(mNeutralAction.getVisibility() == View.VISIBLE){
widthMs = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
heightMs = MeasureSpec.makeMeasureSpec(mActionHeight, MeasureSpec.EXACTLY);
mNeutralAction.measure(widthMs, heightMs);
neutralActionWidth = mNeutralAction.getMeasuredWidth();
if(neutralActionWidth < mActionMinWidth){
mNeutralAction.measure(MeasureSpec.makeMeasureSpec(mActionMinWidth, MeasureSpec.EXACTLY), heightMs);
neutralActionWidth = mActionMinWidth;
}
visibleActions++;
}
int actionBarWidth = positiveActionWidth + negativeActionWidth + neutralActionWidth + mActionOuterPadding * 2 + mActionPadding_w * Math.max(0, visibleActions - 1);
if(width == ViewGroup.LayoutParams.WRAP_CONTENT)
width = Math.min(maxWidth, Math.max(titleWidth, Math.max(contentWidth + mContentMarginLeft + mContentMarginRight, actionBarWidth)));
mLayoutActionVertical = actionBarWidth > width;
// mLayoutActionVertical=false;
int nonContentHeight = titleHeight + (visibleActions > 0 ? mActionPadding_h : 0) + mContentMarginTop + mContentMarginBottom;
if(mLayoutActionVertical)
nonContentHeight += mActionOuterHeight * visibleActions;
else
nonContentHeight += (visibleActions > 0) ? mActionOuterHeight : 0;
if(height == ViewGroup.LayoutParams.WRAP_CONTENT)
height = Math.min(maxHeight, contentHeight + nonContentHeight);
if(mContent != null)
mContent.measure(MeasureSpec.makeMeasureSpec(width - mContentMarginLeft - mContentMarginRight, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height - nonContentHeight, MeasureSpec.EXACTLY));
setMeasuredDimension(width + getPaddingLeft() + getPaddingRight(), height + getPaddingTop() + getPaddingBottom());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childLeft = 0;
int childTop = 0;
int childRight = right - left;
int childBottom = bottom - top;
childLeft += getPaddingLeft();
childTop += getPaddingTop();
childRight -= getPaddingRight();
childBottom -= getPaddingBottom();
if(mTitle.getVisibility() == View.VISIBLE) {
if(mIsRtl)
mTitle.layout(childRight - mTitle.getMeasuredWidth(), childTop, childRight, childTop + mTitle.getMeasuredHeight());
else
mTitle.layout(childLeft, childTop, childLeft + mTitle.getMeasuredWidth(), childTop + mTitle.getMeasuredHeight());
childTop += mTitle.getMeasuredHeight();
}
boolean hasAction = mNeutralAction.getVisibility() == View.VISIBLE || mNegativeAction.getVisibility() == View.VISIBLE || mPositiveAction.getVisibility() == View.VISIBLE;
if(hasAction)
childBottom -= mActionPadding_h;
int temp = (mActionOuterHeight - mActionHeight) / 2;
if(hasAction){
if(mLayoutActionVertical){
if(mNeutralAction.getVisibility() == View.VISIBLE){
mNeutralAction.layout(childRight - mActionOuterPadding - mNeutralAction.getMeasuredWidth(), childBottom - mActionOuterHeight + temp, childRight - mActionOuterPadding, childBottom - temp);
childBottom -= mActionOuterHeight;
}
if(mNegativeAction.getVisibility() == View.VISIBLE){
mNegativeAction.layout(childRight - mActionOuterPadding - mNegativeAction.getMeasuredWidth(), childBottom - mActionOuterHeight + temp, childRight - mActionOuterPadding, childBottom - temp);
childBottom -= mActionOuterHeight;
}
if(mPositiveAction.getVisibility() == View.VISIBLE){
mPositiveAction.layout(childRight - mActionOuterPadding - mPositiveAction.getMeasuredWidth(), childBottom - mActionOuterHeight + temp, childRight - mActionOuterPadding, childBottom - temp);
childBottom -= mActionOuterHeight;
}
}
else{
int actionLeft = childLeft + mActionOuterPadding;
int actionRight = childRight - mActionOuterPadding;
int actionTop = childBottom - mActionOuterHeight + temp;
int actionBottom = childBottom - temp;
if(mIsRtl){
if (mPositiveAction.getVisibility() == View.VISIBLE) {
mPositiveAction.layout(actionLeft , actionTop, actionLeft + mPositiveAction.getMeasuredWidth(), actionBottom);
actionLeft += mPositiveAction.getMeasuredWidth() + mActionPadding_w;
}
if (mNegativeAction.getVisibility() == View.VISIBLE)
mNegativeAction.layout(actionLeft, actionTop, actionLeft + mNegativeAction.getMeasuredWidth(), actionBottom);
if (mNeutralAction.getVisibility() == View.VISIBLE)
mNeutralAction.layout(actionRight - mNeutralAction.getMeasuredWidth(), actionTop, actionRight, actionBottom);
}
else {
if (mPositiveAction.getVisibility() == View.VISIBLE) {
mPositiveAction.layout(actionRight - mPositiveAction.getMeasuredWidth(), actionTop, actionRight, actionBottom);
actionRight -= mPositiveAction.getMeasuredWidth() + mActionPadding_w;
}
if (mNegativeAction.getVisibility() == View.VISIBLE)
mNegativeAction.layout(actionRight - mNegativeAction.getMeasuredWidth(), actionTop, actionRight, actionBottom);
if (mNeutralAction.getVisibility() == View.VISIBLE)
mNeutralAction.layout(actionLeft, actionTop, actionLeft + mNeutralAction.getMeasuredWidth(), actionBottom);
}
childBottom -= mActionOuterHeight;
}
}
mDividerPos = childBottom - mDividerPaint.getStrokeWidth() / 2f;
if(mContent != null)
mContent.layout(childLeft + mContentMarginLeft, childTop + mContentMarginTop, childRight - mContentMarginRight, childBottom - mContentMarginBottom);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if(mShowDivider && (mPositiveAction.getVisibility() == View.VISIBLE || mNegativeAction.getVisibility() == View.VISIBLE || mNeutralAction.getVisibility() == View.VISIBLE))
canvas.drawLine(getPaddingLeft(), mDividerPos, getWidth() - getPaddingRight(), mDividerPos, mDividerPaint);
}
}
public static class Builder implements DialogFragment.Builder, Parcelable {
protected int mStyleId;
protected int mContentViewId;
protected CharSequence mTitle;
protected CharSequence mPositive;
protected CharSequence mNegative;
protected CharSequence mNeutral;
protected Dialog mDialog;
public Builder(){
this(R.style.Material_App_Dialog_Light);
}
public Builder(int styleId){
mStyleId = styleId;
}
public Builder style(int styleId){
mStyleId = styleId;
return this;
}
public Builder contentView(int layoutId){
mContentViewId = layoutId;
return this;
}
public Builder title(CharSequence title){
mTitle = title;
return this;
}
public Builder positiveAction(CharSequence action){
mPositive = action;
return this;
}
public Builder negativeAction(CharSequence action){
mNegative = action;
return this;
}
public Builder neutralAction(CharSequence action){
mNeutral = action;
return this;
}
public Dialog getDialog(){
return mDialog;
}
@Override
public void onPositiveActionClicked(DialogFragment fragment) {
fragment.dismiss();
}
@Override
public void onNegativeActionClicked(DialogFragment fragment) {
fragment.dismiss();
}
@Override
public void onNeutralActionClicked(DialogFragment fragment) {
fragment.dismiss();
}
@Override
public void onCancel(DialogInterface dialog) {}
@Override
public void onDismiss(DialogInterface dialog) {}
@Override
public Dialog build(Context context) {
mDialog = onBuild(context, mStyleId);
mDialog.title(mTitle)
.positiveAction(mPositive)
.negativeAction(mNegative)
.neutralAction(mNeutral);
if(mContentViewId != 0)
mDialog.contentView(mContentViewId);
onBuildDone(mDialog);
return mDialog;
}
/**
* Get a appropriate Dialog instance will be used for styling later.
* Child class should override this function to return appropriate Dialog instance.
* If you want to apply styling to dialog, or get content view, you should do it in {@link #onBuildDone(Dialog)}
* @param context A Context instance.
* @param styleId The resourceId of Dialog's style.
* @return A Dialog instance will be used for styling later.
*/
protected Dialog onBuild(Context context, int styleId){
return new Dialog(context, styleId);
}
/**
* This function will be called after Builder done apply styling to Dialog.
* @param dialog The Dialog instance.
*/
protected void onBuildDone(Dialog dialog){}
protected Builder(Parcel in) {
mStyleId = in.readInt();
mContentViewId = in.readInt();
mTitle = (CharSequence)in.readValue(null);
mPositive = (CharSequence)in.readValue(null);
mNegative = (CharSequence)in.readValue(null);
mNeutral = (CharSequence)in.readValue(null);
onReadFromParcel(in);
}
/**
* Child class should override this function and read back any saved attributes.
* All read methods should be called after super.onReadFromParcel() call to keep the order.
*/
protected void onReadFromParcel(Parcel in){}
/**
* Child class should override this function and write down all attributes will be saved.
* All write methods should be called after super.onWriteToParcel() call to keep the order.
*/
protected void onWriteToParcel(Parcel dest, int flags){}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mStyleId);
dest.writeInt(mContentViewId);
dest.writeValue(mTitle);
dest.writeValue(mPositive);
dest.writeValue(mNegative);
dest.writeValue(mNeutral);
onWriteToParcel(dest, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Builder> CREATOR = new Creator<Builder>() {
public Builder createFromParcel(Parcel in) {
return new Builder(in);
}
public Builder[] newArray(int size) {
return new Builder[size];
}
};
}
}