美文网首页Android开发安卓
WPopup - 一个简单并且可以高度定制的Popupwindo

WPopup - 一个简单并且可以高度定制的Popupwindo

作者: c37d344afd22 | 来源:发表于2018-09-12 15:24 被阅读9次

首先写这个功能的初衷是因为公司项目上有很多弹出框的样式,类似于这样的

去Github上搜了一圈发现没有比较通用、简单的库,大多数都封装了很多样式,比如类似于Dialog之类的,我觉得Dialog就用Dialog写就好了啊,没有必要用PopupWindow来写啊。

但是像这种简单的UI用的还是很多的,于是乎决定自己写一个简单的

所以便有了简单通用并且可以高度定制的Wpopup

GitHub地址,欢迎Star、Fork、issue!!!

GitHub地址,欢迎Star、Fork、issue!!!

GitHub地址,欢迎Star、Fork、issue!!!

首先决定都要有什么功能

  1. 基本的PopupWindow的功能要有
  2. 自动根据锚点View来设置弹出位置(也可以手动设置位置)
  3. 可以根据用户手指点击位置来自动弹出(也可以手动设置位置)
  4. 有一个通用的、基础的UI,必须可以高度定制
  5. 如果想要自定义UI那也必须简单

开始实现

基本的PopupWindow的功能要有

这个比较简单,只需要封装好一些方法到BasePopup即可。比如:

  • 设置contentView
  • 设置背景变暗
  • 设置点击外部不能dismiss
  • ...

说起来比较简单,其实除了第一个,剩下的都很坑

自动根据锚点View来设置弹出位置(也可以手动设置位置)

这个功能是个小难点,因为平时我们写PopupWindow的时候最后都是用的showAsDropDown,但是这个方法缺点就是如果弹出的View比较宽,但是你的锚点View又在边缘,那么有可能就出现显示不齐全的情况,而且想要弹出到哪里还要自己计算偏移量,很是麻烦。

那我们是不是可以改进一下,不用showAsDropDown,来改用showAtLocation,这样一来,所有的参数都由我们自己来设置,那么这个方法就不会出现显示不全的情况。首先我们有两个需求

  1. 自动根据锚点View来设置弹出位置
  2. 根据锚点View手动设置弹出位置

对于这两个需求,首先我们要做的就是要拿到这个View的位置,那么什么时候拿最好呢?我考虑了一下ListView和RecyclerView的情况,那当然是show的时候传入View最好,因为item点击回调才知道是哪个View,所以这就简单了,我们计算一下位置即可。

showAtLocation里的X和Y都是弹出View的左上角坐标

可以根据用户手指点击位置来自动弹出(也可以手动设置位置)

上面说根据锚点View来设置弹出位置比较好做,是因为只有一个View,但是根据手指位置弹出,谁也不知道你点击的是一个RecyclerView还是ListView,所以这里就重点说一下这两个。

想要让Rv/Lv也能设置为根据手指点击位置自动弹出,那么就必须拦截Rv/Lv的事件,具体如何拦截事件呢?

if (popParams.longClickView != null) {
            // 判断是否是ListView或者GridView
            if (popParams.longClickView!! is AbsListView) {
                // 拦截点击事件获取坐标
                (popParams.longClickView!! as AbsListView).setOnTouchListener { v, event ->
                    when (event.action) {
                        MotionEvent.ACTION_DOWN -> {
                            clickLocation[0] = event.rawX
                            clickLocation[1] = event.rawY
                        }
                    }
                    false
                }
            } else if (popParams.longClickView!! is RecyclerView) {
                // 判断是不是RecyclerView 拦截点击事件获取坐标
                (popParams.longClickView!! as RecyclerView).addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
                    override fun onTouchEvent(p0: RecyclerView, p1: MotionEvent) {

                    }

                    override fun onInterceptTouchEvent(p0: RecyclerView, event: MotionEvent): Boolean {
                        when (event.action) {
                            MotionEvent.ACTION_DOWN -> {
                                clickLocation[0] = event.rawX
                                clickLocation[1] = event.rawY
                            }
                        }
                        return false
                    }

                    override fun onRequestDisallowInterceptTouchEvent(p0: Boolean) {

                    }

                })
            } else
                popParams.longClickView!!.setOnTouchListener(this)
        }

必须在创建WPopup的时候就传入一个ClickView,让我来知道你想根据手指点击弹出的View是什么样的View。
因为如果在点击的时候传入ClickView,那么这个时候注册拦截事件就已经晚了

剩下的就和上面一样了,计算坐标,计算距离等等

有一个通用的、基础的UI,必须可以高度定制

这部分就是WPopup

首先搭建好一个弹出的View,这里面有两个部分,一个是弹出的列表(用RecyclerView编写),一个是小三角,这两部分的背景全部都用Drawable来编写,好处是体积小,并且可以自定义颜色,Rv的背景很简单,但是小三角就比较复杂,以下是Drawable画三角的代码

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 正三角 -->
    <item>
        <rotate
            android:fromDegrees="45"
            android:pivotX="-40%"
            android:pivotY="80%">
            <shape android:shape="rectangle">
                <size
                    android:width="16dp"
                    android:height="16dp" />
                <solid android:color="#CC000000" />
            </shape>
        </rotate>
    </item>
</layer-list>

这里就是一个正方形然后旋转的结果,但是变更颜色的时候很复杂,一般我们编写的Drawable是这样的:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#CC000000"/>
    <corners android:radius="5dp"/>
</shape>

这种一般情况下想要变更颜色就可以(view.background as GradientDrawable).setColor(xxx),然鹅,我们的小三角是一个layer-list,而且多层嵌套,这种情况下想要改颜色,那么就只能这样:

val layerListDrawable = view.background as LayerDrawable
val rotateDrawable = layerListDrawable.getDrawable(0) as RotateDrawable
(rotateDrawable.rotateDrawable as GradientDrawable).setColor(xxx)
  1. 首先获得LayerDrawable
  2. 然后通过LayerDrawable.getDrawable(position)来获得下面的Drawable
  3. 再用获得的Drawable.XXXDrawable强制转换为GradientDrawable来设置颜色

虽然View写好了,但是我们弹出的时候是自动设置位置,而且这个小三角是动态添加到Rv上的,所以不管Rv多宽多长,小三角始终是在中间的位置。那么这个时候就必须要设置一下这个小三角的边距才可以。

而且我观察了一部分的APP,他们只有在上下弹出时候有小三角,左右弹出是没有的,所以我的WPopup也是这样设计的,只有上下才有,左右没有。(如果你们有意见,我也可以改23333)

要弹出的View已经画好了,那下面就是可以让用户自己定义这个View里所有的参数。

在设计框架的时候想到了Android原生的AlertDialog,AlertDialog就是用Builder模式编写的,在Build里保存参数,然后在create()的时候传给Wpopup,这样就完成了所有的参数的传递,并且隐藏了所有的细节


这样一个简单又可以高度定制的WPopup就大功告成啦!

GitHub地址,欢迎Star、Fork、issue!!!

GitHub地址,欢迎Star、Fork、issue!!!

GitHub地址,欢迎Star、Fork、issue!!!

相关文章

网友评论

    本文标题:WPopup - 一个简单并且可以高度定制的Popupwindo

    本文链接:https://www.haomeiwen.com/subject/zcnvgftx.html