俗话说:不想当工程师的泥瓦工不是好程序员!
今天我们要实现的最终效果,如下:
其实实现这样的效果不是很难,俗话说:条条大路通罗马。正常路子走不通,咱还不会走野路子呀。
方案一:在RecyclerView上面覆盖一层布局,设置显示和隐藏,大不了在显示和隐藏的时候加个动画呗;
方案二:使用PopupWindow;
方案三:使用Dialog。其他方案自行查找!
此时我想起我有一次面试,面试官问拿着他们开发的app,给我看一个侧滑菜单的功能,然后就问我这个你知道是怎么实现的吗?我说最简单的就弄个布局设置显示隐藏呗,面试官严肃的给我说这个不是这样实现的,一边摇头一边说不是,我就说这样也可以实现的,再说我也没说其他方式实现不了呀。我心里面一万个羊驼在奔腾呀!言归正传,撸码
首先自定义一个popupWindow,如下:
public DownPopupWindow(Activity context, List popupWindowList, String checkedName) {
super(context);
this.mContext = context;
this.mPopupWindowList = popupWindowList;
this.mCheckedName = checkedName;
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_popupwindow, null, false);
this.setContentView(view);
this.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
ColorDrawable colorDrawable = new ColorDrawable(mContext.getResources().getColor(R.color.color_66000000));
this.setBackgroundDrawable(colorDrawable);
this.setFocusable(true);
this.setOutsideTouchable(true);
this.update();
mPopupWindowAdapter = new PopupWindowAdapter(mPopupWindowList, mContext);
RecyclerView recyclerView = view.findViewById(R.id.rv_recycler);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
recyclerView.setAdapter(mPopupWindowAdapter);
for (int i = 0; i < mPopupWindowList.size(); i++) {
if (mCheckedName.equals(mPopupWindowList.get(i).getName())) {
mPopupWindowList.get(i).setChecked(true);
} else {
mPopupWindowList.get(i).setChecked(false);
}
}
}
代码很简单,设置布局的宽高,透明的背景以及外部焦点,还有在里面加了一个RecyclerView的列表数据。
布局如下:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
android:background="#ffffff">
android.support.v7.widget.RecyclerView>
FrameLayout>
也很简单就一个RecyclerView展示我们需要的筛选条件。
我们提供一个展示的方法,如下:
public void showDialog(View view){
if (this.isShowing()) {
dismiss();
} else {
this.showAsDropDown(view);
}
}
在我们的Activity中调用这个方法,如下:
llTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDownPopupWindow = new DownPopupWindow(MainActivity.this, TestBean.getPopupWindowList(), tvTwo.getText().toString()) {
@Override
public void getItem(String id, String name) {
if (mDownPopupWindow.isShowing()) {
dismiss();
}
tvTwo.setText(name);
tvTwo.setTextColor(getColor(R.color.colorAccent));
}
};
mDownPopupWindow.showDialog(llTwo);
}
});
运行看一下效果,如下:
what?说好的半透明呢。网上都说设置setBackgroundDrawable属性就好,再看咱的代码也设置了呀,但是没有啥软用呀?那就解决呗,方式一:覆盖一层半透明,在PopupWindow show的时候显示,dismiss的时候隐藏,不好看?加动画呗(很简单暂时不做代码展示)。方式二:网上的另一种解决方案,给整个手机屏幕设置半透明,那就实现一下呗,代码如下:
public static void setBackgroundAlpha(Activity activity, float bgAlpha) {
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
lp.alpha = bgAlpha;
if (bgAlpha == 1) {
//不移除该Flag的话,在有视频的页面上的视频会出现黑屏的bug
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
} else {
//此行代码主要是解决在华为手机上半透明效果无效的bug
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
activity.getWindow().setAttributes(lp);
}
在我们PopupWindow展示的方法中设置半透明,如下:
public void showDialog(View view){
if (this.isShowing()) {
dismiss();
} else {
this.showAsDropDown(view);
setBackgroundAlpha(mContext,0.5f);
}
}
然后在PopupWindow的dismiss监听中设置全透明,如下
this.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
setBackgroundAlpha(mContext,1f);
}
});
运行看一下效果,如下
什么鬼?用户看到这效果,心里一万个羊驼在奔腾呀!从底部弹出的效果使用这样的方式可以。如下图:
问题是我们要的不是这样的需求呀!看来这种效果此方案是行不通的,方案三:showAtLocation,代码如下
public void showDialog2(View view){
if (isShowing()) {
dismiss();
} else {
int[] location = new int[2];
view.getLocationOnScreen(location);
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
int screenHeight = wm.getDefaultDisplay().getHeight();
setHeight(screenHeight - location[1] - view.getHeight());
showAtLocation(view, Gravity.NO_GRAVITY, 0, location[1] + view.getHeight());
}
}
效果是实现了,但是功能上体验如何?点击半透明部分PopopWindow消失?我们试试,果然没啥反应。问题是我们也设置了属性:
this.setFocusable(true);
this.setOutsideTouchable(true);
那为啥不行呢?因为我们的布局是设置除了选择按钮以上部分的屏幕剩余全部部分,也就是对于我们的PopupWindow布局没有外面的布局可以显示了,我们设置的属性是点击PopupWindow之外可以消失。何以见的?我们按照开始设置PopupWindow方式:
public void showDialog(View view){
if (this.isShowing()) {
dismiss();
} else {
this.showAsDropDown(view);
}
}
没有半透明的背景,也就是我们的PopupWindow不是全屏的,这个时候我们点击外部试一试,果然能让PopupWindow消失。我们也可以点击按钮上面的部分也就是标题部分,也能让PopupWindow消失。
既然我们想要半透明的效果还希望点击半透明部分区域能够让PopupWindow消失,那我们就自己处理半透明部分的点击事件,方式一:
this.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
}
return false;
}
});
其实这个和上面设置属性没啥区别,无效。方式二:
this.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isShowing()) {
dismiss();
return true;
}
return false;
}
});
判断条件变了,测试发现点击半透明部分可以实现让PopupWindow消失,但是,当我们点击我们的筛选条件选项的时候就出问题了,点击之后没有选中我们点击的,PopupWindow就直接消失了,测试发现这个RecyclerView选项的点击事件被后来我们设置的监听消费掉了。方式三:
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
}
});
view是什么呢?就是PopupWindow的布局,我们给整个布局加上一个点击事件,完美解决问题。
这种实现方式在某些非常规手机上是否会存在问题呢?比如:全面屏手机,刘海屏手机。