安卓自带的Spinner局限性较大,基本不能满足开发样式要求,当前又没有成熟的相关框架,所以决定自己使用PopupWindow实现一个下拉菜单
布局:
新建xml文件:layout_dropdown_menu
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_marginLeft="12dp"
android:id="@+id/ly_dropdown_tab">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
android:layout_gravity="center_vertical"
android:id="@+id/tv_dropdown_title"/>
<ImageView
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="4dp"
android:src="@drawable/ic_up"
android:layout_marginStart="4dp"
android:id="@+id/iv_dropdown_icon"/>
LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_alignParentRight="true"
android:layout_marginRight="12dp"
android:layout_alignParentEnd="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
android:layout_gravity="center_vertical"
android:text="@string/choose"/>
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:src="@drawable/ic_choose"
android:layout_gravity="center_vertical"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp" />
LinearLayout>
RelativeLayout>
在需要使用的地方引用
<include layout="@layout/layout_dropdown_menu"/>
为下面布局添加一个遮罩的View:
ps:比如我下拉框下面是一个rv,使用FrameLayout布局,为rv添加一个相同大小的View,来实现遮罩效果(当然有更好的实现方式欢迎私信我)
<include layout="@layout/layout_dropdown_menu"/>
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp">
<com.scwang.smart.refresh.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/rv"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mask_view"
android:visibility="gone"
android:background="@color/shadow_bg"/>
FrameLayout>
com.scwang.smart.refresh.layout.SmartRefreshLayout >
最后添加动画文件:
style.xm
<resources>
<style name="popwin_anim" parent="android:Animation">
- "android:windowEnterAnimation"
>@anim/pop_enter_anim
- "android:windowExitAnimation"
>@anim/pop_exit_anim
style>
resources>
pop_enter_anim.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<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>
pop_exit_anim.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<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>
代码:
新建一个DropdownMenu类,进行基本封装
package cn.edu.swu.reptile_android.ui.base
import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import cn.edu.swu.reptile_android.R
class DropdownMenu (
val context: Context?
) {
lateinit var maskView: View
lateinit var tabView: LinearLayout
lateinit var tabTitle: TextView
lateinit var tabIcon: ImageView
lateinit var data: List<String>
private var onItemSelectListener: OnItemSelectListener? = null
public fun init(view: View,data: List<String>) {
this.data = data
tabView = view.findViewById(R.id.ly_dropdown_tab)
tabTitle = view.findViewById(R.id.tv_dropdown_title)
tabIcon = view.findViewById(R.id.iv_dropdown_icon)
maskView = view.findViewById(R.id.mask_view)
//默认显示item
tabTitle.text = data[0]
tabView.setOnClickListener {
//角标变化
tabIcon.setImageResource(R.drawable.ic_down)
//遮罩层动画
maskView.startAnimation(
AnimationUtils.loadAnimation(
context,
R.anim.view_mask_enter_anim
)
)
//弹出popWin
showPopupWindow(tabView)
}
}
public fun setOnItemSelectListener(onItemSelectListener: OnItemSelectListener){
this.onItemSelectListener = onItemSelectListener
}
interface OnItemSelectListener{
fun onItemSelect(position: Int)
fun onDismiss()
}
private fun showPopupWindow(tabView: View) {
val contentView: View =
LayoutInflater.from(context).inflate(R.layout.popup_dropdown_menu, null)
val popWindow = PopupWindow(
contentView, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, true
)
popWindow.contentView = contentView
//RV
val dropdownRv: RecyclerView = contentView.findViewById(R.id.rv)
dropdownRv.layoutManager = LinearLayoutManager(contentView.context)
val adapter = BaseAdapter(R.layout.item_rv_dropdown, data) { view, s ->
view.findViewById<TextView>(R.id.tv_item_title).text = s
if (s == tabTitle.text) { //当前选中的item
view.findViewById<TextView>(R.id.tv_item_title).setTextColor(Color.BLACK)
view.findViewById<ImageView>(R.id.iv_item_icon).visibility = View.VISIBLE
}
}
//select item
adapter.setOnItemClickListener(object : BaseAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
popWindow.dismiss()
tabTitle.text = data[position]
//加载数据
//暴露给调用者自定义选项逻辑
onItemSelectListener?.onItemSelect(position)
}
})
dropdownRv.adapter = adapter
//弹出动画
popWindow.animationStyle = R.style.popwin_anim
//遮罩效果
maskView.visibility = View.VISIBLE
popWindow.setOnDismissListener {
maskView.visibility = View.GONE
tabIcon.setImageResource(R.drawable.ic_up)
onItemSelectListener?.onDismiss()
}
//弹出窗口
popWindow.showAsDropDown(tabView)
}
}
最后,在Activity或者Fragment中使用:
private fun initDropdownMenu(view: View) {
val dropdownMenu = DropdownMenu(context)
dropdownMenu.init(view, vm.dropdownData)
dropdownMenu.setOnItemSelectListener(object : DropdownMenu.OnItemSelectListener {
override fun onItemSelect(position: Int) {
//点击item后逻辑(加载数据?)
}
override fun onDismiss() {
}
})
}
自己实现的一个基于PopupWindow和RecyclerView实现的下拉菜单,几乎自己实现,可能想法不太成熟,比如遮罩的实现和对于控件的一些封装还存在一些问题,这里仅提供一些思路,有更好的想法欢迎私信讨论。