万能匹配的自定义下拉列表

Android 敏捷开发助手

  1. Lottie动画 轻松使用
  2. PNG、JPG等普通图片高保真转SVG图
  3. Android 完美的蒙层方案
  4. Android MMKV框架引入使用
  5. 强大无匹的自定义下拉列表
  6. Google Protobuf 实践使用开发

博客创建时间:2021.05.05
博客更新时间:2023.01.28

以Android studio build=7.0.0,SDKVersion 31来分析讲解。如图文和网上其他资料不一致,可能是别的资料版本较低而已


前言

对于下拉列表大家都不会陌生,经常使用。虽然Android自带有spinner控件,但是其使用的效果其实并不理想。如果我们的下拉列表类有数据类型int、String、classA、classB。则我们使用过程中可能需要多个Spinner搭配多个适配器来实现,这样就比较麻烦且重复造轮子,现在给大家推荐一款我封装的万能适配自定义下拉列表PopSpinner,源码请自行下载查看
在这里插入图片描述

        val adapter = StringAdapter(this)
        adapter.setDatas(data)
     
        val adapter = StudentAdapter(this)
        adapter.setDatas(data)
        
        val adapter = PersonAdapter(this)
        adapter.setDatas(data)

PopSpinner 核心

PopListAdapter
下拉内容显示适配器,通过泛型的方式可以适配多种类型实体类,通过PopSpinnerItemClickListener接口的实现可以由使用者决定Item的内容显示某个类的某字段。

如果实际开发中,我们需要的适配器Item只用显示一字段信息,该适配器也是完美的万能百搭。

public class PopListAdapter<T> extends RecyclerView.Adapter<PopListAdapter.ViewHolder> {
    private List<T> list;

    private PopSpinnerItemClickListener<T> listener;
    
    ......
    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, @SuppressLint("RecyclerView") final int position) {
        T t = list.get(position);
        if (listener != null) {
            holder.getBinding().textView.setText(listener.setContent(t));
        } else {
            holder.getBinding().textView.setText("");
        }

        holder.getBinding().getRoot().setOnClickListener(v -> {
            if (listener != null) {
                listener.onItemSelected(position, t, listener.setContent(t));
            }
        });
        //立即执行绑定
        holder.getBinding().executePendingBindings();
    }
    
   ......
}

PopWindow
自定义的PopupWindow,下拉的List将在此PopWindow中显示

class PopWindow<T>(private val mContext: Context) : PopupWindow(mContext) {
    private lateinit var mListView: RecyclerView
    private var mAdapter: PopListAdapter<T>? = null
    fun setAdatper(adapter: PopListAdapter<T>) {
        mAdapter = adapter
        mListView.adapter = mAdapter
    }

    init {
        init()
    }

    private fun init() {
        val view = LayoutInflater.from(mContext).inflate(R.layout.layout_spinner_window, null)
        contentView = view
        width = ViewGroup.LayoutParams.WRAP_CONTENT
        height = ViewGroup.LayoutParams.WRAP_CONTENT

        mListView = view.findViewById<View>(R.id.listView) as RecyclerView
        // 初始化 RecyclerView
        val layoutManager = LinearLayoutManager(mContext)
        mListView.layoutManager = layoutManager
    }
}

PopSpinner
PopSpinner有一个内容显示的TextView和一个下拉指示ImageView两子控件。该部分主要控制数据的刷新和下拉列表的显示和退出。

class PopSpinner<T> : LinearLayout {
    /**
     * 下拉监听接口
     */
    var listener: PopSpinnerItemClickListener<T>? = null

    /**
     * 弹出view
     */
    private lateinit var view: View

    constructor(context: Context) : super(context) {
        mcontext = context
        init()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        mcontext = context
        init()
    }

    private fun init() {
        val mInflater = LayoutInflater.from(mcontext)
        view = mInflater.inflate(R.layout.layout_pop_spinner, null)
        addView(view)
        tvValue = view.findViewById<View>(R.id.tv_value) as TextView
        btDropdown = view.findViewById<View>(R.id.btnDropdown) as ImageView
        tvValue.setOnClickListener(spinnerOnClickListener)
        btDropdown.setOnClickListener(spinnerOnClickListener)


        mSpinerPopWindow = PopWindow(mcontext)
        mAdapter = PopListAdapter<T>()
        mAdapter.setListener(object : PopSpinnerItemClickListener<T> {
            override fun onItemSelected(position: Int, t: T, value: String) {
                tvValue.text = value
                listener?.onItemSelected(position, t, value)
            }

            override fun setContent(t: T): String {
                val value = listener?.setContent(t)
                return value ?: ""
            }
        })
    }

