打造通用的PopupWindow

国际惯例,先上地址

https://github.com/razerdp/BasePopup

PS:效果图都放在了github,github有着我继承该类做出来的popupWindow

//2016-01-15 目前只写了两个PopupWindow

效果图:

普通的放大缩小:

打造通用的PopupWindow_第1张图片

从下方弹出:

打造通用的PopupWindow_第2张图片



通常情况下,面对各种浮动窗口,选择窗口什么的,我们通常都是使用popupWindow,但是很多时候我们都希望popupWindow可以在弹出的时候带有动画,但是就popup本身而言,使用的动画是在是太不舒服不自由了。


通常情况下,我们弄个popupWindow的动画都是这么玩的

 popupWindow.setAnimationStyle(R.style.PopMenuAnimation);
我们还得去styles.xml弄弄我们的进入/退出动画,这多不自然啊,而且说好的控制呢对吧


于是这次我们就来打造一个通用的popupWindow,让我们可以随心自由的设置我们的popupWindow


这次我们要实现的popupWindow起码要实现以下几个要求{

  1.   自由的定义样式
  2.   便利的动画实现
  3.   可扩展
  4.   代码简洁易懂
}

好的,说了那么多,接下来我们就开工。

开工之前,我们先谈谈要求和实现方法吧:

  第一点,自由的定义样式,popupWindow在new出来的时候参数里面有一个参数是View,这意味着popupWindow本身就支持添加view(其实楼主我一直都把popupWindow看作一个浮动的viewGroup)

  第二点,便利的动画实现,开头说过,自带的popupWindow动画实现是在不舒服,于是我们打算这么做,动画由我们自己来指定,popupWindow只需要播放就好了。在第一点我说过,我把popupWindow看作一个浮动的viewGroup,既然有了viewGroup,那就意味着必定有view对吧,有了view,那就意味着必定有view的animation对吧,于是第二点的初步构造就出来了,popupWindow包裹着viewGroup,viewGroup里面的view(或者viewGroup)播放动画,实现我们的第二点需求。

  第三点,可扩展,可扩展意味着我们可以轻易的继承父类从而实现我们各种各样的popup,比如listPopup,inputPopup甚至是含有viewpager的popup。那么显然,我们需要一个抽象类,作为顶级父类,并限定子类规则,防止不可预料的问题。
  
  第四点,嗯。。。。。看个人代码风格吧
   ps:楼主是个注释/分块 狂魔


正文:

我们首先创建一个abstract class,作为顶级父类,取名叫BasePopup就好了。
首先我们需要一个popupWindow,作为一个popup用来浮动在当前的activity上面,然后需要一个view,作为popup的整体,然后就是一些参数设置什么的,比如是否需要输入之类的,这是后话。

