今天看了一个简单而实用的Demo所以特地到这里写个帖子,进行一些总结。第一这个技术点算是很常用的,当然我也有几种方法进行实现,但是自认没有这个简单,复用性也没有这个高。
那么进入正题,就像题目写的没错就是自定义底部弹窗。我那之前是使用PopWindow这个算是正统的使用方法,当然你如果不太熟悉PopWindow,那么你还可以用一个比较讨巧的方法,就是定义一个背景为半透明黑色的Activity,就可以了。然后两个Activity进行数据交互。总之都可以实现。但是今天讲的是既不使用PopWindow也不使用Activity,而是使用一个类,注意不是自定义View。
所以这也正是我认为这个框架,设计的巧妙和试用的地方。你只需要在你需要弹出窗口的Activity或者Fragment新建一个这个对象就可以了。
废话不多说,直接上代码。顺便我把我的心得和注释一起放出来。供大家一起欣赏。
首先看一下主要的方法类
//不是自定义View姑且称之为Panel吧
public class BottomPopPanel {
private Context context;
private final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM);
protected ViewGroup contentContainer;
private ViewGroup decorView;//activity的根View
private ViewGroup rootView;//黑色半透明背景View
private OnDismissListener onDismissListener;//小时Listener
private boolean isDismissing;
private Animation outAnim;//退出动画
private Animation inAnim;//进入动画
private final int gravity = Gravity.BOTTOM;
public BottomPopPanel(Context context, int layoutId) {
this.context = context;
//进行父View和子View的初始化工作
initView(this.context);
//把我们的自定义View放到contentContainer里面去
ScaleScreenHelperFactory.getInstance().loadView((ViewGroup) LayoutInflater.from(context).inflate(layoutId, contentContainer));
}
private void initView(Context context) {
this.context = context;
LayoutInflater layoutInflater = LayoutInflater.from(this.context);
//获取该一个Activity底层父View正规写法,R.id.content是每个布局文件都会包含的Id这里可以看Activity的底层实现原理
//说句实话当初看,Activity inflatelayout的底层的时候是看见过这些代码的,但是当时看完之后感觉没有什么用处而且
decorView = (ViewGroup) ((Activity) context).getWindow().getDecorView().findViewById(android.R.id.content);
//由于Activity的底层父View的布局是FrameLayout所以我们这个RootView为了模拟popWindow可以直接设置成黑色半透明的背景
//宽和高设置为占满就可以了 用的确实和巧妙啊
rootView = (ViewGroup) ScaleScreenHelperFactory.getInstance().loadView((ViewGroup) layoutInflater.inflate(R.layout.view_base_pickerview, decorView, false));
rootView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
//过滤出我们加载自定义View 的父V
contentContainer = (FrameLayout) rootView.findViewById(R.id.content_container);
contentContainer.setLayoutParams(params);
//初始化 进入和滑出动画
inAnim = getInAnimation();
outAnim = getOutAnimation();
}
/**
* 添加这个View到Activity的根视图
*/
public void show() {
if (isShowing()) {
return;
}
onAttached(rootView);
}
//通过onAttached 和 remove 实现显示Pop 和得到Pop的当前显示状态
/**
* show的时候调用
*
* @param view 这个View
*/
private void onAttached(View view) {
decorView.addView(view);
contentContainer.startAnimation(inAnim);
}
/**
* 检测该View是不是已经添加到根视图
*
* @return 如果视图已经存在该View返回true
*/
public boolean isShowing() {
View view = decorView.findViewById(R.id.outmost_container);
return view != null;
}
public void dismiss() {
if (isDismissing) {
return;
}
//消失动画
outAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
decorView.post(new Runnable() {
@Override
public void run() {
//从activity根视图移除
decorView.removeView(rootView);
isDismissing = false;
//动画结束调用自定义dismiss函数
if (onDismissListener != null) {
onDismissListener.onDismiss(BottomPopPanel.this);
}
}
});
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
contentContainer.startAnimation(outAnim);
//是否正在消失状态
isDismissing = true;
}
//加载滑入动画
public Animation getInAnimation() {
return AnimationUtils.loadAnimation(context, R.anim.slide_in_bottom);
}
//加载滑出动画
public Animation getOutAnimation() {
return AnimationUtils.loadAnimation(context, R.anim.slide_out_bottom);
}
public BottomPopPanel setOnDismissListener(OnDismissListener onDismissListener) {
this.onDismissListener = onDismissListener;
//直接调用dismiss函数 dismiss函数包含了 显示接口的消失方法
dismiss();
return this;
}
public BottomPopPanel setCancelable(boolean isCancelable) {
View view = rootView.findViewById(R.id.outmost_container);
if (isCancelable) {
view.setOnTouchListener(onCancelableTouchListener);
} else {
view.setOnTouchListener(null);
}
return this;
}
/**
* Called when the user touch on black overlay in order to dismiss the dialog
*/
private final View.OnTouchListener onCancelableTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
dismiss();
}
return false;
}
};
public interface OnDismissListener {
void onDismiss(Object o);
}
public View findViewById(int id) {
return contentContainer.findViewById(id);
}
}
补充一些自定义动画
slide_in_anim
<?xml version="1.0" encoding="UTF-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="@integer/animation_default_duration"
android:fromXDelta="0%"
android:fromYDelta="100%"
android:toXDelta="0%"
android:toYDelta="0%" />
</set>
slide_out_anim
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="@integer/animation_default_duration"
android:fromXDelta="0%"
android:toXDelta="0%"
android:fromYDelta="0%"
android:toYDelta="100%"/>
</set>
在Ativity或者Fragment中的使用
当我们设置点击按钮的时候将下面的代码放进去
final BottomPopPanel bottomView = new BottomPopPanel(getActivity(), R.layout.view_bottom_chosecharge);
bottomView.findViewById(R.id.free).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bottomView.setOnDismissListener(new BottomPopPanel.OnDismissListener() {
@Override
public void onDismiss(Object o) {
}
});
}
});
bottomView.setCancelable(true);
bottomView.show();