先上图:
效果还是很丝滑的,这里动画主要用到了属性动画。实现逻辑很简单,动画都是在popupwindow调用showAsDropDown()
和dismiss()
时执行。这里主要是注意退出动画的实现,在dismiss()
中执行动画是无效的,需要在动画执行完毕后再执行super.dismiss();
可拓展能力强,可以结合自身需求实现不同的效果。
以下是源码:
PopupWindow :
public class TypeSelectWindow extends PopupWindow {
private Activity activity;
private BDBaseAdapter bdBaseAdapter = null;
View contentView;//弹出框的根布局,可以监听其点击事件,达到点击阴影消失弹框的效果
GridView gridView;
public TypeSelectWindow(Activity activity){
this.activity = activity;
initDatas();
initView();
}
private void initView(){
this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
this.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
contentView = LayoutInflater.from(activity).inflate(R.layout.join_popup_layout,null);
gridView = contentView.findViewById(R.id.gridView);
bdBaseAdapter = new BDBaseAdapter(activity,listDatas,R.layout.join_select_item) {
@Override
public void convert(BDViewHolder helper, String item, int position) {
((TextView)helper.getView(R.id.text)).setText(item);
}
};
gridView.setAdapter(bdBaseAdapter);
this.setContentView(contentView);//设置布局
this.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
this.setAnimationStyle(R.style.SelectPopupWindow);
this.setOutsideTouchable(true);
this.setFocusable(true);
}
/**
* 模拟数据
*/
List listDatas = new ArrayList<>();
private void initDatas(){
listDatas.clear();
for (int i = 0;i<10;i++){
listDatas.add("menu"+i);
}
}
//显示
public void showPopupWindow(View parent) {
if (!this.isShowing()) {
this.showAsDropDown(parent,0,0);
//执行进入动画,这里主要是执行列表下滑,背景变半透明在setAnimationStyle(R.style.SelectPopupWindow);中实现
AnimationUtil.createAnimation(true,contentView,gridView,null);
} else {
dismissPopup();
}
}
//消失
public void dismissPopup(){
super.dismiss();// 调用super.dismiss(),如果直接dismiss()会一直会调用下面的dismiss()
}
@Override
public void dismiss() {
//执行推出动画,列表上滑退出,同时背景变透明
AnimationUtil.createAnimation(false,contentView,gridView , new AnimationUtil.AnimInterface() {
@Override
public void animEnd() {
dismissPopup();//动画执行完毕后消失
}
});
}
}
AnimationUtil:
public class AnimationUtil {
//动画持续时间
public final static int ANIMATION_IN_TIME=500;
public final static int ANIMATION_OUT_TIME=500;
/**
* @param isIn 动画类型,进入或消失
* @param rootView 根布局,主要用来设置半透明背景
* @param target 要移动的view
* @param animInterface 动画执行完毕后的回调
*/
public static void createAnimation(final boolean isIn, final View rootView, final View target,
final AnimInterface animInterface){
final int toYDelta = ViewUtils.getViewMeasuredHeight(target);//测量布局高度
ValueAnimator valueAnimator = ValueAnimator.ofFloat(isIn?-toYDelta:0,isIn?0:-toYDelta);
valueAnimator.setDuration(isIn?ANIMATION_IN_TIME:ANIMATION_OUT_TIME);
valueAnimator.setRepeatCount(0);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (Float) animation.getAnimatedValue();
target.setY(currentValue);
if (!isIn){//因为在setAnimationStyle(R.style.SelectPopupWindow);设置了进入动画,所以执行进入动画时不再设置
rootView.setAlpha(1-Math.abs(currentValue)/animation.getDuration());
}
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (animInterface!=null){
animInterface.animEnd();
}
}
});
valueAnimator.start();
}
public interface AnimInterface{
void animEnd();
}
}
ViewUtils
public class ViewUtils {
/**
* 获取控件的高度
*/
public static int getViewMeasuredHeight(View view) {
calculateViewMeasure(view);
return view.getMeasuredHeight();
}
/**
* 获取控件的宽度
*/
public static int getViewMeasuredWidth(View view) {
calculateViewMeasure(view);
return view.getMeasuredWidth();
}
/**
* 测量控件的尺寸
*/
private static void calculateViewMeasure(View view) {
int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);
}
}
style.xml
这里我只设置了进入的背景渐变的动画,列表的滑动动画我在AnimationUtil中实现了,在上面的AnimationUtil中也可以实现进入动画,这样也更统一,看个人喜好。
<style name="SelectPopupWindow">
<item name="android:windowEnterAnimation">@anim/p_in
style>
p_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="500"
android:fromAlpha="0"
android:toAlpha="1"/>
set>
布局部分就是简单的gridview,布局可以有多种方式来实现。可以直接用View标签来实现阴影效果。
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:background="#88000000"
android:layout_height="match_parent">
<GridView
android:id="@+id/gridView"
android:scrollbars="none"
android:numColumns="3"
android:background="@color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
RelativeLayout>
执行:
JoinTypeSelectWindow joinTypeSelectWindow = new JoinTypeSelectWindow(getActivity());
joinTypeSelectWindow.showPopupWindow(targetView);
————— 完 ——————
一个小小的源码链接