于是我们就有了下面的一段代码:
[java]  view plain  copy
 print ?
  1. public abstract class BasePopupWindow implements ViewCreate {  
  2.     private static final String TAG = "BasePopupWindow";  
  3.     //元素定义  
  4.     protected PopupWindow mPopupWindow;  
  5.     //popup视图  
  6.     protected View mPopupView;  
  7.     protected Activity mContext;  
  8.     //是否自动弹出输入框(default:false)  
  9.     private boolean autoShowInputMethod = false;  
  10.     private OnDismissListener mOnDismissListener;  
  11.   
  12.     public BasePopupWindow(Activity context) {  
  13.         mContext = context;  
  14.   
  15.         mPopupView = getPopupView();  
  16.         mPopupView.setFocusable(true);  
  17.         mPopupView.setFocusableInTouchMode(true);  
  18.         //默认占满全屏  
  19.         mPopupWindow =  
  20.             new PopupWindow(mPopupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);  
  21.         //指定透明背景,点击外面相关  
  22.         mPopupWindow.setBackgroundDrawable(new ColorDrawable());  
  23.         mPopupWindow.setFocusable(true);  
  24.         mPopupWindow.setOutsideTouchable(true);  
  25.         //无需动画  
  26.         mPopupWindow.setAnimationStyle(0);  
  27.     }  
  28.   
  29.     public BasePopupWindow(Activity context, int w, int h) {  
  30.         mContext = context;  
  31.   
  32.         mPopupView = getPopupView();  
  33.         mPopupView.setFocusable(true);  
  34.         mPopupView.setFocusableInTouchMode(true);  
  35.         //默认占满全屏  
  36.         mPopupWindow = new PopupWindow(mPopupView, w, h);  
  37.         //指定透明背景,点击外面相关  
  38.         mPopupWindow.setBackgroundDrawable(new ColorDrawable());  
  39.         mPopupWindow.setFocusable(true);  
  40.         mPopupWindow.setOutsideTouchable(true);  
  41.         //无需动画  
  42.         mPopupWindow.setAnimationStyle(0);  
  43.     }  

设置两个构造器是为了扩展,因为默认状态下我们的popup是全屏显示,但是特殊的popup呢,最经典的例子,微信朋友圈的点赞/评论弹出来的那个东东,我们可以用popup来做,但很明显我们不可以用全屏的popup对吧,于是就有了第二个构造器

从代码我们看到我们实现了一个接口,这个接口只提供两个方法,具体如下
[java]  view plain  copy
 print ?
  1. /** 
  2.  * Created by 大灯泡 on 2016/1/14. 
  3.  */  
  4. public interface ViewCreate {  
  5.      View getPopupView();  
  6.      View getAnimaView();  
  7.   
  8. }  

getPopupView,在构造器我们可以看到,popupWindow的contentView就是通过这个得到
getAnimaView,明显用来播放动画用的


 基本构造有了,接下来就是对子类的限定
[java]  view plain  copy
 print ?
  1. //------------------------------------------抽象-----------------------------------------------  
  2. public abstract Animation getAnimation();  
  3. public abstract AnimationSet getAnimationSet();  
  4. public abstract View getInputView();  

期中getAnimation提供给子类用来设置播放动画,AnimationSet也是,但是AnimationSet是使用ObjectAnima的,也就是物理上改变的view参数,Animation只是改变视觉,对于物理事件(比如点击事件)的位置是不涉及改变的。


getInputView用于获取需要输入内容的popup里面的输入框,用于自动弹出输入法。

对子类规则定好后,接下来就是实现show方法。

对于popup我们都知道有一个showAtLocation方法来展示popup,我们这里也使用这个。
[java]  view plain  copy
 print ?
  1. //------------------------------------------showPopup-----------------------------------------------  
  2. public void showPopupWindow() {  
  3.     try {  
  4.         tryToShowPopup(0null);  
  5.     } catch (Exception e) {  
  6.         Log.e(TAG, "show error");  
  7.         e.printStackTrace();  
  8.     }  
  9. }  
  10.   
  11. public void showPopupWindow(int res) {  
  12.     try {  
  13.         tryToShowPopup(res, null);  
  14.     } catch (Exception e) {  
  15.         Log.e(TAG, "show error");  
  16.         e.printStackTrace();  
  17.     }  
  18. }  
  19.   
  20. public void showPopupWindow(View v) {  
  21.     try {  
  22.         tryToShowPopup(0, v);  
  23.     } catch (Exception e) {  
  24.         Log.e(TAG, "show error");  
  25.         e.printStackTrace();  
  26.     }  
  27. }  
  28.   
  29. //------------------------------------------Methods-----------------------------------------------  
  30. private void tryToShowPopup(int res, View v) throws Exception {  
  31.     //传递了view  
  32.     if (res == 0 && v != null) {  
  33.         mPopupWindow.showAtLocation(v, Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 00);  
  34.     }  
  35.     //传递了res  
  36.     if (res != 0 && v == null) {  
  37.         mPopupWindow.showAtLocation(mContext.findViewById(res), Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 00);  
  38.     }  
  39.     //什么都没传递,取顶级view的id  
  40.     if (res == 0 && v == null) {  
  41.         mPopupWindow.showAtLocation(mContext.findViewById(android.R.id.content),  
  42.                                     Gravity.RIGHT | Gravity.CENTER_HORIZONTAL,  
  43.                                     0,  
  44.                                     0  
  45.         );  
  46.     }  
  47.     if (getAnimation() != null && getAnimaView() != null) {  
  48.         getAnimaView().startAnimation(getAnimation());  
  49.     }  
  50.     //ViewHelper.setPivotX是包nineoldAndroid的方法,用于兼容低版本的anima以及方便的view工具  
  51.     if (getAnimation() == null && getAnimationSet() != null && getAnimaView() != null &&  
  52.             Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {  
  53.         ViewHelper.setPivotX(getAnimaView(), getAnimaView().getMeasuredWidth() / 2.0f);  
  54.         ViewHelper.setPivotY(getAnimaView(), getAnimaView().getMeasuredHeight() / 2.0f);  
  55.         getAnimationSet().start();  
  56.     }  
  57.     //自动弹出键盘  
  58.     if (autoShowInputMethod && getInputView() != null) {  
  59.         InputMethodUtils.showInputMethod(getInputView(), 150);  
  60.     }  
  61. }  




相关注释都写上了,这里就不解释
附上InputMethodUtils的代码:
150ms后弹出软键盘是为了给窗体绘制时间。
[java]  view plain  copy
 print ?
  1. /** 
  2.  * Created by 大灯泡 on 2016/1/14. 
  3.  * 显示键盘d工具类 
  4.  */  
  5. public class InputMethodUtils {  
  6.     /** 显示软键盘 */  
  7.     public static void showInputMethod(View view) {  
  8.         InputMethodManager imm = (InputMethodManager) view.getContext()  
  9.                                                           .getSystemService(Context.INPUT_METHOD_SERVICE);  
  10.         if (imm != null) {  
  11.             imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);  
  12.         }  
  13.     }  
  14.   
  15.     /** 显示软键盘 */  
  16.     public static void showInputMethod(Context context) {  
  17.         InputMethodManager imm = (InputMethodManager) context  
  18.             .getSystemService(Context.INPUT_METHOD_SERVICE);  
  19.         imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);  
  20.     }  
  21.   
  22.     /** 多少时间后显示软键盘 */  
  23.     public static void showInputMethod(final View view, long delayMillis) {  
  24.         // 显示输入法  
  25.         new Handler().postDelayed(new Runnable() {  
  26.   
  27.             @Override  
  28.             public void run() {  
  29.                 InputMethodUtils.showInputMethod(view);  
  30.             }  
  31.         }, delayMillis);  
  32.     }  
  33. }  

最后就是一些Setter/Getter和接口方法了
总体代码:


BasePopup.java:
[java]  view plain  copy
 print ?
  1. package razerdp.basepopup.basepopup;  
  2.   
  3. import android.app.Activity;  
  4. import android.graphics.drawable.ColorDrawable;  
  5. import android.os.Build;  
  6. import android.util.Log;  
  7. import android.view.Gravity;  
  8. import android.view.View;  
  9. import android.view.ViewGroup;  
  10. import android.view.WindowManager;  
  11. import android.view.animation.AccelerateInterpolator;  
  12. import android.view.animation.AlphaAnimation;  
  13. import android.view.animation.Animation;  
  14. import android.view.animation.AnimationSet;  
  15. import android.view.animation.ScaleAnimation;  
  16. import android.view.animation.TranslateAnimation;  
  17. import android.widget.PopupWindow;  
  18. import com.nineoldandroids.view.ViewHelper;  
  19. import razerdp.basepopup.utils.InputMethodUtils;  
  20.   
  21. /** 
  22.  * Created by 大灯泡 on 2016/1/14. 
  23.  * 通用的popupWindow 
  24.  */  
  25. public abstract class BasePopupWindow implements ViewCreate {  
  26.     private static final String TAG = "BasePopupWindow";  
  27.     //元素定义  
  28.     protected PopupWindow mPopupWindow;  
  29.     //popup视图  
  30.     protected View mPopupView;  
  31.     protected Activity mContext;  
  32.     //是否自动弹出输入框(default:false)  
  33.     private boolean autoShowInputMethod = false;  
  34.     private OnDismissListener mOnDismissListener;  
  35.   
  36.     public BasePopupWindow(Activity context) {  
  37.         mContext = context;  
  38.   
  39.         mPopupView = getPopupView();  
  40.         mPopupView.setFocusable(true);  
  41.         mPopupView.setFocusableInTouchMode(true);  
  42.         //默认占满全屏  
  43.         mPopupWindow =  
  44.             new PopupWindow(mPopupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);  
  45.         //指定透明背景,back键相关  
  46.         mPopupWindow.setBackgroundDrawable(new ColorDrawable());  
  47.         mPopupWindow.setFocusable(true);  
  48.         mPopupWindow.setOutsideTouchable(true);  
  49.         //无需动画  
  50.         mPopupWindow.setAnimationStyle(0);  
  51.     }  
  52.   
  53.     public BasePopupWindow(Activity context, int w, int h) {  
  54.         mContext = context;  
  55.   
  56.         mPopupView = getPopupView();  
  57.         mPopupView.setFocusable(true);  
  58.         mPopupView.setFocusableInTouchMode(true);  
  59.         //默认占满全屏  
  60.         mPopupWindow = new PopupWindow(mPopupView, w, h);  
  61.         //指定透明背景,back键相关  
  62.         mPopupWindow.setBackgroundDrawable(new ColorDrawable());  
  63.         mPopupWindow.setFocusable(true);  
  64.         mPopupWindow.setOutsideTouchable(true);  
  65.         //无需动画  
  66.         mPopupWindow.setAnimationStyle(0);  
  67.     }  
  68.   
  69.     //------------------------------------------抽象-----------------------------------------------  
  70.     public abstract Animation getAnimation();  
  71.     public abstract AnimationSet getAnimationSet();  
  72.     public abstract View getInputView();  
  73.   
  74.     //------------------------------------------showPopup-----------------------------------------------  
  75.     public void showPopupWindow() {  
  76.         try {  
  77.             tryToShowPopup(0null);  
  78.         } catch (Exception e) {  
  79.             Log.e(TAG, "show error");  
  80.             e.printStackTrace();  
  81.         }  
  82.     }  
  83.   
  84.     public void showPopupWindow(int res) {  
  85.         try {  
  86.             tryToShowPopup(res, null);  
  87.         } catch (Exception e) {  
  88.             Log.e(TAG, "show error");  
  89.             e.printStackTrace();  
  90.         }  
  91.     }  
  92.   
  93.     public void showPopupWindow(View v) {  
  94.         try {  
  95.             tryToShowPopup(0, v);  
  96.         } catch (Exception e) {  
  97.             Log.e(TAG, "show error");  
  98.             e.printStackTrace();  
  99.         }  
  100.     }  
  101.   
  102.     //------------------------------------------Methods-----------------------------------------------  
  103.     private void tryToShowPopup(int res, View v) throws Exception {  
  104.         //传递了view  
  105.         if (res == 0 && v != null) {  
  106.             mPopupWindow.showAtLocation(v, Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 00);  
  107.         }  
  108.         //传递了res  
  109.         if (res != 0 && v == null) {  
  110.             mPopupWindow.showAtLocation(mContext.findViewById(res), Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 00);  
  111.         }  
  112.         //什么都没传递,取顶级view的id  
  113.         if (res == 0 && v == null) {  
  114.             mPopupWindow.showAtLocation(mContext.findViewById(android.R.id.content),  
  115.                                         Gravity.RIGHT | Gravity.CENTER_HORIZONTAL,  
  116.                                         0,  
  117.                                         0  
  118.             );  
  119.         }  
  120.         if (getAnimation() != null && getAnimaView() != null) {  
  121.             getAnimaView().startAnimation(getAnimation());  
  122.         }  
  123.         //ViewHelper.setPivotX是包nineoldAndroid的方法,用于兼容低版本的anima以及方便的view工具  
  124.         if (getAnimation() == null && getAnimationSet() != null && getAnimaView() != null &&  
  125.                 Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {  
  126.             ViewHelper.setPivotX(getAnimaView(), getAnimaView().getMeasuredWidth() / 2.0f);  
  127.             ViewHelper.setPivotY(getAnimaView(), getAnimaView().getMeasuredHeight() / 2.0f);  
  128.             getAnimationSet().start();  
  129.         }  
  130.         //自动弹出键盘  
  131.         if (autoShowInputMethod && getInputView() != null) {  
  132.             InputMethodUtils.showInputMethod(getInputView(), 150);  
  133.         }  
  134.     }  
  135.   
  136.     public void setAdjustInputMethod(boolean needAdjust) {  
  137.         if (needAdjust) {  
  138.             mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);  
  139.         } else {  
  140.             mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);  
  141.         }  
  142.     }  
  143.   
  144.     public void setAutoShowInputMethod(boolean autoShow) {  
  145.         this.autoShowInputMethod = autoShow;  
  146.         if (autoShow) {  
  147.             setAdjustInputMethod(true);  
  148.         } else {  
  149.             setAdjustInputMethod(false);  
  150.         }  
  151.     }  
  152.     public void setBackPressEnable(boolean backPressEnable){  
  153.         if (backPressEnable){  
  154.             mPopupWindow.setBackgroundDrawable(new ColorDrawable());  
  155.         }else {  
  156.             mPopupWindow.setBackgroundDrawable(null);  
  157.         }  
  158.     }  
  159.   
  160.     //------------------------------------------Getter/Setter-----------------------------------------------  
  161.     public boolean isShowing() {  
  162.         return mPopupWindow.isShowing();  
  163.     }  
  164.   
  165.     public OnDismissListener getOnDismissListener() {  
  166.         return mOnDismissListener;  
  167.     }  
  168.   
  169.     public void setOnDismissListener(OnDismissListener onDismissListener) {  
  170.         mOnDismissListener = onDismissListener;  
  171.         if (mOnDismissListener!=null){  
  172.             mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {  
  173.                 @Override  
  174.                 public void onDismiss() {  
  175.                     mOnDismissListener.onDismiss();  
  176.                 }  
  177.             });  
  178.         }  
  179.     }  
  180.   
  181.     //------------------------------------------状态控制-----------------------------------------------  
  182.     public void dismiss() {  
  183.         try {  
  184.             mPopupWindow.dismiss();  
  185.         } catch (Exception e) {  
  186.             Log.d(TAG, "dismiss error");  
  187.         }  
  188.     }  
  189.     //------------------------------------------Anima-----------------------------------------------  
  190.     /** 
  191.      * 生成TranslateAnimation 
  192.      * @param durationMillis 动画显示时间 
  193.      * @param start 初始位置 
  194.      */  
  195.     protected Animation getTranslateAnimation(int start, int end, int durationMillis) {  
  196.         Animation translateAnimation = new TranslateAnimation(00, start, end);  
  197.         translateAnimation.setDuration(durationMillis);  
  198.         translateAnimation.setFillEnabled(true);  
  199.         translateAnimation.setFillAfter(true);  
  200.         return translateAnimation;  
  201.     }  
  202.   
  203.     /** 
  204.      * 生成ScaleAnimation 
  205.      */  
  206.     protected Animation getScaleAnimation(float fromX, float toX, float fromY, float toY,  
  207.         int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {  
  208.         Animation scaleAnimation =  
  209.             new ScaleAnimation(fromX, toX, fromY, toY, pivotXType, pivotXValue, pivotYType,  
  210.                                pivotYValue);  
  211.         scaleAnimation.setDuration(300);  
  212.         scaleAnimation.setFillEnabled(true);  
  213.         scaleAnimation.setFillAfter(true);  
  214.         return scaleAnimation;  
  215.     }  
  216.   
  217.     /** 
  218.      * 生成自定义ScaleAnimation 
  219.      */  
  220.     protected Animation getDefaultScaleAnimation() {  
  221.         Animation scaleAnimation =  
  222.             new ScaleAnimation(0f, 1f, 0f, 1f, Animation.RELATIVE_TO_SELF, 0.5f,  
  223.                                Animation.RELATIVE_TO_SELF, 0.5f);  
  224.         scaleAnimation.setDuration(300);  
  225.         scaleAnimation.setInterpolator(new AccelerateInterpolator());  
  226.         scaleAnimation.setFillEnabled(true);  
  227.         scaleAnimation.setFillAfter(true);  
  228.         return scaleAnimation;  
  229.     }  
  230.     /** 
  231.      * 生成默认的AlphaAnimation 
  232.      * */  
  233.     protected Animation getDefaultAlphaAnimation() {  
  234.         Animation alphaAnimation =  
  235.             new AlphaAnimation(0.0f, 1.0f);  
  236.         alphaAnimation.setDuration(300);  
  237.         alphaAnimation.setInterpolator(new AccelerateInterpolator());  
  238.         alphaAnimation.setFillEnabled(true);  
  239.         alphaAnimation.setFillAfter(true);  
  240.         return alphaAnimation;  
  241.     }  
  242.   
  243.     //------------------------------------------Interface-----------------------------------------------  
  244.     public interface OnDismissListener {  
  245.         void onDismiss();  
  246.     }  
  247. }  



