您的位置:首页 > 移动开发 > Android开发

简单定制Android控件(3) - 打造通用的PopupWindow(一)

2016-01-15 11:33 573 查看
国际惯例,先上地址

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


ViewCreate.java:
import android.view.View;

/**
* Created by 大灯泡 on 2016/1/14.
*/
public interface ViewCreate {
View getPopupView();
View getAnimaView();

}


InputMethodUtils.java:
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); } }


下一章继续,通过继承我们的BasePopup来打造我们的popup
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: