Android 敏捷开发助手
博客创建时间: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)
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()
}
}
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源码
相关链接:
扩展链接:
扩展训练:
博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !