下图的弹出框就是需求的样式。里面的内容可以随意增删改。右边的角 要指向目标view。要实现这两个需求就需要 用常见的热门标签的流式布局 和 设定弹出框的xy。还有一个隐形的需求,弹出框的宽度,只知道最大值和最小值。最大值是屏幕宽,最小值是标题提交按钮的宽度
要实现上面的样式我应用了PopupWindow 和 GitHub 上的一个开源项目 android-flowlayout https://github.com/ApmeM/android-flowlayout,flowlayout在计算宽度的地方有两个地方不符合需求,所以我进行的修改,https://github.com/langzuxiaozi/android-flowlayout。
首先看看PopupWindow的布局文件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp" >
<ViewStub android:id="@+id/img_top_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/corner_layout"
/>
<LinearLayout
android:id="@+id/title_layout"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@drawable/pop_window_top_item_background"
android:orientation="horizontal"
android:gravity="center_vertical"
>
<TextView
android:id="@+id/title_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="13dp"
android:text="讨厌的理由"
android:textColor="@color/white"
android:textSize="14sp" />
<TextView
android:id="@+id/title_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="(可多选)"
android:textColor="@color/white_alpha60"
android:textSize="14sp" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/commit"
android:layout_width="48dp"
android:layout_height="24dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="@drawable/commit_btn_background_selector"
android:text="提交"
android:textColor="@color/white"
android:textSize="12sp" />
</LinearLayout>
<com.example.wolf.popupwindow_flowlayout.flowlayout.FlowLayout
xmlns:f="http://schemas.android.com/apk/res-auto"
android:id="@+id/content_flowlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/pop_window_bottom_item_background"
android:paddingBottom="8dip"
android:paddingTop="13dip"
android:paddingLeft="9dip"
android:paddingRight="9dip"
android:orientation="horizontal"
>
</com.example.wolf.popupwindow_flowlayout.flowlayout.FlowLayout>
<ViewStub android:id="@+id/img_bottom_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/corner_layout"
/>
</LinearLayout>
title_layout 是标题提交按钮那一层
content_flowlayout 是热门标签那一层
img_bottom_layout img_top_layout 这两个是PopupWindeow的角,只有一个layout会显示。上图就是img_top_layout显示的结果。
计算宽度的思想就是 计算出title_layout的宽content_flowlayout宽,取最大值。然后和屏幕宽比较,取最小值。最后的结果结果就是PopupWindeow的宽。
设置PopupWindeow的宽后,再计算PopupWindeow的高。
获取目标view的xy坐标(view的左上角)后,根据PopupWindeow的宽高计算出要显示的位置。
显示PopupWindeow的代码是:
private void showPopupWindow(View parent) {
// 一个自定义的布局,作为显示的内容
View contentView = LayoutInflater.from(this).inflate(
R.layout.pop_window, null);
flowLayout = (FlowLayout) contentView.findViewById(R.id.content_flowlayout);
String [] stings = {"大骗子","不喜欢","大人渣","爱","十分垃圾","大坏蛋","大笨蛋"};
int margin = dip2px(this, 4);
for (int i=0;i<stings.length;i++){
CheckBox btn = (CheckBox) LayoutInflater.from(this).inflate(R.layout.checkbox, null,false);
btn.setText(stings[i]);
FlowLayout.LayoutParams lp = new FlowLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(margin, margin, margin, margin);
flowLayout.addView(btn,lp);
}
//获取屏幕的最大宽
WindowManager mWindowManager = (WindowManager) this.getSystemService(this.WINDOW_SERVICE);
int screenWidth = mWindowManager.getDefaultDisplay().getWidth() ;
int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
int w = View.MeasureSpec.makeMeasureSpec(screenWidth,View.MeasureSpec.AT_MOST);
int mWidth=0;
//获取flowLayout宽
flowLayout.measure(w, h);
mWidth = flowLayout.getMeasuredWidth();
ViewGroup vGroup = (ViewGroup) contentView.findViewById(R.id.title_layout);
w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
//获取title_layout view的宽,并且和flowLayout宽比较,取最大值
vGroup.measure(w, h);
mWidth = Math.max(mWidth,vGroup.getMeasuredWidth());
//获取子view的最大宽后再加上父view的左右padding,获得父view的真实宽
mWidth+=contentView.getPaddingLeft()+contentView.getPaddingRight();
//如果父view的宽大于屏幕宽,那就取屏幕宽为最终popupWindeow的最终宽
mWidth = Math.min(mWidth,screenWidth);
final PopupWindow popupWindow = new PopupWindow(contentView,
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
popupWindow.setWidth(mWidth);
popupWindow.setTouchable(true);
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
// 这里如果返回true的话,touch事件将被拦截
// 拦截后 PopupWindow的onTouchEvent不被调用,这样点击外部区域无法dismiss
}
});
// 如果不设置PopupWindow的背景,无论是点击外部区域还是Back键都无法dismiss弹框
// 我觉得这里是API的一个bug
popupWindow.setBackgroundDrawable(new BitmapDrawable());
//获取目标view 的xy坐标
int[] location = new int[2];
parent.getLocationOnScreen(location);
int parentXPos = location[0];
int parentYPos = location[1];
//通过popupwindow的宽高计算 显示的的位置(xy坐标)
w = View.MeasureSpec.makeMeasureSpec(screenWidth,View.MeasureSpec.AT_MOST);
popupWindow.getContentView().measure(w, h);
int yPos = parentYPos - popupWindow.getContentView().getMeasuredHeight();
int xPos = parentXPos -mWidth + popupWindow.getContentView().getPaddingRight()+dip2px(this, 10);//+parent.getWidth();
//这里得到的是除了系统自带显示区域之外的所有区域,这里就是除了最上面的一条显示电量的状态栏之外的所有区域
Rect frameRect = new Rect();
this.getWindow().getDecorView()
.getWindowVisibleDisplayFrame(frameRect);
//判断popupwindow是显示在目标view的上边还是下边,通过状态栏判断
View inflated = null;
// frameRect.top 这里便可以得到状态栏的高度,即最上面一条显示电量,信号等
if((frameRect.top)>yPos){
yPos = parentYPos+parent.getHeight();
ViewStub stub = (ViewStub) contentView.findViewById(R.id.img_top_layout);
inflated = stub.inflate();
//设置显示角向上的图标
ImageView iv = (ImageView) inflated.findViewById(R.id.img);
iv.setImageResource(R.drawable.pop_window_corner_top);
}else{
ViewStub stub = (ViewStub) contentView.findViewById(R.id.img_bottom_layout);
inflated = stub.inflate();
//因为用了ViewStub,所以 计算的popupwindow的高的时候没有计算图片高
inflated.measure(w,h);
yPos -=inflated.getMeasuredHeight();
}
// 设置好参数之后再show
popupWindow.showAtLocation(parent, Gravity.NO_GRAVITY, xPos, yPos);
}
http://download.csdn.net/detail/langzxz/8894973