在PopupWindow中应用 FlowLayout 实现快捷菜单

下图的弹出框就是需求的样式。里面的内容可以随意增删改。右边的角 要指向目标view。要实现这两个需求就需要 用常见的热门标签的流式布局 和 设定弹出框的xy。还有一个隐形的需求,弹出框的宽度,只知道最大值和最小值。最大值是屏幕宽,最小值是标题提交按钮的宽度

在PopupWindow中应用 FlowLayout 实现快捷菜单_第1张图片


要实现上面的样式我应用了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







你可能感兴趣的:(标签,PopupWindow,FlowLayout,流式布局)