利用PopupWindow制作顶部下拉菜单

利用PopupWindow制作顶部下拉菜单

顶部最常见的筛选下拉菜单,每个菜单view可以自定义,进出动画等都可以自定义。效果如图.
完整的例子demo见GitHub: https://github.com/yinghuihou/PopupWindow

image

开始撸代码

  • 首先我们使用的控件是PopupWindow
  • 筛选栏就是一个自定义layout,一个LinearLayout包着三个TextView,很简单没什么可说的,xml如下
  • 最外层用标签是因为使用了DataBinding,可以省去findViewById这个操作,Java代码直接用 binding.xxx来获取控件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:id="@+id/ll_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="@color/gray_divider_line_color"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <LinearLayout
            android:id="@+id/ll_bank"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/all_bank"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="全部银行"
                android:textColor="@color/load_txt_color_3"
                android:textSize="12dp" />

            <ImageView
                android:id="@+id/bank_img"
                android:layout_width="5dp"
                android:layout_height="3dp"
                android:layout_marginLeft="5dp"
                android:src="@drawable/img_small_arrow_down" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_topic"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/all_topic"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="全部主题"
                android:textColor="@color/load_txt_color_3"
                android:textSize="12dp" />

            <ImageView
                android:id="@+id/topic_img"
                android:layout_width="5dp"
                android:layout_height="3dp"
                android:layout_marginLeft="5dp"
                android:src="@drawable/img_small_arrow_down" />

        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_more"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:clickable="false"
            android:gravity="center"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/more"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingBottom="10dp"
                android:paddingTop="10dp"
                android:text="更多"
                android:textColor="@color/load_txt_color_3"
                android:textSize="12dp" />

            <ImageView
                android:id="@+id/more_img"
                android:layout_width="5dp"
                android:layout_height="3dp"
                android:layout_marginLeft="5dp"
                android:src="@drawable/img_small_arrow_down" />
        </LinearLayout>
    </LinearLayout>

</layout>
  • 开始写Java文件HotCreditCardSelectLayout.java
  • 首先就是对三个筛选item设置点击事件
  • 在这里有三个筛选项,所以我用了三个PopupWindow来操作,互不影响操作灵活一些
mBinding.allBank.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mBankPopupwindow != null && mBankPopupwindow.isShowing()) {
                    dismissPopWindow(mBankPopupwindow);
                } else {
                    if (mBankPopupwindow == null) {
                        mBankPopupwindow = initmPopupWindowView(mData.bank, TYPE_BANK);
                    }
                    showPopWindow(TYPE_BANK, v);
                }
            }
        });
        mBinding.allTopic.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mTopicPopupwindow != null && mTopicPopupwindow.isShowing()) {
                    dismissPopWindow(mTopicPopupwindow);
                } else {
                    if (mTopicPopupwindow == null) {
                        mTopicPopupwindow = initmPopupWindowView(mData.topic, TYPE_TOPIC);
                    }
                    showPopWindow(TYPE_TOPIC, v);
                }
            }
        });

        mBinding.more.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (mMorePopupwindow != null && mMorePopupwindow.isShowing()) {
                    dismissPopWindow(mMorePopupwindow);
                } else {
                    if (mMorePopupwindow == null) {
                        mMorePopupwindow = initmPopupWindowView(mData);
                    }
                    showPopWindow(TYPE_MORE, v);
                }
            }
        });
  • 这里主要就是三个方法,dismissPopWindow,initmPopupWindowView,showPopWindow。这三个都是我封装过后的方法,很简单也很好理解
  • showPopWindow方法中的常量参数是为了根据不同的筛选项做不同的操作的,因为每个筛选项下拉出来的菜单view可能不一样

1. 生成下拉菜单 initmPopupWindowView

