Prechádzať zdrojové kódy

完成自定义Crouton编写(自定义颜色,自定义延时时间)

RaoMeng 9 rokov pred
rodič
commit
f6868be5a8

+ 1 - 0
WeiChat/build.gradle

@@ -43,6 +43,7 @@ android {
 buildscript {
     repositories {
         mavenCentral() // or jcenter()
+
     }
 
     dependencies {

+ 3 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/base/BaseActivity.java

@@ -13,6 +13,7 @@ import com.xzjmyk.pm.activity.ui.erp.net.ViewUtil;
 import com.xzjmyk.pm.activity.ui.erp.view.CustomProgressDialog;
 import com.xzjmyk.pm.activity.util.Constants;
 import com.xzjmyk.pm.activity.util.PreferenceUtils;
+import com.xzjmyk.pm.activity.view.crouton.Crouton;
 import com.xzjmyk.pm.activity.volley.FastVolley;
 
 /**
@@ -64,6 +65,8 @@ public abstract class BaseActivity extends ActionBackActivity {
     protected void onDestroy() {
         // 取消所有HASHCODE包含该类名的request
         mFastVolley.cancelAll(HASHCODE);
+
+        Crouton.cancelAllCroutons();
         super.onDestroy();
     }
 

+ 6 - 4
WeiChat/src/main/java/com/xzjmyk/pm/activity/ui/erp/activity/crm/CustomerListActivity.java

@@ -15,13 +15,11 @@ import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
-import android.widget.Button;
 import android.widget.ListView;
 import android.widget.PopupWindow;
 import android.widget.SimpleAdapter;
 import android.widget.TextView;
 
-
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
@@ -38,6 +36,7 @@ import com.xzjmyk.pm.activity.ui.erp.net.ViewUtil;
 import com.xzjmyk.pm.activity.ui.erp.util.CommonUtil;
 import com.xzjmyk.pm.activity.ui.erp.util.Constants;
 import com.xzjmyk.pm.activity.util.DisplayUtil;
+import com.xzjmyk.pm.activity.view.crouton.Crouton;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -159,15 +158,18 @@ public class CustomerListActivity extends BaseActivity  implements View.OnClickL
         switch (v.getId()){
             case R.id.tv_count_unend:
                 type=0;
-                sendHttpResquest(Constants.HTTP_SUCCESS_INIT,kind,page,0,"");
+//                Crouton.makeText(this,"切换成功", 0xffff8888, 10000).show();
+                sendHttpResquest(Constants.HTTP_SUCCESS_INIT, kind, page, 0, "");
                 break;
             case R.id.tv_count_end:
                 type=1;
+//                Crouton.makeText(this,"切换成功", Style.CONFIRM).show();
                 sendHttpResquest(Constants.HTTP_SUCCESS_INIT,kind,page,0,"");
                 break;
             case R.id.tv_count_total:
                 type=2;
-                sendHttpResquest(Constants.HTTP_SUCCESS_INIT,kind,page,0,"");
+//                Crouton.makeText(this,"切换成功", Style.CONFIRM).show();
+                sendHttpResquest(Constants.HTTP_SUCCESS_INIT, kind, page, 0, "");
                 break;
         }
     }

+ 138 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/Configuration.java

@@ -0,0 +1,138 @@
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.xzjmyk.pm.activity.view.crouton;
+
+/**
+ * Allows configuring a {@link Crouton}s behaviour aside from the actual view,
+ * which is defined via {@link Style}.
+ * <p/>
+ * This allows to re-use a {@link Style} while modifying parameters that only have to be applied
+ * when the {@link Crouton} is being displayed.
+ *
+ * @author chris
+ * @since 1.8
+ */
+public class Configuration {
+
+
+    /**
+     * Display a {@link Crouton} for an infinite amount of time or
+     * until {@link Crouton#cancel()} has been called.
+     */
+    public static final int DURATION_INFINITE = -1;
+    /**
+     * The default short display duration of a {@link Crouton}.
+     */
+    public static final int DURATION_SHORT = 3000;
+    /**
+     * The default long display duration of a {@link Crouton}.
+     */
+    public static final int DURATION_LONG = 5000;
+
+    /**
+     * The default {@link Configuration} of a {@link Crouton}.
+     */
+    public static final Configuration DEFAULT;
+
+    static {
+        DEFAULT = new Builder().setDuration(DURATION_SHORT).build();
+    }
+
+    /**
+     * The durationInMilliseconds the {@link Crouton} will be displayed in milliseconds.
+     */
+    final int durationInMilliseconds;
+    /**
+     * The resource id for the in animation.
+     */
+    final int inAnimationResId;
+    /**
+     * The resource id for the out animation.
+     */
+    final int outAnimationResId;
+
+    private Configuration(Builder builder) {
+        this.durationInMilliseconds = builder.durationInMilliseconds;
+        this.inAnimationResId = builder.inAnimationResId;
+        this.outAnimationResId = builder.outAnimationResId;
+    }
+
+    /**
+     * Creates a {@link Builder} to build a {@link Configuration} upon.
+     */
+    public static class Builder {
+        private int durationInMilliseconds = DURATION_SHORT;
+        private int inAnimationResId = 0;
+        private int outAnimationResId = 0;
+
+        /**
+         * Set the durationInMilliseconds option of the {@link Crouton}.
+         *
+         * @param duration The durationInMilliseconds the crouton will be displayed
+         *                 {@link Crouton} in milliseconds.
+         * @return the {@link Builder}.
+         */
+        public Builder setDuration(final int duration) {
+            this.durationInMilliseconds = duration;
+
+            return this;
+        }
+
+        /**
+         * The resource id for the in animation.
+         *
+         * @param inAnimationResId The resource identifier for the animation that's being shown
+         *                         when the {@link Crouton} is going to be displayed.
+         * @return the {@link Builder}.
+         */
+        public Builder setInAnimation(final int inAnimationResId) {
+            this.inAnimationResId = inAnimationResId;
+
+            return this;
+        }
+
+        /**
+         * The resource id for the out animation
+         *
+         * @param outAnimationResId The resource identifier for the animation that's being shown
+         *                          when the {@link Crouton} is going to be removed.
+         * @return the {@link Builder}.
+         */
+        public Builder setOutAnimation(final int outAnimationResId) {
+            this.outAnimationResId = outAnimationResId;
+
+            return this;
+        }
+
+        /**
+         * Builds the {@link Configuration}.
+         *
+         * @return The built {@link Configuration}.
+         */
+        public Configuration build() {
+            return new Configuration(this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Configuration{" +
+                "durationInMilliseconds=" + durationInMilliseconds +
+                ", inAnimationResId=" + inAnimationResId +
+                ", outAnimationResId=" + outAnimationResId +
+                '}';
+    }
+}

+ 1057 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/Crouton.java

@@ -0,0 +1,1057 @@
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xzjmyk.pm.activity.view.crouton;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Shader;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+/*
+ * Based on an article by Cyril Mottier (http://android.cyrilmottier.com/?p=773) <br>
+ */
+
+
+/**
+ * Displays information in a non-invasive context related manner. Like
+ * {@link android.widget.Toast}, but better.
+ * <p/>
+ * <b>Important: </b>
+ * Call {@link Crouton#clearCroutonsForActivity(Activity)} within
+ * {@link Activity#onDestroy()} to avoid {@link Context} leaks.
+ */
+public final class Crouton {
+  private static final String NULL_PARAMETERS_ARE_NOT_ACCEPTED = "Null parameters are not accepted";
+  private static final int IMAGE_ID = 0x100;
+  private static final int TEXT_ID = 0x101;
+  private final CharSequence text;
+  private final Style style;
+  private Configuration configuration = null;
+  private final View customView;
+
+  private OnClickListener onClickListener;
+
+  private Activity activity;
+  private ViewGroup viewGroup;
+  private FrameLayout croutonView;
+  private Animation inAnimation;
+  private Animation outAnimation;
+  private LifecycleCallback lifecycleCallback = null;
+
+  /**
+   * Creates the {@link Crouton}.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should be attached
+   *     to.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   */
+  private Crouton(Activity activity, CharSequence text, Style style) {
+    if ((activity == null) || (text == null) || (style == null)) {
+      throw new IllegalArgumentException(NULL_PARAMETERS_ARE_NOT_ACCEPTED);
+    }
+
+    this.activity = activity;
+    this.viewGroup = null;
+    this.text = text;
+    this.style = style;
+    this.customView = null;
+  }
+
+  /**
+   * Creates the {@link Crouton}.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  private Crouton(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) {
+    if ((activity == null) || (text == null) || (style == null)) {
+      throw new IllegalArgumentException(NULL_PARAMETERS_ARE_NOT_ACCEPTED);
+    }
+
+    this.activity = activity;
+    this.text = text;
+    this.style = style;
+    this.viewGroup = viewGroup;
+    this.customView = null;
+  }
+
+  /**
+   * Creates the {@link Crouton}.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should be attached
+   *     to.
+   * @param customView
+   *     The custom {@link View} to display
+   */
+  private Crouton(Activity activity, View customView) {
+    if ((activity == null) || (customView == null)) {
+      throw new IllegalArgumentException(NULL_PARAMETERS_ARE_NOT_ACCEPTED);
+    }
+
+    this.activity = activity;
+    this.viewGroup = null;
+    this.customView = customView;
+    this.style = new Style.Builder().build();
+    this.text = null;
+  }
+
+  /**
+   * Creates the {@link Crouton}.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  private Crouton(Activity activity, View customView, ViewGroup viewGroup) {
+    this(activity, customView, viewGroup, Configuration.DEFAULT);
+  }
+
+  /**
+   * Creates the {@link Crouton}.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   * @param configuration
+   *     The {@link Configuration} for this {@link Crouton}.
+   */
+  private Crouton(final Activity activity, final View customView, final ViewGroup viewGroup,
+                  final Configuration configuration) {
+    if ((activity == null) || (customView == null)) {
+      throw new IllegalArgumentException(NULL_PARAMETERS_ARE_NOT_ACCEPTED);
+    }
+
+    this.activity = activity;
+    this.customView = customView;
+    this.viewGroup = viewGroup;
+    this.style = new Style.Builder().build();
+    this.text = null;
+    this.configuration = configuration;
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should be attached
+   *     to.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton makeText(Activity activity, CharSequence text, Style style) {
+    return new Crouton(activity, text, style);
+  }
+
+  /**
+   * 自定义设置背景色,延时时间
+   * @param activity
+   * @param text
+   * @param color
+   * @param time
+   * @return
+   */
+  public static Crouton makeText(Activity activity, CharSequence text, int color, int time){
+    Style customColor = new Style.Builder().setBackgroundColorValue(color).build();
+    Configuration customConfiguration = new Configuration.Builder().setDuration(time).build();
+    return new Crouton(activity, text, customColor).setConfiguration(customConfiguration);
+  }
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton makeText(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) {
+    return new Crouton(activity, text, style, viewGroup);
+  }
+  /**
+   * 自定义设置背景色,延时时间
+   * @param activity
+   * @param text
+   * @param color
+   * @param time
+   * @return
+   */
+  public static Crouton makeText(Activity activity, CharSequence text, int color, int time, ViewGroup viewGroup){
+    Style customColor = new Style.Builder().setBackgroundColorValue(color).build();
+    Configuration customConfiguration = new Configuration.Builder().setDuration(time).build();
+    return new Crouton(activity, text, customColor,viewGroup).setConfiguration(customConfiguration);
+  }
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton makeText(Activity activity, CharSequence text, Style style, int viewGroupResId) {
+    return new Crouton(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId));
+  }
+  /**
+   * 自定义设置背景色,延时时间
+   * @param activity
+   * @param text
+   * @param color
+   * @param time
+   * @return
+   */
+  public static Crouton makeText(Activity activity, CharSequence text, int color, int time, int viewGroupResId) {
+    Style customColor = new Style.Builder().setBackgroundColorValue(color).build();
+    Configuration customConfiguration = new Configuration.Builder().setDuration(time).build();
+    return new Crouton(activity, text, customColor, (ViewGroup) activity.findViewById(viewGroupResId)).setConfiguration(customConfiguration);
+  }
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should be attached
+   *     to.
+   * @param textResourceId
+   *     The resource id of the text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton makeText(Activity activity, int textResourceId, Style style) {
+    return makeText(activity, activity.getString(textResourceId), style);
+  }
+
+  /**
+   * 自定义设置背景色,延时时间
+   * @param activity
+   * @param textResourceId
+   * @param color
+   * @param time
+   * @return
+   */
+  public static Crouton makeText(Activity activity, int textResourceId, int color, int time) {
+    Style customColor = new Style.Builder().setBackgroundColorValue(color).build();
+    Configuration customConfiguration = new Configuration.Builder().setDuration(time).build();
+    return makeText(activity, activity.getString(textResourceId), customColor).setConfiguration(customConfiguration);
+  }
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param textResourceId
+   *     The resource id of the text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton makeText(Activity activity, int textResourceId, Style style, ViewGroup viewGroup) {
+    return makeText(activity, activity.getString(textResourceId), style, viewGroup);
+  }
+
+  /**
+   * 自定义设置背景色,延时时间
+   * @param activity
+   * @param textResourceId
+   * @param color
+   * @param time
+   * @param viewGroup
+   * @return
+   */
+  public static Crouton makeText(Activity activity, int textResourceId, int color, int time, ViewGroup viewGroup) {
+    Style customColor = new Style.Builder().setBackgroundColorValue(color).build();
+    Configuration customConfiguration = new Configuration.Builder().setDuration(time).build();
+    return makeText(activity, activity.getString(textResourceId), customColor, viewGroup).setConfiguration(customConfiguration);
+  }
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param textResourceId
+   *     The resource id of the text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton makeText(Activity activity, int textResourceId, Style style, int viewGroupResId) {
+    return makeText(activity, activity.getString(textResourceId), style,
+            (ViewGroup) activity.findViewById(viewGroupResId));
+  }
+
+  /**
+   * 自定义设置背景色,延时时间
+   * @param activity
+   * @param textResourceId
+   * @param color
+   * @param time
+   * @param viewGroupResId
+   * @return
+   */
+  public static Crouton makeText(Activity activity, int textResourceId, int color, int time, int viewGroupResId) {
+    Style customColor = new Style.Builder().setBackgroundColorValue(color).build();
+    Configuration customConfiguration = new Configuration.Builder().setDuration(time).build();
+    return makeText(activity, activity.getString(textResourceId), customColor,
+            (ViewGroup) activity.findViewById(viewGroupResId)).setConfiguration(customConfiguration);
+  }
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should be attached
+   *     to.
+   * @param customView
+   *     The custom {@link View} to display
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton make(Activity activity, View customView) {
+    return new Crouton(activity, customView);
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton make(Activity activity, View customView, ViewGroup viewGroup) {
+    return new Crouton(activity, customView, viewGroup);
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton make(Activity activity, View customView, int viewGroupResId) {
+    return new Crouton(activity, customView, (ViewGroup) activity.findViewById(viewGroupResId));
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   * @param configuration
+   *     The configuration for this crouton.
+   *
+   * @return The created {@link Crouton}.
+   */
+  public static Crouton make(Activity activity, View customView, int viewGroupResId,
+                             final Configuration configuration) {
+    return new Crouton(activity, customView, (ViewGroup) activity.findViewById(viewGroupResId), configuration);
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should
+   *     be attached to.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   */
+  public static void showText(Activity activity, CharSequence text, Style style) {
+    makeText(activity, text, style).show();
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  public static void showText(Activity activity, CharSequence text, Style style, ViewGroup viewGroup) {
+    makeText(activity, text, style, viewGroup).show();
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  public static void showText(Activity activity, CharSequence text, Style style, int viewGroupResId) {
+    makeText(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId)).show();
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param text
+   *     The text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   * @param configuration
+   *     The configuration for this Crouton.
+   */
+  public static void showText(Activity activity, CharSequence text, Style style, int viewGroupResId,
+                              final Configuration configuration) {
+    makeText(activity, text, style, (ViewGroup) activity.findViewById(viewGroupResId)).setConfiguration(configuration)
+        .show();
+  }
+
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should
+   *     be attached to.
+   * @param customView
+   *     The custom {@link View} to display
+   */
+  public static void show(Activity activity, View customView) {
+    make(activity, customView).show();
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  public static void show(Activity activity, View customView, ViewGroup viewGroup) {
+    make(activity, customView, viewGroup).show();
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text and style for a given activity
+   * and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param customView
+   *     The custom {@link View} to display
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  public static void show(Activity activity, View customView, int viewGroupResId) {
+    make(activity, customView, viewGroupResId).show();
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that the {@link Crouton} should be attached
+   *     to.
+   * @param textResourceId
+   *     The resource id of the text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   */
+  public static void showText(Activity activity, int textResourceId, Style style) {
+    showText(activity, activity.getString(textResourceId), style);
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param textResourceId
+   *     The resource id of the text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroup
+   *     The {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  public static void showText(Activity activity, int textResourceId, Style style, ViewGroup viewGroup) {
+    showText(activity, activity.getString(textResourceId), style, viewGroup);
+  }
+
+  /**
+   * Creates a {@link Crouton} with provided text-resource and style for a given
+   * activity and displays it directly.
+   *
+   * @param activity
+   *     The {@link Activity} that represents the context in which the Crouton should exist.
+   * @param textResourceId
+   *     The resource id of the text you want to display.
+   * @param style
+   *     The style that this {@link Crouton} should be created with.
+   * @param viewGroupResId
+   *     The resource id of the {@link ViewGroup} that this {@link Crouton} should be added to.
+   */
+  public static void showText(Activity activity, int textResourceId, Style style, int viewGroupResId) {
+    showText(activity, activity.getString(textResourceId), style, viewGroupResId);
+  }
+
+  /**
+   * Allows hiding of a previously displayed {@link Crouton}.
+   *
+   * @param crouton
+   *     The {@link Crouton} you want to hide.
+   */
+  public static void hide(Crouton crouton) {
+    crouton.hide();
+  }
+
+  /**
+   * Cancels all queued {@link Crouton}s. If there is a {@link Crouton}
+   * displayed currently, it will be the last one displayed.
+   */
+  public static void cancelAllCroutons() {
+    Manager.getInstance().clearCroutonQueue();
+  }
+
+  /**
+   * Clears (and removes from {@link Activity}'s content view, if necessary) all
+   * croutons for the provided activity
+   *
+   * @param activity
+   *     - The {@link Activity} to clear the croutons for.
+   */
+  public static void clearCroutonsForActivity(Activity activity) {
+    Manager.getInstance().clearCroutonsForActivity(activity);
+  }
+
+  /**
+   * Cancels a {@link Crouton} immediately.
+   */
+  public void cancel() {
+    Manager manager = Manager.getInstance();
+    manager.removeCroutonImmediately(this);
+  }
+
+  /**
+   * Displays the {@link Crouton}. If there's another {@link Crouton} visible at
+   * the time, this {@link Crouton} will be displayed afterwards.
+   */
+  public void show() {
+    Manager.getInstance().add(this);
+  }
+
+  public Animation getInAnimation() {
+    if ((null == this.inAnimation) && (null != this.activity)) {
+      if (getConfiguration().inAnimationResId > 0) {
+        this.inAnimation = AnimationUtils.loadAnimation(getActivity(), getConfiguration().inAnimationResId);
+      } else {
+        measureCroutonView();
+        this.inAnimation = DefaultAnimationsBuilder.buildDefaultSlideInDownAnimation(getView());
+      }
+    }
+
+    return inAnimation;
+  }
+
+  public Animation getOutAnimation() {
+    if ((null == this.outAnimation) && (null != this.activity)) {
+      if (getConfiguration().outAnimationResId > 0) {
+        this.outAnimation = AnimationUtils.loadAnimation(getActivity(), getConfiguration().outAnimationResId);
+      } else {
+        this.outAnimation = DefaultAnimationsBuilder.buildDefaultSlideOutUpAnimation(getView());
+      }
+    }
+
+    return outAnimation;
+  }
+
+  /**
+   * @param lifecycleCallback
+   *     Callback object for notable events in the life of a Crouton.
+   */
+  public void setLifecycleCallback(LifecycleCallback lifecycleCallback) {
+    this.lifecycleCallback = lifecycleCallback;
+  }
+
+  /**
+   * Removes this {@link Crouton}.
+   *
+   * @since 1.9
+   */
+  public void hide() {
+    Manager.getInstance().removeCrouton(this);
+  }
+
+  /**
+   * Allows setting of an {@link OnClickListener} directly to a {@link Crouton} without having to use a custom view.
+   *
+   * @param onClickListener
+   *     The {@link OnClickListener} to set.
+   *
+   * @return this {@link Crouton}.
+   */
+  public Crouton setOnClickListener(OnClickListener onClickListener) {
+    this.onClickListener = onClickListener;
+    return this;
+  }
+
+  /**
+   * Set the {@link Configuration} on this {@link Crouton}, prior to showing it.
+   *
+   * @param configuration
+   *     a {@link Configuration} built using the {@link Configuration.Builder}.
+   *
+   * @return this {@link Crouton}.
+   */
+  public Crouton setConfiguration(final Configuration configuration) {
+    this.configuration = configuration;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return "Crouton{" +
+        "text=" + text +
+        ", style=" + style +
+        ", configuration=" + configuration +
+        ", customView=" + customView +
+        ", onClickListener=" + onClickListener +
+        ", activity=" + activity +
+        ", viewGroup=" + viewGroup +
+        ", croutonView=" + croutonView +
+        ", inAnimation=" + inAnimation +
+        ", outAnimation=" + outAnimation +
+        ", lifecycleCallback=" + lifecycleCallback +
+        '}';
+  }
+
+  /**
+   * Convenience method to get the license text for embedding within your application.
+   *
+   * @return The license text.
+   */
+  public static String getLicenseText() {
+    return "This application uses the Crouton library.\n\n" +
+        "Copyright 2012 - 2013 Benjamin Weiss \n" +
+        "\n" +
+        "Licensed under the Apache License, Version 2.0 (the \"License\");\n" +
+        "you may not use this file except in compliance with the License.\n" +
+        "You may obtain a copy of the License at\n" +
+        "\n" +
+        "   http://www.apache.org/licenses/LICENSE-2.0\n" +
+        "\n" +
+        "Unless required by applicable law or agreed to in writing, software\n" +
+        "distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+        "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+        "See the License for the specific language governing permissions and\n" +
+        "limitations under the License.";
+  }
+
+  //////////////////////////////////////////////////////////////////////////////////////
+  // You have reached the internal API of Crouton.
+  // If you do not plan to develop for Crouton there is nothing of interest below here.
+  //////////////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * @return <code>true</code> if the {@link Crouton} is being displayed, else
+   * <code>false</code>.
+   */
+  boolean isShowing() {
+    return (null != activity) && (isCroutonViewNotNull() || isCustomViewNotNull());
+  }
+
+  private boolean isCroutonViewNotNull() {
+    return (null != croutonView) && (null != croutonView.getParent());
+  }
+
+  private boolean isCustomViewNotNull() {
+    return (null != customView) && (null != customView.getParent());
+  }
+
+  /**
+   * Removes the activity reference this {@link Crouton} is holding
+   */
+  void detachActivity() {
+    activity = null;
+  }
+
+  /**
+   * Removes the viewGroup reference this {@link Crouton} is holding
+   */
+  void detachViewGroup() {
+    viewGroup = null;
+  }
+
+  /**
+   * Removes the lifecycleCallback reference this {@link Crouton} is holding
+   */
+  void detachLifecycleCallback() {
+    lifecycleCallback = null;
+  }
+
+  /**
+   * @return the lifecycleCallback
+   */
+  LifecycleCallback getLifecycleCallback() {
+    return lifecycleCallback;
+  }
+
+  /**
+   * @return the style
+   */
+  Style getStyle() {
+    return style;
+  }
+
+  /**
+   * @return this croutons configuration
+   */
+  Configuration getConfiguration() {
+    if (null == configuration) {
+      configuration = getStyle().configuration;
+    }
+    return configuration;
+  }
+
+  /**
+   * @return the activity
+   */
+  Activity getActivity() {
+    return activity;
+  }
+
+  /**
+   * @return the viewGroup
+   */
+  ViewGroup getViewGroup() {
+    return viewGroup;
+  }
+
+  /**
+   * @return the text
+   */
+  CharSequence getText() {
+    return text;
+  }
+
+  /**
+   * @return the view
+   */
+  View getView() {
+    // return the custom view if one exists
+    if (null != this.customView) {
+      return this.customView;
+    }
+
+    // if already setup return the view
+    if (null == this.croutonView) {
+      initializeCroutonView();
+    }
+
+    return croutonView;
+  }
+
+  private void measureCroutonView() {
+    View view = getView();
+    int widthSpec;
+    if (null != viewGroup) {
+      widthSpec = View.MeasureSpec.makeMeasureSpec(viewGroup.getMeasuredWidth(), View.MeasureSpec.AT_MOST);
+    } else {
+      widthSpec = View.MeasureSpec.makeMeasureSpec(activity.getWindow().getDecorView().getMeasuredWidth(),
+          View.MeasureSpec.AT_MOST);
+    }
+
+    view.measure(widthSpec, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+  }
+
+  private void initializeCroutonView() {
+    Resources resources = this.activity.getResources();
+
+    this.croutonView = initializeCroutonViewGroup(resources);
+
+    // create content view
+    RelativeLayout contentView = initializeContentView(resources);
+    this.croutonView.addView(contentView);
+  }
+
+  private FrameLayout initializeCroutonViewGroup(Resources resources) {
+    FrameLayout croutonView = new FrameLayout(this.activity);
+
+    if (null != onClickListener) {
+      croutonView.setOnClickListener(onClickListener);
+    }
+
+    final int height;
+    if (this.style.heightDimensionResId > 0) {
+      height = resources.getDimensionPixelSize(this.style.heightDimensionResId);
+    } else {
+      height = this.style.heightInPixels;
+    }
+
+    final int width;
+    if (this.style.widthDimensionResId > 0) {
+      width = resources.getDimensionPixelSize(this.style.widthDimensionResId);
+    } else {
+      width = this.style.widthInPixels;
+    }
+
+    croutonView.setLayoutParams(
+        new FrameLayout.LayoutParams(width != 0 ? width : FrameLayout.LayoutParams.MATCH_PARENT, height));
+
+    // set background
+    if (this.style.backgroundColorValue != Style.NOT_SET) {
+      croutonView.setBackgroundColor(this.style.backgroundColorValue);
+    } else {
+      croutonView.setBackgroundColor(resources.getColor(this.style.backgroundColorResourceId));
+    }
+
+    // set the background drawable if set. This will override the background
+    // color.
+    if (this.style.backgroundDrawableResourceId != 0) {
+      Bitmap background = BitmapFactory.decodeResource(resources, this.style.backgroundDrawableResourceId);
+      BitmapDrawable drawable = new BitmapDrawable(resources, background);
+      if (this.style.isTileEnabled) {
+        drawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+      }
+      croutonView.setBackgroundDrawable(drawable);
+    }
+    return croutonView;
+  }
+
+  private RelativeLayout initializeContentView(final Resources resources) {
+    RelativeLayout contentView = new RelativeLayout(this.activity);
+    contentView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
+        RelativeLayout.LayoutParams.MATCH_PARENT));
+
+    // set padding
+    int padding = this.style.paddingInPixels;
+
+    // if a padding dimension has been set, this will overwrite any padding
+    // in pixels
+    if (this.style.paddingDimensionResId > 0) {
+      padding = resources.getDimensionPixelSize(this.style.paddingDimensionResId);
+    }
+    contentView.setPadding(padding, padding, padding, padding);
+
+    // only setup image if one is requested
+    ImageView image = null;
+    if ((null != this.style.imageDrawable) || (0 != this.style.imageResId)) {
+      image = initializeImageView();
+      contentView.addView(image, image.getLayoutParams());
+    }
+
+    TextView text = initializeTextView(resources);
+
+    RelativeLayout.LayoutParams textParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
+        RelativeLayout.LayoutParams.WRAP_CONTENT);
+    if (null != image) {
+      textParams.addRule(RelativeLayout.RIGHT_OF, image.getId());
+    }
+
+    if ((this.style.gravity & Gravity.CENTER) != 0) {
+      textParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+    } else if ((this.style.gravity & Gravity.CENTER_VERTICAL) != 0) {
+      textParams.addRule(RelativeLayout.CENTER_VERTICAL);
+    } else if ((this.style.gravity & Gravity.CENTER_HORIZONTAL) != 0) {
+      textParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
+    }
+
+    contentView.addView(text, textParams);
+    return contentView;
+  }
+
+  private TextView initializeTextView(final Resources resources) {
+    TextView text = new TextView(this.activity);
+    text.setId(TEXT_ID);
+    if (this.style.fontName != null) {
+      setTextWithCustomFont(text, this.style.fontName);
+    } else if (this.style.fontNameResId != 0) {
+      setTextWithCustomFont(text, resources.getString(this.style.fontNameResId));
+    } else {
+      text.setText(this.text);
+    }
+    text.setTypeface(Typeface.DEFAULT_BOLD);
+    text.setGravity(this.style.gravity);
+
+    // set the text color if set
+    if (this.style.textColorValue != Style.NOT_SET) {
+      text.setTextColor(this.style.textColorValue);
+    } else if (this.style.textColorResourceId != 0) {
+      text.setTextColor(resources.getColor(this.style.textColorResourceId));
+    }
+
+    // Set the text size. If the user has set a text size and text
+    // appearance, the text size in the text appearance
+    // will override this.
+    if (this.style.textSize != 0) {
+      text.setTextSize(TypedValue.COMPLEX_UNIT_SP, this.style.textSize);
+    }
+
+    // Setup the shadow if requested
+    if (this.style.textShadowColorResId != 0) {
+      initializeTextViewShadow(resources, text);
+    }
+
+    // Set the text appearance
+    if (this.style.textAppearanceResId != 0) {
+      text.setTextAppearance(this.activity, this.style.textAppearanceResId);
+    }
+    return text;
+  }
+
+  private void setTextWithCustomFont(TextView text, String fontName) {
+    if (this.text != null) {
+      SpannableString s = new SpannableString(this.text);
+      s.setSpan(new TypefaceSpan(text.getContext(), fontName), 0, s.length(),
+          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+      text.setText(s);
+    }
+  }
+
+  private void initializeTextViewShadow(final Resources resources, final TextView text) {
+    int textShadowColor = resources.getColor(this.style.textShadowColorResId);
+    float textShadowRadius = this.style.textShadowRadius;
+    float textShadowDx = this.style.textShadowDx;
+    float textShadowDy = this.style.textShadowDy;
+    text.setShadowLayer(textShadowRadius, textShadowDx, textShadowDy, textShadowColor);
+  }
+
+  private ImageView initializeImageView() {
+    ImageView image;
+    image = new ImageView(this.activity);
+    image.setId(IMAGE_ID);
+    image.setAdjustViewBounds(true);
+    image.setScaleType(this.style.imageScaleType);
+
+    // set the image drawable if not null
+    if (null != this.style.imageDrawable) {
+      image.setImageDrawable(this.style.imageDrawable);
+    }
+
+    // set the image resource if not 0. This will overwrite the drawable
+    // if both are set
+    if (this.style.imageResId != 0) {
+      image.setImageResource(this.style.imageResId);
+    }
+
+    RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(
+        RelativeLayout.LayoutParams.WRAP_CONTENT,
+        RelativeLayout.LayoutParams.WRAP_CONTENT);
+    imageParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
+    imageParams.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
+
+    image.setLayoutParams(imageParams);
+
+    return image;
+  }
+}

+ 88 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/DefaultAnimationsBuilder.java

@@ -0,0 +1,88 @@
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xzjmyk.pm.activity.view.crouton;
+
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+
+/** Builds the default animations for showing and hiding a {@link Crouton}. */
+final class DefaultAnimationsBuilder {
+  private static final long DURATION = 400;
+  private static Animation slideInDownAnimation, slideOutUpAnimation;
+  private static int lastInAnimationHeight, lastOutAnimationHeight;
+
+  private DefaultAnimationsBuilder() {
+    /* no-op */
+  }
+
+  /**
+   * @param croutonView
+   *   The croutonView which gets animated.
+   *
+   * @return The default Animation for a showing {@link Crouton}.
+   */
+  static Animation buildDefaultSlideInDownAnimation(View croutonView) {
+    if (!areLastMeasuredInAnimationHeightAndCurrentEqual(croutonView) || (null == slideInDownAnimation)) {
+      slideInDownAnimation = new TranslateAnimation(
+        0, 0,                               // X: from, to
+        -croutonView.getMeasuredHeight(), 0 // Y: from, to
+      );
+      slideInDownAnimation.setDuration(DURATION);
+      setLastInAnimationHeight(croutonView.getMeasuredHeight());
+    }
+    return slideInDownAnimation;
+  }
+
+  /**
+   * @param croutonView
+   *   The croutonView which gets animated.
+   *
+   * @return The default Animation for a hiding {@link Crouton}.
+   */
+  static Animation buildDefaultSlideOutUpAnimation(View croutonView) {
+    if (!areLastMeasuredOutAnimationHeightAndCurrentEqual(croutonView) || (null == slideOutUpAnimation)) {
+      slideOutUpAnimation = new TranslateAnimation(
+        0, 0,                               // X: from, to
+        0, -croutonView.getMeasuredHeight() // Y: from, to
+      );
+      slideOutUpAnimation.setDuration(DURATION);
+      setLastOutAnimationHeight(croutonView.getMeasuredHeight());
+    }
+    return slideOutUpAnimation;
+  }
+
+  private static boolean areLastMeasuredInAnimationHeightAndCurrentEqual(View croutonView) {
+    return areLastMeasuredAnimationHeightAndCurrentEqual(lastInAnimationHeight, croutonView);
+  }
+
+  private static boolean areLastMeasuredOutAnimationHeightAndCurrentEqual(View croutonView) {
+    return areLastMeasuredAnimationHeightAndCurrentEqual(lastOutAnimationHeight, croutonView);
+  }
+
+  private static boolean areLastMeasuredAnimationHeightAndCurrentEqual(int lastHeight, View croutonView) {
+    return lastHeight == croutonView.getMeasuredHeight();
+  }
+
+  private static void setLastInAnimationHeight(int lastInAnimationHeight) {
+    DefaultAnimationsBuilder.lastInAnimationHeight = lastInAnimationHeight;
+  }
+
+  private static void setLastOutAnimationHeight(int lastOutAnimationHeight) {
+    DefaultAnimationsBuilder.lastOutAnimationHeight = lastOutAnimationHeight;
+  }
+}

+ 28 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/LifecycleCallback.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xzjmyk.pm.activity.view.crouton;
+
+/** Provides callback methods on major lifecycle events of a {@link Crouton}. */
+public interface LifecycleCallback {
+  /** Will be called when your Crouton has been displayed. */
+  public void onDisplayed();
+
+  /** Will be called when your {@link Crouton} has been removed. */
+  public void onRemoved();
+
+  //public void onCeasarDressing();
+}

+ 480 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/Manager.java

@@ -0,0 +1,480 @@
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xzjmyk.pm.activity.view.crouton;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.AdapterView;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+import java.util.Iterator;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+
+/**
+ * Manages the lifecycle of {@link Crouton}s.
+ */
+final class Manager extends Handler {
+  private static final class Messages {
+    private Messages() { /* no-op */ }
+
+    public static final int DISPLAY_CROUTON = 0xc2007;
+    public static final int ADD_CROUTON_TO_VIEW = 0xc20074dd;
+    public static final int REMOVE_CROUTON = 0xc2007de1;
+  }
+
+  private static Manager INSTANCE;
+
+  private final Queue<Crouton> croutonQueue;
+
+  private Manager() {
+    croutonQueue = new LinkedBlockingQueue<Crouton>();
+  }
+
+  /**
+   * @return The currently used instance of the {@link Manager}.
+   */
+  static synchronized Manager getInstance() {
+    if (null == INSTANCE) {
+      INSTANCE = new Manager();
+    }
+
+    return INSTANCE;
+  }
+
+  /**
+   * Inserts a {@link Crouton} to be displayed.
+   *
+   * @param crouton
+   *     The {@link Crouton} to be displayed.
+   */
+  void add(Crouton crouton) {
+    croutonQueue.add(crouton);
+    displayCrouton();
+  }
+
+  /**
+   * Displays the next {@link Crouton} within the queue.
+   */
+  private void displayCrouton() {
+    if (croutonQueue.isEmpty()) {
+      return;
+    }
+
+    // First peek whether the Crouton has an activity.
+    final Crouton currentCrouton = croutonQueue.peek();
+
+    // If the activity is null we poll the Crouton off the queue.
+    if (null == currentCrouton.getActivity()) {
+      croutonQueue.poll();
+    }
+
+    if (!currentCrouton.isShowing()) {
+      // Display the Crouton
+      sendMessage(currentCrouton, Messages.ADD_CROUTON_TO_VIEW);
+      if (null != currentCrouton.getLifecycleCallback()) {
+        currentCrouton.getLifecycleCallback().onDisplayed();
+      }
+    } else {
+      sendMessageDelayed(currentCrouton, Messages.DISPLAY_CROUTON, calculateCroutonDuration(currentCrouton));
+    }
+  }
+
+  private long calculateCroutonDuration(Crouton crouton) {
+    long croutonDuration = crouton.getConfiguration().durationInMilliseconds;
+    croutonDuration += crouton.getInAnimation().getDuration();
+    croutonDuration += crouton.getOutAnimation().getDuration();
+    return croutonDuration;
+  }
+
+  /**
+   * Sends a {@link Crouton} within a {@link Message}.
+   *
+   * @param crouton
+   *     The {@link Crouton} that should be sent.
+   * @param messageId
+   *     The {@link Message} id.
+   */
+  private void sendMessage(Crouton crouton, final int messageId) {
+    final Message message = obtainMessage(messageId);
+    message.obj = crouton;
+    sendMessage(message);
+  }
+
+  /**
+   * Sends a {@link Crouton} within a delayed {@link Message}.
+   *
+   * @param crouton
+   *     The {@link Crouton} that should be sent.
+   * @param messageId
+   *     The {@link Message} id.
+   * @param delay
+   *     The delay in milliseconds.
+   */
+  private void sendMessageDelayed(Crouton crouton, final int messageId, final long delay) {
+    Message message = obtainMessage(messageId);
+    message.obj = crouton;
+    sendMessageDelayed(message, delay);
+  }
+
+  /*
+   * (non-Javadoc)
+   *
+   * @see android.os.Handler#handleMessage(android.os.Message)
+   */
+  @Override
+  public void handleMessage(Message message) {
+    final Crouton crouton = (Crouton) message.obj;
+    if (null == crouton) {
+      return;
+    }
+    switch (message.what) {
+      case Messages.DISPLAY_CROUTON: {
+        displayCrouton();
+        break;
+      }
+
+      case Messages.ADD_CROUTON_TO_VIEW: {
+        addCroutonToView(crouton);
+        break;
+      }
+
+      case Messages.REMOVE_CROUTON: {
+        removeCrouton(crouton);
+        if (null != crouton.getLifecycleCallback()) {
+          crouton.getLifecycleCallback().onRemoved();
+        }
+        break;
+      }
+
+      default: {
+        super.handleMessage(message);
+        break;
+      }
+    }
+  }
+
+  /**
+   * Adds a {@link Crouton} to the {@link ViewParent} of it's {@link Activity}.
+   *
+   * @param crouton
+   *     The {@link Crouton} that should be added.
+   */
+  private void addCroutonToView(final Crouton crouton) {
+    // don't add if it is already showing
+    if (crouton.isShowing()) {
+      return;
+    }
+
+    final View croutonView = crouton.getView();
+    if (null == croutonView.getParent()) {
+      ViewGroup.LayoutParams params = croutonView.getLayoutParams();
+      if (null == params) {
+        params =
+            new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+      }
+      // display Crouton in ViewGroup if it has been supplied
+      if (null != crouton.getViewGroup()) {
+        final ViewGroup croutonViewGroup = crouton.getViewGroup();
+        if (shouldAddViewWithoutPosition(croutonViewGroup)) {
+          croutonViewGroup.addView(croutonView, params);
+        } else {
+          croutonViewGroup.addView(croutonView, 0, params);
+        }
+      } else {
+        Activity activity = crouton.getActivity();
+        if (null == activity || activity.isFinishing()) {
+          return;
+        }
+        handleTranslucentActionBar((ViewGroup.MarginLayoutParams) params, activity);
+        handleActionBarOverlay((ViewGroup.MarginLayoutParams) params, activity);
+
+        activity.addContentView(croutonView, params);
+      }
+    }
+
+    croutonView.requestLayout(); // This is needed so the animation can use the measured with/height
+    ViewTreeObserver observer = croutonView.getViewTreeObserver();
+    if (null != observer) {
+      observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+        @Override
+        @TargetApi(16)
+        public void onGlobalLayout() {
+          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+            croutonView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+          } else {
+            croutonView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+          }
+
+          if(crouton.getInAnimation() != null) {
+            croutonView.startAnimation(crouton.getInAnimation());
+            announceForAccessibilityCompat(crouton.getActivity(), crouton.getText());
+            if (Configuration.DURATION_INFINITE != crouton.getConfiguration().durationInMilliseconds) {
+              sendMessageDelayed(crouton, Messages.REMOVE_CROUTON,
+                  crouton.getConfiguration().durationInMilliseconds + crouton.getInAnimation().getDuration());
+            }
+          }
+        }
+      });
+    }
+  }
+
+  private boolean shouldAddViewWithoutPosition(ViewGroup croutonViewGroup) {
+    return croutonViewGroup instanceof FrameLayout || croutonViewGroup instanceof AdapterView ||
+        croutonViewGroup instanceof RelativeLayout;
+  }
+
+  @TargetApi(19)
+  private void handleTranslucentActionBar(ViewGroup.MarginLayoutParams params, Activity activity) {
+    // Translucent status is only available as of Android 4.4 Kit Kat.
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      final int flags = activity.getWindow().getAttributes().flags;
+      final int translucentStatusFlag = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
+      if ((flags & translucentStatusFlag) == translucentStatusFlag) {
+        setActionBarMargin(params, activity);
+      }
+    }
+  }
+
+  @TargetApi(11)
+  private void handleActionBarOverlay(ViewGroup.MarginLayoutParams params, Activity activity) {
+    // ActionBar overlay is only available as of Android 3.0 Honeycomb.
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+      final boolean flags = activity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+      if (flags) {
+        setActionBarMargin(params, activity);
+      }
+    }
+  }
+
+  private void setActionBarMargin(ViewGroup.MarginLayoutParams params, Activity activity) {
+    final int actionBarContainerId = Resources.getSystem().getIdentifier("action_bar_container", "id", "android");
+    final View actionBarContainer = activity.findViewById(actionBarContainerId);
+    // The action bar is present: the app is using a Holo theme.
+    if (null != actionBarContainer) {
+      params.topMargin = actionBarContainer.getBottom();
+    }
+  }
+
+  /**
+   * Removes the {@link Crouton}'s view after it's display
+   * durationInMilliseconds.
+   *
+   * @param crouton
+   *     The {@link Crouton} added to a {@link ViewGroup} and should be
+   *     removed.
+   */
+  protected void removeCrouton(Crouton crouton) {
+    // If the crouton hasn't been displayed yet a `Crouton.hide()` will fail to hide
+    // it since the DISPLAY message might still be in the queue. Remove all messages
+    // for this crouton.
+    removeAllMessagesForCrouton(crouton);
+
+    View croutonView = crouton.getView();
+    ViewGroup croutonParentView = (ViewGroup) croutonView.getParent();
+
+    if (null != croutonParentView) {
+      croutonView.startAnimation(crouton.getOutAnimation());
+
+      // Remove the Crouton from the queue.
+      Crouton removed = croutonQueue.poll();
+
+      // Remove the crouton from the view's parent.
+      croutonParentView.removeView(croutonView);
+      if (null != removed) {
+        removed.detachActivity();
+        removed.detachViewGroup();
+        if (null != removed.getLifecycleCallback()) {
+          removed.getLifecycleCallback().onRemoved();
+        }
+        removed.detachLifecycleCallback();
+      }
+
+      // Send a message to display the next crouton but delay it by the out
+      // animation duration to make sure it finishes
+      sendMessageDelayed(crouton, Messages.DISPLAY_CROUTON, crouton.getOutAnimation().getDuration());
+    }
+  }
+
+  /**
+   * Removes a {@link Crouton} immediately, even when it's currently being
+   * displayed.
+   *
+   * @param crouton
+   *     The {@link Crouton} that should be removed.
+   */
+  void removeCroutonImmediately(Crouton crouton) {
+    // if Crouton has already been displayed then it may not be in the queue (because it was popped).
+    // This ensures the displayed Crouton is removed from its parent immediately, whether another instance
+    // of it exists in the queue or not.
+    // Note: crouton.isShowing() is false here even if it really is showing, as croutonView object in
+    // Crouton seems to be out of sync with reality!
+    if (null != crouton.getActivity() && null != crouton.getView() && null != crouton.getView().getParent()) {
+      ((ViewGroup) crouton.getView().getParent()).removeView(crouton.getView());
+
+      // remove any messages pending for the crouton
+      removeAllMessagesForCrouton(crouton);
+    }
+    // remove any matching croutons from queue
+    final Iterator<Crouton> croutonIterator = croutonQueue.iterator();
+    while (croutonIterator.hasNext()) {
+      final Crouton c = croutonIterator.next();
+      if (c.equals(crouton) && (null != c.getActivity())) {
+        // remove the crouton from the content view
+        removeCroutonFromViewParent(crouton);
+
+        // remove any messages pending for the crouton
+        removeAllMessagesForCrouton(c);
+
+        // remove the crouton from the queue
+        croutonIterator.remove();
+
+        // we have found our crouton so just break
+        break;
+      }
+    }
+  }
+
+  /**
+   * Removes all {@link Crouton}s from the queue.
+   */
+  void clearCroutonQueue() {
+    removeAllMessages();
+
+    // remove any views that may already have been added to the activity's
+    // content view
+    for (Crouton crouton : croutonQueue) {
+      removeCroutonFromViewParent(crouton);
+    }
+    croutonQueue.clear();
+  }
+
+  /**
+   * Removes all {@link Crouton}s for the provided activity. This will remove
+   * crouton from {@link Activity}s content view immediately.
+   */
+  void clearCroutonsForActivity(Activity activity) {
+    Iterator<Crouton> croutonIterator = croutonQueue.iterator();
+    while (croutonIterator.hasNext()) {
+      Crouton crouton = croutonIterator.next();
+      if ((null != crouton.getActivity()) && crouton.getActivity().equals(activity)) {
+        // remove the crouton from the content view
+        removeCroutonFromViewParent(crouton);
+
+        removeAllMessagesForCrouton(crouton);
+
+        // remove the crouton from the queue
+        croutonIterator.remove();
+      }
+    }
+  }
+
+  private void removeCroutonFromViewParent(Crouton crouton) {
+    if (crouton.isShowing()) {
+      ViewGroup parent = (ViewGroup) crouton.getView().getParent();
+      if (null != parent) {
+        parent.removeView(crouton.getView());
+      }
+    }
+  }
+
+  private void removeAllMessages() {
+    removeMessages(Messages.ADD_CROUTON_TO_VIEW);
+    removeMessages(Messages.DISPLAY_CROUTON);
+    removeMessages(Messages.REMOVE_CROUTON);
+  }
+
+  private void removeAllMessagesForCrouton(Crouton crouton) {
+    removeMessages(Messages.ADD_CROUTON_TO_VIEW, crouton);
+    removeMessages(Messages.DISPLAY_CROUTON, crouton);
+    removeMessages(Messages.REMOVE_CROUTON, crouton);
+
+  }
+
+  /**
+   * Generates and dispatches an SDK-specific spoken announcement.
+   * <p>
+   * For backwards compatibility, we're constructing an event from scratch
+   * using the appropriate event type. If your application only targets SDK
+   * 16+, you can just call View.announceForAccessibility(CharSequence).
+   * </p>
+   * <p/>
+   * note: AccessibilityManager is only available from API lvl 4.
+   * <p/>
+   * Adapted from https://http://eyes-free.googlecode.com/files/accessibility_codelab_demos_v2_src.zip
+   * via https://github.com/coreform/android-formidable-validation
+   *
+   * @param context
+   *     Used to get {@link AccessibilityManager}
+   * @param text
+   *     The text to announce.
+   */
+  public static void announceForAccessibilityCompat(Context context, CharSequence text) {
+    if (Build.VERSION.SDK_INT >= 4) {
+      AccessibilityManager accessibilityManager = null;
+      if (null != context) {
+        accessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+      }
+      if (null == accessibilityManager || !accessibilityManager.isEnabled()) {
+        return;
+      }
+
+      // Prior to SDK 16, announcements could only be made through FOCUSED
+      // events. Jelly Bean (SDK 16) added support for speaking text verbatim
+      // using the ANNOUNCEMENT event type.
+      final int eventType;
+      if (Build.VERSION.SDK_INT < 16) {
+        eventType = AccessibilityEvent.TYPE_VIEW_FOCUSED;
+      } else {
+        eventType = AccessibilityEvent.TYPE_ANNOUNCEMENT;
+      }
+
+      // Construct an accessibility event with the minimum recommended
+      // attributes. An event without a class name or package may be dropped.
+      final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+      event.getText().add(text);
+      event.setClassName(Manager.class.getName());
+      event.setPackageName(context.getPackageName());
+
+      // Sends the event directly through the accessibility manager. If your
+      // application only targets SDK 14+, you should just call
+      // getParent().requestSendAccessibilityEvent(this, event);
+      accessibilityManager.sendAccessibilityEvent(event);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "Manager{" +
+        "croutonQueue=" + croutonQueue +
+        '}';
+  }
+}

+ 565 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/Style.java

@@ -0,0 +1,565 @@
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xzjmyk.pm.activity.view.crouton;
+
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+
+
+/** The style for a {@link Crouton}. */
+public class Style {
+  
+  public static final int NOT_SET = -1;
+
+  public static final int holoRedLight = 0xffff4444;
+  public static final int holoGreenLight = 0xff99cc00;
+  public static final int holoBlueLight = 0xff33b5e5;
+
+  /** Default style for alerting the user. */
+  public static final Style ALERT;
+  /** Default style for confirming an action. */
+  public static final Style CONFIRM;
+  /** Default style for general information. */
+  public static final Style INFO;
+
+  static {
+    ALERT = new Builder()
+      .setBackgroundColorValue(holoRedLight)
+      .build();
+    CONFIRM = new Builder()
+      .setBackgroundColorValue(holoGreenLight)
+      .build();
+    INFO = new Builder()
+      .setBackgroundColorValue(holoBlueLight)
+      .build();
+  }
+
+  /**
+   * The {@link Configuration} for this {@link Style}.
+   * It can be overridden via {@link Crouton#setConfiguration(Configuration)}.
+   */
+  final Configuration configuration;
+
+  /**
+   * The resource id of the backgroundResourceId.
+   * <p/>
+   * 0 for no backgroundResourceId.
+   */
+  final int backgroundColorResourceId;
+
+  /**
+   * The resource id of the backgroundDrawableResourceId.
+   * <p/>
+   * 0 for no backgroundDrawableResourceId.
+   */
+  final int backgroundDrawableResourceId;
+
+  /**
+   * The backgroundColorResourceValue's e.g. 0xffff4444;
+   * <p/>
+   * NOT_SET for no value.
+   */
+  final int backgroundColorValue;
+
+  /** Whether we should isTileEnabled the backgroundResourceId or not. */
+  final boolean isTileEnabled;
+
+  /**
+   * The text colorResourceId's resource id.
+   * <p/>
+   * 0 sets the text colorResourceId to the system theme default.
+   */
+  final int textColorResourceId;
+
+  /**
+   * The textColorResourceValue's e.g. 0xffff4444;
+   * <p/>
+   * NOT_SET for no value.
+   */
+  final int textColorValue;
+
+  /** The height of the {@link Crouton} in pixels. */
+  final int heightInPixels;
+
+  /** Resource ID for the height of the {@link Crouton}. */
+  final int heightDimensionResId;
+
+  /** The width of the {@link Crouton} in pixels. */
+  final int widthInPixels;
+
+  /** Resource ID for the width of the {@link Crouton}. */
+  final int widthDimensionResId;
+
+  /** The text's gravity as provided by {@link Gravity}. */
+  final int gravity;
+
+  /** An additional image to display in the {@link Crouton}. */
+  final Drawable imageDrawable;
+
+  /** An additional image to display in the {@link Crouton}. */
+  final int imageResId;
+
+  /** The {@link ImageView.ScaleType} for the image to display in the {@link Crouton}. */
+  final ImageView.ScaleType imageScaleType;
+
+  /**
+   * The text size in sp
+   * <p/>
+   * 0 sets the text size to the system theme default
+   */
+  final int textSize;
+
+  /** The text shadow color's resource id */
+  final int textShadowColorResId;
+
+  /** The text shadow radius */
+  final float textShadowRadius;
+
+  /** The text shadow vertical offset */
+  final float textShadowDy;
+
+  /** The text shadow horizontal offset */
+  final float textShadowDx;
+
+  /** The text appearance resource id for the text. */
+  final int textAppearanceResId;
+
+  /** The padding for the crouton view content in pixels */
+  final int paddingInPixels;
+
+  /** The resource id for the padding for the view content */
+  final int paddingDimensionResId;
+
+  /** The file path and font name for the view content */
+  final String fontName;
+
+  /** The file path and font name resource id for the view content */
+  final int fontNameResId;
+
+  private Style(final Builder builder) {
+    this.configuration = builder.configuration;
+    this.backgroundColorResourceId = builder.backgroundColorResourceId;
+    this.backgroundDrawableResourceId = builder.backgroundDrawableResourceId;
+    this.isTileEnabled = builder.isTileEnabled;
+    this.textColorResourceId = builder.textColorResourceId;
+    this.textColorValue = builder.textColorValue;
+    this.heightInPixels = builder.heightInPixels;
+    this.heightDimensionResId = builder.heightDimensionResId;
+    this.widthInPixels = builder.widthInPixels;
+    this.widthDimensionResId = builder.widthDimensionResId;
+    this.gravity = builder.gravity;
+    this.imageDrawable = builder.imageDrawable;
+    this.textSize = builder.textSize;
+    this.textShadowColorResId = builder.textShadowColorResId;
+    this.textShadowRadius = builder.textShadowRadius;
+    this.textShadowDx = builder.textShadowDx;
+    this.textShadowDy = builder.textShadowDy;
+    this.textAppearanceResId = builder.textAppearanceResId;
+    this.imageResId = builder.imageResId;
+    this.imageScaleType = builder.imageScaleType;
+    this.paddingInPixels = builder.paddingInPixels;
+    this.paddingDimensionResId = builder.paddingDimensionResId;
+    this.backgroundColorValue = builder.backgroundColorValue;
+    this.fontName = builder.fontName;
+    this.fontNameResId = builder.fontNameResId;
+  }
+
+  /** Builder for the {@link Style} object. */
+  public static class Builder {
+    private Configuration configuration;
+    private int backgroundColorValue;
+    private int backgroundColorResourceId;
+    private int backgroundDrawableResourceId;
+    private boolean isTileEnabled;
+    private int textColorResourceId;
+    private int textColorValue;
+    private int heightInPixels;
+    private int heightDimensionResId;
+    private int widthInPixels;
+    private int widthDimensionResId;
+    private int gravity;
+    private Drawable imageDrawable;
+    private int textSize;
+    private int textShadowColorResId;
+    private float textShadowRadius;
+    private float textShadowDx;
+    private float textShadowDy;
+    private int textAppearanceResId;
+    private int imageResId;
+    private ImageView.ScaleType imageScaleType;
+    private int paddingInPixels;
+    private int paddingDimensionResId;
+    private String fontName;
+    private int fontNameResId;
+
+    /** Creates a {@link Builder} to build a {@link Style} upon. */
+    public Builder() {
+      configuration = Configuration.DEFAULT;
+      paddingInPixels = 10;
+      backgroundColorResourceId = android.R.color.holo_blue_light;
+      backgroundDrawableResourceId = 0;
+      backgroundColorValue = NOT_SET;
+      isTileEnabled = false;
+      textColorResourceId = android.R.color.white;
+      textColorValue = NOT_SET;
+//      heightInPixels = LayoutParams.WRAP_CONTENT;
+      heightInPixels = 100;
+      widthInPixels = LayoutParams.MATCH_PARENT;
+      gravity = Gravity.CENTER;
+      imageDrawable = null;
+      imageResId = 0;
+      imageScaleType = ImageView.ScaleType.FIT_XY;
+      fontName = null;
+      fontNameResId = 0;
+    }
+
+    /**
+     * Creates a {@link Builder} to build a {@link Style} upon.
+     *
+     * @param baseStyle
+     *   The base {@link Style} to use for this {@link Style}.
+     */
+    public Builder(final Style baseStyle) {
+      configuration = baseStyle.configuration;
+      backgroundColorValue = baseStyle.backgroundColorValue;
+      backgroundColorResourceId = baseStyle.backgroundColorResourceId;
+      backgroundDrawableResourceId = baseStyle.backgroundDrawableResourceId;
+      isTileEnabled = baseStyle.isTileEnabled;
+      textColorResourceId = baseStyle.textColorResourceId;
+      textColorValue = baseStyle.textColorValue;
+      heightInPixels = baseStyle.heightInPixels;
+      heightDimensionResId = baseStyle.heightDimensionResId;
+      widthInPixels = baseStyle.widthInPixels;
+      widthDimensionResId = baseStyle.widthDimensionResId;
+      gravity = baseStyle.gravity;
+      imageDrawable = baseStyle.imageDrawable;
+      textSize = baseStyle.textSize;
+      textShadowColorResId = baseStyle.textShadowColorResId;
+      textShadowRadius = baseStyle.textShadowRadius;
+      textShadowDx = baseStyle.textShadowDx;
+      textShadowDy = baseStyle.textShadowDy;
+      textAppearanceResId = baseStyle.textAppearanceResId;
+      imageResId = baseStyle.imageResId;
+      imageScaleType = baseStyle.imageScaleType;
+      paddingInPixels = baseStyle.paddingInPixels;
+      paddingDimensionResId = baseStyle.paddingDimensionResId;
+      fontName = baseStyle.fontName;
+      fontNameResId = baseStyle.fontNameResId;
+    }
+    /**
+     * Set the {@link Configuration} option of the {@link Crouton}.
+     *
+     * @param configuration
+     *   The {@link Configuration}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setConfiguration(Configuration configuration) {
+      this.configuration = configuration;
+      return this;
+    }
+
+    /**
+     * Set the backgroundColorResourceId option of the {@link Crouton}.
+     *
+     * @param backgroundColorResourceId
+     *   The backgroundColorResourceId's resource id.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setBackgroundColor(int backgroundColorResourceId) {
+      this.backgroundColorResourceId = backgroundColorResourceId;
+
+      return this;
+    }
+
+    /**
+     * Set the backgroundColorResourceValue option of the {@link Crouton}.
+     *
+     * @param backgroundColorValue
+     *   The backgroundColorResourceValue's e.g. 0xffff4444;
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setBackgroundColorValue(int backgroundColorValue) {
+      this.backgroundColorValue = backgroundColorValue;
+      return this;
+    }
+
+    /**
+     * Set the backgroundDrawableResourceId option for the {@link Crouton}.
+     *
+     * @param backgroundDrawableResourceId
+     *   Resource ID of a backgroundDrawableResourceId image drawable.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setBackgroundDrawable(int backgroundDrawableResourceId) {
+      this.backgroundDrawableResourceId = backgroundDrawableResourceId;
+
+      return this;
+    }
+
+    /**
+     * Set the heightInPixels option for the {@link Crouton}.
+     *
+     * @param height
+     *   The height of the {@link Crouton} in pixel. Can also be
+     *   {@link LayoutParams#MATCH_PARENT} or
+     *   {@link LayoutParams#WRAP_CONTENT}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setHeight(int height) {
+      this.heightInPixels = height;
+
+      return this;
+    }
+
+    /**
+     * Set the resource id for the height option for the {@link Crouton}.
+     *
+     * @param heightDimensionResId
+     *   Resource ID of a dimension for the height of the {@link Crouton}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setHeightDimensionResId(int heightDimensionResId) {
+      this.heightDimensionResId = heightDimensionResId;
+
+      return this;
+    }
+
+    /**
+     * Set the widthInPixels option for the {@link Crouton}.
+     *
+     * @param width
+     *   The width of the {@link Crouton} in pixel. Can also be
+     *   {@link LayoutParams#MATCH_PARENT} or
+     *   {@link LayoutParams#WRAP_CONTENT}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setWidth(int width) {
+      this.widthInPixels = width;
+
+      return this;
+    }
+
+    /**
+     * Set the resource id for the width option for the {@link Crouton}.
+     *
+     * @param widthDimensionResId
+     *   Resource ID of a dimension for the width of the {@link Crouton}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setWidthDimensionResId(int widthDimensionResId) {
+      this.widthDimensionResId = widthDimensionResId;
+
+      return this;
+    }
+
+    /**
+     * Set the isTileEnabled option for the {@link Crouton}.
+     *
+     * @param isTileEnabled
+     *   <code>true</code> if you want the backgroundResourceId to be
+     *   tiled, else <code>false</code>.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setTileEnabled(boolean isTileEnabled) {
+      this.isTileEnabled = isTileEnabled;
+
+      return this;
+    }
+
+    /**
+     * Set the textColorResourceId option for the {@link Crouton}.
+     *
+     * @param textColor
+     *   The resource id of the text colorResourceId.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setTextColor(int textColor) {
+      this.textColorResourceId = textColor;
+
+      return this;
+    }
+
+    /**
+     * Set the textColorResourceValue option of the {@link Crouton}.
+     *
+     * @param textColorValue
+     *   The textColorResourceValue's e.g. 0xffff4444;
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setTextColorValue(int textColorValue) {
+      this.textColorValue = textColorValue;
+      return this;
+    }
+
+    /**
+     * Set the gravity option for the {@link Crouton}.
+     *
+     * @param gravity
+     *   The text's gravity as provided by {@link Gravity}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setGravity(int gravity) {
+      this.gravity = gravity;
+
+      return this;
+    }
+
+    /**
+     * Set the image option for the {@link Crouton}.
+     *
+     * @param imageDrawable
+     *   An additional image to display in the {@link Crouton}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setImageDrawable(Drawable imageDrawable) {
+      this.imageDrawable = imageDrawable;
+
+      return this;
+    }
+
+    /**
+     * Set the image resource option for the {@link Crouton}.
+     *
+     * @param imageResId
+     *   An additional image to display in the {@link Crouton}.
+     *
+     * @return the {@link Builder}.
+     */
+    public Builder setImageResource(int imageResId) {
+      this.imageResId = imageResId;
+
+      return this;
+    }
+
+    /** The text size in sp. */
+    public Builder setTextSize(int textSize) {
+      this.textSize = textSize;
+      return this;
+    }
+
+    /** The text shadow color resource id. */
+    public Builder setTextShadowColor(int textShadowColorResId) {
+      this.textShadowColorResId = textShadowColorResId;
+      return this;
+    }
+
+    /** The text shadow radius. */
+    public Builder setTextShadowRadius(float textShadowRadius) {
+      this.textShadowRadius = textShadowRadius;
+      return this;
+    }
+
+    /** The text shadow horizontal offset. */
+    public Builder setTextShadowDx(float textShadowDx) {
+      this.textShadowDx = textShadowDx;
+      return this;
+    }
+
+    /** The text shadow vertical offset. */
+    public Builder setTextShadowDy(float textShadowDy) {
+      this.textShadowDy = textShadowDy;
+      return this;
+    }
+
+    /** The text appearance resource id for the text. */
+    public Builder setTextAppearance(int textAppearanceResId) {
+      this.textAppearanceResId = textAppearanceResId;
+      return this;
+    }
+
+    /** The {@link ImageView.ScaleType} for the image. */
+    public Builder setImageScaleType(ImageView.ScaleType imageScaleType) {
+      this.imageScaleType = imageScaleType;
+      return this;
+    }
+
+    /** The padding for the crouton view's content in pixels. */
+    public Builder setPaddingInPixels(int padding) {
+      this.paddingInPixels = padding;
+      return this;
+    }
+
+    /** The resource id for the padding for the crouton view's content. */
+    public Builder setPaddingDimensionResId(int paddingResId) {
+      this.paddingDimensionResId = paddingResId;
+      return this;
+    }
+
+    /** The file path and name of the font for the crouton view's content. */
+    public Builder setFontName(String fontName) {
+      this.fontName = fontName;
+      return this;
+    }
+
+    /** The resource id for the file path and name of the font for the crouton view's content. */
+    public Builder setFontNameResId(int fontNameResId) {
+      this.fontNameResId = fontNameResId;
+      return this;
+    }
+
+    /** @return a configured {@link Style} object. */
+    public Style build() {
+      return new Style(this);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "Style{" +
+      "configuration=" + configuration +
+      ", backgroundColorResourceId=" + backgroundColorResourceId +
+      ", backgroundDrawableResourceId=" + backgroundDrawableResourceId +
+      ", backgroundColorValue=" + backgroundColorValue +
+      ", isTileEnabled=" + isTileEnabled +
+      ", textColorResourceId=" + textColorResourceId +
+      ", textColorValue=" + textColorValue +
+      ", heightInPixels=" + heightInPixels +
+      ", heightDimensionResId=" + heightDimensionResId +
+      ", widthInPixels=" + widthInPixels +
+      ", widthDimensionResId=" + widthDimensionResId +
+      ", gravity=" + gravity +
+      ", imageDrawable=" + imageDrawable +
+      ", imageResId=" + imageResId +
+      ", imageScaleType=" + imageScaleType +
+      ", textSize=" + textSize +
+      ", textShadowColorResId=" + textShadowColorResId +
+      ", textShadowRadius=" + textShadowRadius +
+      ", textShadowDy=" + textShadowDy +
+      ", textShadowDx=" + textShadowDx +
+      ", textAppearanceResId=" + textAppearanceResId +
+      ", paddingInPixels=" + paddingInPixels +
+      ", paddingDimensionResId=" + paddingDimensionResId +
+      ", fontName=" + fontName +
+      ", fontNameResId=" + fontNameResId +
+      '}';
+  }
+}

+ 59 - 0
WeiChat/src/main/java/com/xzjmyk/pm/activity/view/crouton/TypefaceSpan.java

@@ -0,0 +1,59 @@
+
+/*
+ * Copyright 2012 - 2014 Benjamin Weiss
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xzjmyk.pm.activity.view.crouton;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.support.v4.util.LruCache;
+import android.text.TextPaint;
+import android.text.style.MetricAffectingSpan;
+
+/**
+ * Style a spannable with a custom {@link Typeface}.
+ */
+public class TypefaceSpan extends MetricAffectingSpan {
+  /** An <code>LruCache</code> for previously loaded typefaces. */
+  private static LruCache<String, Typeface> sTypefaceCache = new LruCache<String, Typeface>(5);
+
+  private Typeface mTypeface;
+
+  /**
+   * Load the {@link Typeface} and apply to a spannable.
+   */
+  public TypefaceSpan(Context context, String typefaceName) {
+    mTypeface = sTypefaceCache.get(typefaceName);
+
+    if (mTypeface == null) {
+      mTypeface = Typeface.createFromAsset(context.getApplicationContext()
+          .getAssets(), String.format("%s", typefaceName));
+
+      // Cache the loaded Typeface
+      sTypefaceCache.put(typefaceName, mTypeface);
+    }
+  }
+
+  @Override
+  public void updateMeasureState(TextPaint p) {
+    p.setTypeface(mTypeface);
+  }
+
+  @Override
+  public void updateDrawState(TextPaint tp) {
+    tp.setTypeface(mTypeface);
+  }
+}

+ 1 - 1
build.gradle

@@ -6,7 +6,7 @@ buildscript {
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:2.1.0'
-
+        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
     }
 }