国际惯例,先上地址
https://github.com/razerdp/BasePopup
PS:效果图都放在了github,github有着我继承该类做出来的popupWindow
//2016-01-15 目前只写了两个PopupWindow
效果图:
普通的放大缩小:
从下方弹出:
通常情况下,面对各种浮动窗口,选择窗口什么的,我们通常都是使用popupWindow,但是很多时候我们都希望popupWindow可以在弹出的时候带有动画,但是就popup本身而言,使用的动画是在是太不舒服不自由了。
通常情况下,我们弄个popupWindow的动画都是这么玩的
popupWindow.setAnimationStyle(R.style.PopMenuAnimation);
我们还得去styles.xml弄弄我们的进入/退出动画,这多不自然啊,而且说好的控制呢对吧
于是这次我们就来打造一个通用的popupWindow,让我们可以随心自由的设置我们的popupWindow
这次我们要实现的popupWindow起码要实现以下几个要求{
- 自由的定义样式
- 便利的动画实现
- 可扩展
- 代码简洁易懂
}
好的,说了那么多,接下来我们就开工。
开工之前,我们先谈谈要求和实现方法吧:
第一点,自由的定义样式,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的整体,然后就是一些参数设置什么的,比如是否需要输入之类的,这是后话。
于是我们就有了下面的一段代码:
- public abstract class BasePopupWindow implements ViewCreate {
- private static final String TAG = "BasePopupWindow";
- //元素定义
- protected PopupWindow mPopupWindow;
- //popup视图
- protected View mPopupView;
- protected Activity mContext;
- //是否自动弹出输入框(default:false)
- private boolean autoShowInputMethod = false;
- private OnDismissListener mOnDismissListener;
- public BasePopupWindow(Activity context) {
- mContext = context;
- mPopupView = getPopupView();
- mPopupView.setFocusable(true);
- mPopupView.setFocusableInTouchMode(true);
- //默认占满全屏
- mPopupWindow =
- new PopupWindow(mPopupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- //指定透明背景,点击外面相关
- mPopupWindow.setBackgroundDrawable(new ColorDrawable());
- mPopupWindow.setFocusable(true);
- mPopupWindow.setOutsideTouchable(true);
- //无需动画
- mPopupWindow.setAnimationStyle(0);
- }
- public BasePopupWindow(Activity context, int w, int h) {
- mContext = context;
- mPopupView = getPopupView();
- mPopupView.setFocusable(true);
- mPopupView.setFocusableInTouchMode(true);
- //默认占满全屏
- mPopupWindow = new PopupWindow(mPopupView, w, h);
- //指定透明背景,点击外面相关
- mPopupWindow.setBackgroundDrawable(new ColorDrawable());
- mPopupWindow.setFocusable(true);
- mPopupWindow.setOutsideTouchable(true);
- //无需动画
- mPopupWindow.setAnimationStyle(0);
- }
设置两个构造器是为了扩展,因为默认状态下我们的popup是全屏显示,但是特殊的popup呢,最经典的例子,微信朋友圈的点赞/评论弹出来的那个东东,我们可以用popup来做,但很明显我们不可以用全屏的popup对吧,于是就有了第二个构造器
从代码我们看到我们实现了一个接口,这个接口只提供两个方法,具体如下
- /**
- * Created by 大灯泡 on 2016/1/14.
- */
- public interface ViewCreate {
- View getPopupView();
- View getAnimaView();
- }
getPopupView,在构造器我们可以看到,popupWindow的contentView就是通过这个得到
getAnimaView,明显用来播放动画用的
基本构造有了,接下来就是对子类的限定
- //------------------------------------------抽象-----------------------------------------------
- public abstract Animation getAnimation();
- public abstract AnimationSet getAnimationSet();
- public abstract View getInputView();
期中getAnimation提供给子类用来设置播放动画,AnimationSet也是,但是AnimationSet是使用ObjectAnima的,也就是物理上改变的view参数,Animation只是改变视觉,对于物理事件(比如点击事件)的位置是不涉及改变的。
getInputView用于获取需要输入内容的popup里面的输入框,用于自动弹出输入法。
对子类规则定好后,接下来就是实现show方法。
对于popup我们都知道有一个showAtLocation方法来展示popup,我们这里也使用这个。
- //------------------------------------------showPopup-----------------------------------------------
- public void showPopupWindow() {
- try {
- tryToShowPopup(0, null);
- } catch (Exception e) {
- Log.e(TAG, "show error");
- e.printStackTrace();
- }
- }
- public void showPopupWindow(int res) {
- try {
- tryToShowPopup(res, null);
- } catch (Exception e) {
- Log.e(TAG, "show error");
- e.printStackTrace();
- }
- }
- public void showPopupWindow(View v) {
- try {
- tryToShowPopup(0, v);
- } catch (Exception e) {
- Log.e(TAG, "show error");
- e.printStackTrace();
- }
- }
- //------------------------------------------Methods-----------------------------------------------
- private void tryToShowPopup(int res, View v) throws Exception {
- //传递了view
- if (res == 0 && v != null) {
- mPopupWindow.showAtLocation(v, Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 0, 0);
- }
- //传递了res
- if (res != 0 && v == null) {
- mPopupWindow.showAtLocation(mContext.findViewById(res), Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 0, 0);
- }
- //什么都没传递,取顶级view的id
- if (res == 0 && v == null) {
- mPopupWindow.showAtLocation(mContext.findViewById(android.R.id.content),
- Gravity.RIGHT | Gravity.CENTER_HORIZONTAL,
- 0,
- 0
- );
- }
- if (getAnimation() != null && getAnimaView() != null) {
- getAnimaView().startAnimation(getAnimation());
- }
- //ViewHelper.setPivotX是包nineoldAndroid的方法,用于兼容低版本的anima以及方便的view工具
- if (getAnimation() == null && getAnimationSet() != null && getAnimaView() != null &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- ViewHelper.setPivotX(getAnimaView(), getAnimaView().getMeasuredWidth() / 2.0f);
- ViewHelper.setPivotY(getAnimaView(), getAnimaView().getMeasuredHeight() / 2.0f);
- getAnimationSet().start();
- }
- //自动弹出键盘
- if (autoShowInputMethod && getInputView() != null) {
- InputMethodUtils.showInputMethod(getInputView(), 150);
- }
- }
相关注释都写上了,这里就不解释
附上InputMethodUtils的代码:
150ms后弹出软键盘是为了给窗体绘制时间。
- /**
- * Created by 大灯泡 on 2016/1/14.
- * 显示键盘d工具类
- */
- public class InputMethodUtils {
- /** 显示软键盘 */
- public static void showInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager) view.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
- }
- }
- /** 显示软键盘 */
- public static void showInputMethod(Context context) {
- InputMethodManager imm = (InputMethodManager) context
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
- }
- /** 多少时间后显示软键盘 */
- public static void showInputMethod(final View view, long delayMillis) {
- // 显示输入法
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- InputMethodUtils.showInputMethod(view);
- }
- }, delayMillis);
- }
- }
最后就是一些Setter/Getter和接口方法了
总体代码:
BasePopup.java:
- package razerdp.basepopup.basepopup;
- import android.app.Activity;
- import android.graphics.drawable.ColorDrawable;
- import android.os.Build;
- import android.util.Log;
- import android.view.Gravity;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.WindowManager;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.AlphaAnimation;
- import android.view.animation.Animation;
- import android.view.animation.AnimationSet;
- import android.view.animation.ScaleAnimation;
- import android.view.animation.TranslateAnimation;
- import android.widget.PopupWindow;
- import com.nineoldandroids.view.ViewHelper;
- import razerdp.basepopup.utils.InputMethodUtils;
- /**
- * Created by 大灯泡 on 2016/1/14.
- * 通用的popupWindow
- */
- public abstract class BasePopupWindow implements ViewCreate {
- private static final String TAG = "BasePopupWindow";
- //元素定义
- protected PopupWindow mPopupWindow;
- //popup视图
- protected View mPopupView;
- protected Activity mContext;
- //是否自动弹出输入框(default:false)
- private boolean autoShowInputMethod = false;
- private OnDismissListener mOnDismissListener;
- public BasePopupWindow(Activity context) {
- mContext = context;
- mPopupView = getPopupView();
- mPopupView.setFocusable(true);
- mPopupView.setFocusableInTouchMode(true);
- //默认占满全屏
- mPopupWindow =
- new PopupWindow(mPopupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- //指定透明背景,back键相关
- mPopupWindow.setBackgroundDrawable(new ColorDrawable());
- mPopupWindow.setFocusable(true);
- mPopupWindow.setOutsideTouchable(true);
- //无需动画
- mPopupWindow.setAnimationStyle(0);
- }
- public BasePopupWindow(Activity context, int w, int h) {
- mContext = context;
- mPopupView = getPopupView();
- mPopupView.setFocusable(true);
- mPopupView.setFocusableInTouchMode(true);
- //默认占满全屏
- mPopupWindow = new PopupWindow(mPopupView, w, h);
- //指定透明背景,back键相关
- mPopupWindow.setBackgroundDrawable(new ColorDrawable());
- mPopupWindow.setFocusable(true);
- mPopupWindow.setOutsideTouchable(true);
- //无需动画
- mPopupWindow.setAnimationStyle(0);
- }
- //------------------------------------------抽象-----------------------------------------------
- public abstract Animation getAnimation();
- public abstract AnimationSet getAnimationSet();
- public abstract View getInputView();
- //------------------------------------------showPopup-----------------------------------------------
- public void showPopupWindow() {
- try {
- tryToShowPopup(0, null);
- } catch (Exception e) {
- Log.e(TAG, "show error");
- e.printStackTrace();
- }
- }
- public void showPopupWindow(int res) {
- try {
- tryToShowPopup(res, null);
- } catch (Exception e) {
- Log.e(TAG, "show error");
- e.printStackTrace();
- }
- }
- public void showPopupWindow(View v) {
- try {
- tryToShowPopup(0, v);
- } catch (Exception e) {
- Log.e(TAG, "show error");
- e.printStackTrace();
- }
- }
- //------------------------------------------Methods-----------------------------------------------
- private void tryToShowPopup(int res, View v) throws Exception {
- //传递了view
- if (res == 0 && v != null) {
- mPopupWindow.showAtLocation(v, Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 0, 0);
- }
- //传递了res
- if (res != 0 && v == null) {
- mPopupWindow.showAtLocation(mContext.findViewById(res), Gravity.RIGHT | Gravity.CENTER_HORIZONTAL, 0, 0);
- }
- //什么都没传递,取顶级view的id
- if (res == 0 && v == null) {
- mPopupWindow.showAtLocation(mContext.findViewById(android.R.id.content),
- Gravity.RIGHT | Gravity.CENTER_HORIZONTAL,
- 0,
- 0
- );
- }
- if (getAnimation() != null && getAnimaView() != null) {
- getAnimaView().startAnimation(getAnimation());
- }
- //ViewHelper.setPivotX是包nineoldAndroid的方法,用于兼容低版本的anima以及方便的view工具
- if (getAnimation() == null && getAnimationSet() != null && getAnimaView() != null &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- ViewHelper.setPivotX(getAnimaView(), getAnimaView().getMeasuredWidth() / 2.0f);
- ViewHelper.setPivotY(getAnimaView(), getAnimaView().getMeasuredHeight() / 2.0f);
- getAnimationSet().start();
- }
- //自动弹出键盘
- if (autoShowInputMethod && getInputView() != null) {
- InputMethodUtils.showInputMethod(getInputView(), 150);
- }
- }
- public void setAdjustInputMethod(boolean needAdjust) {
- if (needAdjust) {
- mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
- } else {
- mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
- }
- }
- public void setAutoShowInputMethod(boolean autoShow) {
- this.autoShowInputMethod = autoShow;
- if (autoShow) {
- setAdjustInputMethod(true);
- } else {
- setAdjustInputMethod(false);
- }
- }
- public void setBackPressEnable(boolean backPressEnable){
- if (backPressEnable){
- mPopupWindow.setBackgroundDrawable(new ColorDrawable());
- }else {
- mPopupWindow.setBackgroundDrawable(null);
- }
- }
- //------------------------------------------Getter/Setter-----------------------------------------------
- public boolean isShowing() {
- return mPopupWindow.isShowing();
- }
- public OnDismissListener getOnDismissListener() {
- return mOnDismissListener;
- }
- public void setOnDismissListener(OnDismissListener onDismissListener) {
- mOnDismissListener = onDismissListener;
- if (mOnDismissListener!=null){
- mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
- @Override
- public void onDismiss() {
- mOnDismissListener.onDismiss();
- }
- });
- }
- }
- //------------------------------------------状态控制-----------------------------------------------
- public void dismiss() {
- try {
- mPopupWindow.dismiss();
- } catch (Exception e) {
- Log.d(TAG, "dismiss error");
- }
- }
- //------------------------------------------Anima-----------------------------------------------
- /**
- * 生成TranslateAnimation
- * @param durationMillis 动画显示时间
- * @param start 初始位置
- */
- protected Animation getTranslateAnimation(int start, int end, int durationMillis) {
- Animation translateAnimation = new TranslateAnimation(0, 0, start, end);
- translateAnimation.setDuration(durationMillis);
- translateAnimation.setFillEnabled(true);
- translateAnimation.setFillAfter(true);
- return translateAnimation;
- }
- /**
- * 生成ScaleAnimation
- */
- protected Animation getScaleAnimation(float fromX, float toX, float fromY, float toY,
- int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {
- Animation scaleAnimation =
- new ScaleAnimation(fromX, toX, fromY, toY, pivotXType, pivotXValue, pivotYType,
- pivotYValue);
- scaleAnimation.setDuration(300);
- scaleAnimation.setFillEnabled(true);
- scaleAnimation.setFillAfter(true);
- return scaleAnimation;
- }
- /**
- * 生成自定义ScaleAnimation
- */
- protected Animation getDefaultScaleAnimation() {
- Animation scaleAnimation =
- new ScaleAnimation(0f, 1f, 0f, 1f, Animation.RELATIVE_TO_SELF, 0.5f,
- Animation.RELATIVE_TO_SELF, 0.5f);
- scaleAnimation.setDuration(300);
- scaleAnimation.setInterpolator(new AccelerateInterpolator());
- scaleAnimation.setFillEnabled(true);
- scaleAnimation.setFillAfter(true);
- return scaleAnimation;
- }
- /**
- * 生成默认的AlphaAnimation
- * */
- protected Animation getDefaultAlphaAnimation() {
- Animation alphaAnimation =
- new AlphaAnimation(0.0f, 1.0f);
- alphaAnimation.setDuration(300);
- alphaAnimation.setInterpolator(new AccelerateInterpolator());
- alphaAnimation.setFillEnabled(true);
- alphaAnimation.setFillAfter(true);
- return alphaAnimation;
- }
- //------------------------------------------Interface-----------------------------------------------
- public interface OnDismissListener {
- void onDismiss();
- }
- }
- import android.view.View;
- /**
- * Created by 大灯泡 on 2016/1/14.
- */
- public interface ViewCreate {
- View getPopupView();
- View getAnimaView();
- }
- import android.content.Context;
- import android.os.Handler;
- import android.view.View;
- import android.view.inputmethod.InputMethodManager;
- /**
- * Created by 大灯泡 on 2016/1/14.
- * 显示键盘d工具类
- */
- public class InputMethodUtils {
- /** 显示软键盘 */
- public static void showInputMethod(View view) {
- InputMethodManager imm = (InputMethodManager) view.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
- }
- }
- /** 显示软键盘 */
- public static void showInputMethod(Context context) {
- InputMethodManager imm = (InputMethodManager) context
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
- }
- /** 多少时间后显示软键盘 */
- public static void showInputMethod(final View view, long delayMillis) {
- // 显示输入法
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- InputMethodUtils.showInputMethod(view);
- }
- }, delayMillis);
- }
- }