这里我写了两个一样的方法,因为前两个菜单是listview下拉菜单,最后一个是自定义RadioButton单选菜单

  • 先看生成listview的下拉菜单方法
 private PopupWindow initmPopupWindowView(final List<HotCreditCardPageData.SelectItem> list, final int type) {
        // 获取popwindow布局文件的视图
        View customView = LayoutInflater.from(getContext()).inflate(R.layout.layout_popwindow_list_view,
                null, false);
        MaxHeightListView listView = customView.findViewById(R.id.list_view);
        //填充数据
        if (type == TYPE_BANK) {
            mBankAdapter = new CardSelectAdapter(getContext());
            mBankAdapter.setList(list);
            listView.setAdapter(mBankAdapter);
        } else if (type == TYPE_TOPIC) {
            mTopicAdapter = new CardSelectAdapter(getContext());
            mTopicAdapter.setList(list);
            listView.setAdapter(mTopicAdapter);
        }

        final PopupWindow popupwindow = generatePopupWindow(customView);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (mListener == null) {
                    return;
                }
                if (type == TYPE_BANK) {
                    mParams.put("bank_id", String.valueOf(list.get(position).id));
                    mBinding.allBank.setText(list.get(position).value);
                    mListener.onItemClick(mParams);
                } else if (type == TYPE_TOPIC) {
                    mParams.put("topic_id", String.valueOf(list.get(position).id));
                    mBinding.allTopic.setText(list.get(position).value);
                    mListener.onItemClick(mParams);
                }
                if (popupwindow != null && popupwindow.isShowing()) {
                    dismissPopWindow(popupwindow);
                }
            }
        });
        return popupwindow;
    }

这里向listview中填充数据就不用多说,最基本的。listview的点击事件,选中时将对应选中的id加入map,请求页面数据使用

2.生成更多页面的popupWindow方法

这里的view可以自己定义,数据填充方法都不相同,页面效果图是这样的

image

代码如下

//生成更多页面的Window
    private PopupWindow initmPopupWindowView(HotCreditCardPageData.Select data) {
        // 获取自定义布局文件pop.xml的视图
        moreItemBinding = DataBindingUtil.inflate(LayoutInflater.from(getContext()), R.layout.layout_popwindow_custom_view,
                null, false);
        //填充数据
        moreItemBinding.cardLevel.setText(data.level_title);
        moreItemBinding.cardAnnualFee.setText(data.annual_fee_title);
        setMoreViewData(data.level, moreItemBinding.cardLevelContent, TYPE_LEVEL);
        setMoreViewData(data.annual_fee, moreItemBinding.cardAnnualFeeContent, TYPE_FEE);
        final PopupWindow popupwindow = generatePopupWindow(moreItemBinding.getRoot());
        moreItemBinding.confirm.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int levelId = moreItemBinding.cardLevelContent.getCheckedRadioButtonId();
                int annualFeeId = moreItemBinding.cardAnnualFeeContent.getCheckedRadioButtonId();
                if (levelId == -1) {
                    mParams.put("level_id", "0");
                } else {
                    mParams.put("level_id", String.valueOf(levelId));
                }
                if (annualFeeId == -1) {
                    mParams.put("annual_fee", "0");
                } else {
                    mParams.put("annual_fee", String.valueOf(annualFeeId));
                }
                if (mListener != null) {
                    mListener.onItemClick(mParams);
                }
                if (popupwindow != null && popupwindow.isShowing()) {
                    dismissPopWindow(popupwindow);
                }
            }
        });
        return popupwindow;
    }

这里不要在乎填充数据的方法,可以自行扩展。这里和上个一样都有一个共同的方法generatePopupWindow。这个方法是生成Window的核心方法

代码如下

//根据view初始化popupWindow
    public PopupWindow generatePopupWindow(View customView) {
        final PopupWindow popupwindow = new PopupWindow(customView,
                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        popupwindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        popupwindow.setOutsideTouchable(false);
        popupwindow.setAnimationStyle(R.style.popup_animation);
        popupwindow.setFocusable(false);
        //弹出框消失,筛选条颜色和箭头归位
        popupwindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                resetSelectView();
            }
        });
        return popupwindow;
    }
  • 这里一定要注意,setOutsideTouchable一定要置为false,是为了点击Window外部区域自身不消失,因为这样我们可以很灵活的控制Window的消失时机
  • 同时,setFocusable(false)也很重要,因为如果设置为true,在三个item之间切换时候,Window会点击一下先消失,再次点击才弹出
  • resetSelectView()这个方法是让Window消失时候标题颜色设置为黑色,并且小箭头归位用的,可以无视

3.showPopWindow方法

在点击筛选item时候,如果当前对应的Window在显示,则收起自己的Window,如果别的item对应的Window在显示,则先收起所有Window,在显示自己的Window

