Day09 - 使用PopupWindow显示搜寻结果

今天来把昨天的搜寻结果给显示出来,并让使用者选择要去哪个看板。显示的部分我想使用PopupWindow,单纯觉得适合且过去比较少用到,顺便练习看看。

PopupWindow

在看完相关资料後,其实就是把它当作一个用来显示View的框架。在使用上先自行创建View後利用建构子设进PopupWindow即可。

建立子View

layout(layout_search_board_result.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/one_grid_unit"
    android:background="@color/transparent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="@dimen/one_grid_unit"
        android:background="@color/black" />

    <TextView
        android:id="@+id/noResultLabel"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="@dimen/one_grid_unit"
        android:background="@color/black"
        android:gravity="center"
        android:text="查无看板"
        android:textColor="@color/text_normal"
        android:textSize="20sp"
        android:textStyle="bold"
        android:typeface="monospace"
        android:visibility="gone" />

</FrameLayout>

基本上就是一个RecyclerView来显示解析出来的看板列表,若列表为空的话就改显示"查无看板"的TextView。

inflate view

@SuppressLint("InflateParams")
private fun showSearchResult(boardList: List<String>) {
    val view =
        LayoutInflater.from(requireContext())
            .inflate(R.layout.layout_search_board_result, null)
    // ...
}

接着根据传入的boardList是否为空来决定要显示RecyclerView或是TextView

val height = if (boardList.isEmpty()) {
    view.noResultLabel.visibility = View.VISIBLE
    view.recyclerView.visibility = View.GONE
    120f.dpToPx(requireContext())
} else {
    val adapter = SearchBoardResultAdapter(boardList)
    adapter.onBoardClickListener = { board ->
        popupWindow?.dismiss()
        searchBoardInput.setText(board)
    }
    view.recyclerView.setHasFixedSize(true)
    view.recyclerView.layoutManager = LinearLayoutManager(requireContext())
    view.recyclerView.adapter = adapter
    360f.dpToPx(requireContext())
}

RecyclerView的创建跟内容没啥特别的就不多贴了,这段if/else除了确定要显示哪个View以外也同时给定的height值,这是在接下来创建popupWindow时会需要带入的参数。

而在计算height时我是使用扩展函数,将预期的DP值转为PX:

fun Float.dpToPx(context: Context): Int {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this,
        context.resources.displayMetrics
    ).toInt()
}

onBoardClickListener是我在SearchBoardResultAdapter内宣告的函数变数,用来回传itemView点击时所点的看板名称,点击回传後就直接把所选的看板放回EditText(searchBoardInput)。

最後就是创建PopupWindow并显示,整体的function内容如下

@SuppressLint("InflateParams")
private fun showSearchResult(boardList: List<String>) {
    val view =
        LayoutInflater.from(requireContext())
            .inflate(R.layout.layout_search_board_result, null)

    val height = if (boardList.isEmpty()) {
        view.noResultLabel.visibility = View.VISIBLE
        view.recyclerView.visibility = View.GONE
        120f.dpToPx(requireContext())
    } else {
        val adapter = SearchBoardResultAdapter(boardList)
        adapter.onBoardClickListener = { board ->
            popupWindow?.dismiss()
            searchBoardInput.setText(board)
        }
        view.recyclerView.setHasFixedSize(true)
        view.recyclerView.layoutManager =
            LinearLayoutManager(requireContext())
        view.recyclerView.adapter = adapter
        360f.dpToPx(requireContext())
    }

    popupWindow = PopupWindow(
        view, // view
        ViewGroup.LayoutParams.MATCH_PARENT, // width
        height, // height
        true // focusable
    )
    popupWindow?.showAsDropDown(searchBoardInput)
}

值得一提的是建构函数最後需带入focusable的布林值,以我的View来说若focusable为false的话影响只在点击Window外的其他区域或按back时无法自动关闭Window。

最终操作画面会像这样:
https://imgur.com/YQp4EVU.gif


<<:  Day 9 - Container With Most Water

>>:  [重构倒数第07天] - 不用靠後端的 client 端上传图片预览图

JavaScript Day24 - Promise(1)

ES6:Promise Promise:代表一个即将成功或失败的非同步操作 会有这几状态: 搁置 (...

Day30 结语

完结,撒花~~~~~~~~~~· 谢谢这三十天以来看这三十篇废文的各位, 其实很多细节没有能够提及,...

桌面端 YouTube 影片下载器--〖2022亲测〗

接下来为您介绍 10 款不错的第三方 YouTube 下载软件!让我们来看看哪个软件才是 2022 ...

Ruby on Rails 实体方法与类别方法

依据方法作用的对象不同,有分实体方法(instance method)及类别方法(class met...

[Day23] Rust 直接使用资料库语法操作资料库 (Part2)

那今天不说废话了直接开始 延续昨天的 我们已经建立出一个 main function 和 新增的 f...