ViewCreate.java:
[java]  view plain  copy
 print ?
  1. import android.view.View;  
  2.   
  3. /** 
  4.  * Created by 大灯泡 on 2016/1/14. 
  5.  */  
  6. public interface ViewCreate {  
  7.      View getPopupView();  
  8.      View getAnimaView();  
  9.   
  10. }  

InputMethodUtils.java:
[java]  view plain  copy
 print ?
  1. import android.content.Context;  
  2. import android.os.Handler;  
  3. import android.view.View;  
  4. import android.view.inputmethod.InputMethodManager;  
  5.   
  6. /** 
  7.  * Created by 大灯泡 on 2016/1/14. 
  8.  * 显示键盘d工具类 
  9.  */  
  10. public class InputMethodUtils {  
  11.     /** 显示软键盘 */  
  12.     public static void showInputMethod(View view) {  
  13.         InputMethodManager imm = (InputMethodManager) view.getContext()  
  14.                                                           .getSystemService(Context.INPUT_METHOD_SERVICE);  
  15.         if (imm != null) {  
  16.             imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);  
  17.         }  
  18.     }  
  19.   
  20.     /** 显示软键盘 */  
  21.     public static void showInputMethod(Context context) {  
  22.         InputMethodManager imm = (InputMethodManager) context  
  23.             .getSystemService(Context.INPUT_METHOD_SERVICE);  
  24.         imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);  
  25.     }  
  26.   
  27.     /** 多少时间后显示软键盘 */  
  28.     public static void showInputMethod(final View view, long delayMillis) {  
  29.         // 显示输入法  
  30.         new Handler().postDelayed(new Runnable() {  
  31.   
  32.             @Override  
  33.             public void run() {  
  34.                 InputMethodUtils.showInputMethod(view);  
  35.             }  
  36.         }, delayMillis);  
  37.     }  
  38. }  

你可能感兴趣的:(android)