    /**
     * 设置控件是否可用
     *
     * @param enable true:可用
     */
    fun setEnable(enable: Boolean) {
        tvValue.isEnabled = enable
        btDropdown.isEnabled = enable
    }

    /**
     * 自定义的 MySpinner 被点击
     */
    private val spinnerOnClickListener = OnClickListener {
        isShowing = if (isShowing) {
            dismiss()
            false
        } else {
            showSpinWindow()
            true
        }
    }

    private fun showSpinWindow() {
        mAdapter.setDatas(mItems)
        mSpinerPopWindow.setAdatper(mAdapter)

        this.mSpinerPopWindow.width = view.width
        if (mItems == null || mItems?.size == 0) {
            this.mSpinerPopWindow.height = 0
        }
        this.mSpinerPopWindow.showAsDropDown(view)
    }

    fun dismiss() {
        this.mSpinerPopWindow.dismiss()
    }
}

PopSpinner 使用

Activity中使用

public class MainActivity extends AppCompatActivity {
    PopSpinner<String> sp1;
    PopSpinner<Student> sp2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sp1 = findViewById(R.id.pop1);
        sp2 = findViewById(R.id.pop2);

        initData1();
        initData2();
    }

    void initData1() {
        String[] names = new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"};
        sp1.setData(Arrays.asList(names));
        sp1.setListener(new PopSpinnerItemClickListener<String>() {
            @Override
            public void onItemSelected(int position, @NonNull String s, @NonNull String value) {
                Toast.makeText(MainActivity.this, "你点击的是:" + value, Toast.LENGTH_SHORT).show();
                sp1.dismiss();
            }

            @NonNull
            @Override
            public String setContent(@NonNull String s) {
                return s;
            }
        });
        sp1.setText("8");
    }

    void initData2() {

        ArrayList<Student> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student student = new Student();
            student.setAge(10 + i);
            student.setName("学生 " + i);
            list.add(student);
        }

        sp2.setData(list);
        sp2.setListener(new PopSpinnerItemClickListener<Student>() {
            @Override
            public void onItemSelected(int position, @NonNull Student s, @NonNull String value) {
                Toast.makeText(MainActivity.this, "你点击的是:" + value, Toast.LENGTH_SHORT).show();
                sp2.dismiss();
            }

            @NonNull
            @Override
            public String setContent(@NonNull Student s) {
                return s.getName();
            }
        });
        sp2.setText("学生 7");
    }
}

PopSpinner一定要实现PopSpinnerItemClickListener接口,其中onItemSelected()是列表Item处理逻辑。setContent()方法非常重要,该方法的返回值决定着下拉列表中的内容显示class的哪个字段内容。

布局配置

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    tools:context="com.xuanyuan.MainActivity">

    <com.xuanyuan.popspinner.PopSpinner
        android:id="@+id/pop1"
        android:layout_margin="3dp"
        android:layout_width="match_parent"
        android:layout_height="40dp" />

    <com.xuanyuan.popspinner.PopSpinner
        android:id="@+id/pop2"
        android:layout_margin="3dp"
        android:layout_width="match_parent"
        android:layout_height="40dp" />
</LinearLayout>

PopSpinner控件的使用灰常简单,可以匹配多种类型的数据


总结

PopSpinner经过封装和修改后,对于单字段显示的下拉列表 是万能匹配的,无论提供的数据是何种类型。希望这样的小工具类控件能帮助大家少造轮子多干事。

github源码
gitee源码


相关链接

  1. Lottie动画 轻松使用
  2. PNG、JPG等普通图片高保真转SVG图
  3. Android 完美的蒙层方案
  4. Android MMKV框架引入使用
  5. 强大无匹的自定义下拉列表
  6. Google Protobuf 实践使用开发

扩展链接:

  1. Android CameraX 使用入门
  2. Android 今日头条屏幕适配详细使用攻略

扩展训练:

  1. 怎么使用PopSpinner
  2. 为何能适配多种类型类,完成万能适配

博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值