public void showPopWindow(int type, View view) {
        //取消所有在显示的Window
        dismissAllPopWindow();
        String selectItemText = "";
        switch (type) {
            case TYPE_BANK:
                selectItemText = mBinding.allBank.getText().toString();
                mBankPopupwindow.showAsDropDown(view);
                setOpenState(mBinding.allBank, mBinding.bankImg);
                mBankAdapter.setSelectItem(selectItemText);
                break;
            case TYPE_TOPIC:
                selectItemText = mBinding.allTopic.getText().toString();
                mTopicPopupwindow.showAsDropDown(view);
                setOpenState(mBinding.allTopic, mBinding.topicImg);
                mTopicAdapter.setSelectItem(selectItemText);
                break;
            case TYPE_MORE:
                setOpenState(mBinding.more, mBinding.moreImg);
                mMorePopupwindow.showAsDropDown(view);
                break;
            default:
                break;
        }
        if (mListener != null) {
            mListener.onSelectTitleClick();
        }
    }
  • setOpenState方法就是对应的Window展开时候筛选item颜色变为蓝色,箭头变换方向的动作

4.dismissPopWindow这个就很简单了

//关闭单个popWindow
    public void dismissPopWindow(PopupWindow popupWindow) {
        popupWindow.dismiss();
        if (mListener != null) {
            mListener.closeBackground();
        }
    }

    public void dismissAllPopWindow() {
        if (mBankPopupwindow != null && mBankPopupwindow.isShowing()) {
            mBankPopupwindow.dismiss();
        }
        if (mTopicPopupwindow != null && mTopicPopupwindow.isShowing()) {
            mTopicPopupwindow.dismiss();
        }
        if (mMorePopupwindow != null && mMorePopupwindow.isShowing()) {
            mMorePopupwindow.dismiss();
        }
    }

最后附加上下拉菜单进出的动画,需要的直接复制即可

设置展开收起动画是在初始化PopupWindow时候直接设置的xml文件,这样设置setAnimationStyle:

popupwindow.setOutsideTouchable(false);
popupwindow.setAnimationStyle(R.style.popup_animation);
popupwindow.setFocusable(false);

然后在style.xml里定义进出动画:

    <style name="popup_animation" mce_bogus="1" parent="android:Animation">
        <item name="android:windowEnterAnimation">@anim/dialog_in</item>
        <item name="android:windowExitAnimation">@anim/dialog_out</item>
    </style>

最后在res下的anim文件夹里自定义你的进出动画文件即可:
dialog_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:fillEnabled="true" >

    <scale
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="0.0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="0%"
        android:toXScale="1.0"
        android:toYScale="1.0" >
    </scale>

    <alpha
        android:duration="180"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

dialog_out.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillEnabled="true"
    android:fillAfter="true">

    <scale
        android:duration="200"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="0%"
        android:toXScale="1.0"
        android:toYScale="0.0" >
    </scale>

    <alpha
        android:duration="180"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />

</set>

完整的demo例子在GitHub

https://github.com/yinghuihou/PopupWindow

刚开始写博客写的不好大家将就着看看吧,有疑问或者意见可留言

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,我会尽力回答你的问题。 要实现类似微信聊天消息长按弹出菜单的效果,可以使用 PopupWindow 组件来实现。下面是实现的大致步骤: 1. 在布局文件中定义一个包含要弹出的菜单项的布局文件,例如一个 LinearLayout。 2. 在代码中创建 PopupWindow 对象,并设置其内容为上一步中定义的布局文件。 3. 设置 PopupWindow 的大小、位置、背景等属性。 4. 监听长按事件,在长按事件中弹出 PopupWindow。 5. 监听 PopupWindow 中菜单项的点击事件,处理相应的逻辑。 具体实现细节可以参考以下代码: ``` // 定义 PopupWindow PopupWindow popupWindow = new PopupWindow(context); View contentView = LayoutInflater.from(context).inflate(R.layout.menu_layout, null); popupWindow.setContentView(contentView); popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // 监听长按事件 view.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { // 显示 PopupWindow int[] location = new int[2]; v.getLocationOnScreen(location); popupWindow.showAtLocation(v, Gravity.NO_GRAVITY, location[0], location[1] - popupWindow.getHeight()); return true; } }); // 监听菜单项的点击事件 TextView menuItem = contentView.findViewById(R.id.menu_item); menuItem.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 处理菜单项的逻辑 popupWindow.dismiss(); } }); ``` 希望这个回答能够帮到你,如果还有其他问题,可以继续问我。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值