下图的弹出框就是需求的样式。里面的内容可以随意增删改。右边的角 要指向目标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