iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Mobile Development

花30天做個Android小專案系列 第 9

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


上一篇
Day08 - 尋找看板
下一篇
Day10 - 補漏
系列文
花30天做個Android小專案30

尚未有邦友留言

立即登入留言