Android uses PopupWindow to implement bottom popup function

Time:2022-8-16

1. Knowledge points

I will not expand all the specific ways of using PopupWindow or view animation in detail, but only introduce a general process and some knowledge points of use. The specific introduction is below.Design implementationtold in

(1) PopupWindow

1. Initialize

  • Load the layout of the popup
  • Instantiate PopupWindow and pass in the width and height of the layout and popup window
  • Operations on the controls in the layout
  • Some settings on the layout itself
// Load the layout of the popup
pwView = LayoutInflater.from(this).inflate(R.layout.pw_search_engine, null, false)
//Instantiate PopupWindow
popupWindow = PopupWindow(
 pwView,
 ViewGroup.LayoutParams.MATCH_PARENT,
 ViewGroup.LayoutParams.WRAP_CONTENT
)
// Operations on the controls in the layout
initRecyclerView()
// some settings for the layout itself
popupWindow.isOutsideTouchable = true
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = R.style.pw_bottom_anim_style
popupWindow.setOnDismissListener {
 backgroundAlpha(1f)
}

2. Show popup

Pop-up window to modify background brightness – darken

// pop up the popup
val rootView = LayoutInflater.from(this).inflate(R.layout.activity_main,null)
popupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, 0)
// Modify background brightness - darken
backgroundAlpha(0.7f)

3. Close the popup

  • close the popup
  • Modify Background Brightness – Brighten
// close the popup
popupWindow.dismiss() 
// Modify the background brightness - brighten
backgroundAlpha(1f)

4. Background brightness modification

// control background brightness
private fun backgroundAlpha(bgAlpha: Float) {
 val lp = window.attributes
 lp.alpha = bgAlpha //0.0-1.0
 window.attributes = lp
}

(2) View animation

Define and animate the view using XML tags:

1. XML tags

  • alpha gradient transparency
  • scale Gradient size scaling
  • translate screen position move
  • rotate screen transfer rotation
  • set defines the animation set

2. Add animation to PopupWindow


popupWindow.animationStyle = R.style.pw_bottom_anim_style

Second, the interface effect

底部弹窗

3. Design and Implementation

(1) Demand analysis

  • Click the home button to pop up the bottom popup
  • Click the popup engine to display the engine name as a Toast and close the popup
  • Click outside the popup to close the popup

(2) List of documents

文件列表

(3) Layout design

1. Main interface style design

(activity_main.xml)

The style of the main interface is very simple, just an ordinary button

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context=".MainActivity">

 <Button
  android:id="@+id/btn"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_margin="14dp"
  android:text=&quot;Click - bottom popup&quot;
  android:textColor="@color/white"/>

</LinearLayout>

2. Pop-up style design

(pw_search_engine.xml)

The layout of the popup style is also very simple, which is a basic linear layout of RecyclerView
It is worth noting that the most basic layoutManager can be specified by specifyingapp:layoutManagerto fulfill


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:orientation="vertical"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="@color/white">

 <androidx.recyclerview.widget.RecyclerView
  android:id="@+id/recyclerView"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:overScrollMode="never"
  app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

</LinearLayout>

3. Pop-up list item style design

(item_search_engine.xml)

The list item, because it is a Demo example, simply uses a horizontal layout with a built-in icon icon and a name TextView to display


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="horizontal"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:gravity="center">

 <ImageView
  android:id="@+id/iconIV"
  android:layout_width="36dp"
  android:layout_height="36dp"
  android:layout_margin="14dp" />
  
 <TextView
  android:id="@+id/titleTV"
  android:layout_width="0dp"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:layout_marginEnd="36dp"
  android:maxLines="1"
  android:ellipsize = "end"
  android:textColor="@color/black"
  android:textSize="16sp" />

</LinearLayout>

4. Pop-up animation design

(pw_bottom_in.xml 与 pw_bottom_out.xml)

<!--pw_bottom_in.xml-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <!--
  Pan animation
  duration-- animation duration
  android:fromXDelta,android:fromYDelta--play x,y
  android:toXDelta,android:toYDelta--end point x,y
 -->
 <translate
  android:duration="300"
  android:fromXDelta="0"
  android:fromYDelta="1000"
  android:toXDelta="0"
  android:toYDelta="0" />
</set>
<!--pw_bottom_out.xml-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate
  android:duration="300"
  android:fromXDelta="0"
  android:fromYDelta="0"
  android:toXDelta="0"
  android:toYDelta="1000" />
</set>

(4) Data storage and loading

1. Data storage(UIData.kt and arrays.xml)

// The data entity class of the search engine, including two attributes: name and icon resource id
data class SearchEngine(
 val title : String,
 val res : Int
)

Store the name of the search engine and the corresponding icon resource in the form of a string array

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <string-array name="search_engine_title_list">
  <item>Baidu</item>
  <item>Sogou</item>
  <item>360</item>
  <item>must</item>
  <item>What</item>
 </string-array>
 <string-array name="search_engine_res_list">
  <item>@drawable/ic_baidu</item>
  <item>@drawable/ic_sougou</item>
  <item>@drawable/ic_360</item>
  <item>@drawable/ic_bing</item>
  <item>@drawable/ic_shenma</item>
 </string-array>
</resources>

2. Data loading(MainActivity.kt)

private lateinit var engines : MutableList<SearchEngine>

private fun initData() {
	// initialize the engine list
 engines = mutableListOf()
	// Get the array of engine names from arrays.xml
 val titleList = resources.getStringArray(R.array.search_engine_title_list)
	// Since the resource id is an integer, but a string is stored in arrays.xml,
	// So first initialize an array of resource ids, the element type is integer
 val iconResList : MutableList<Int> = mutableListOf()
 // Load the related engine resource list through the type array, traverse the elements, pass in the index value,
 // Get the resource id of the icon by calling getResourceId(index,0) and store it in the id array just initialized
 val resList: TypedArray = 
 resources.obtainTypedArray(R.array.search_engine_res_list)
 for (index in 0 until resList.length()) {
  iconResList.add(resList.getResourceId(index,0))
 }
 // Remember to call recycle() in time to recycle the TypedArray object
 resList.recycle()
	// Loop, use the obtained title and id to generate the corresponding search engine object and store it in the search engine list
 for (index in titleList.indices){
  if (index < iconResList.size){
   engines.add(SearchEngine(titleList[index],iconResList[index]))
  }
 }
}

(5) Remaining content

The content code mentioned above will not be shown here; because the focus is on the implementation of the bottom pop-up window, the implementation of the RecyclerView in the pop-up window layout will not be introduced too much.

1. AdapterForSearchEngine.kt popup list adapter

class AdapterForSearchEngine (dataList: MutableList<SearchEngine>) :
  RecyclerView.Adapter<AdapterForSearchEngine.ViewHolder>() {

 // search engine data collection
 private val mDataList: MutableList<SearchEngine> = mutableListOf()

 init {
  // Initialization is mainly to initialize the data
  mDataList.clear()
  mDataList.addAll(dataList)
 }

 // ViewHolder facilitates item reuse
 class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {}

 // Get the number of items in the list
 override fun getItemCount(): Int {
  return mDataList.size
 }
 
 // bind view and data
 override fun onBindViewHolder(holder: ViewHolder, position: Int) {
  val engine: SearchEngine = mDataList[position]
  holder.itemView.titleTV.text = engine.title
  holder.itemView.iconIV.setImageResource(engine.res)

  holder.itemView.setOnClickListener {
   listener?.click(engine)
  }
 }

 // Create ViewHolder instance
 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
  val view: View = LayoutInflater.from(parent.context).inflate(R.layout.item_search_engine, parent, false)
  return ViewHolder(view)
 }

 // click event
 private var listener :OnItemClickListener? = null

 interface OnItemClickListener {
  fun click(engine: SearchEngine)
 }

 fun setOnItemClickListener(listener: OnItemClickListener) {
  this.listener = listener
 }
}

2. MainActivity.kt

class MainActivity : AppCompatActivity() {

 private lateinit var engines : MutableList<SearchEngine>

 private lateinit var popupWindow : PopupWindow
 private lateinit var pwView : View

 private lateinit var mAdapter : AdapterForSearchEngine

 override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)
  // Initialization data
  initData()
  // Initialize PopupWindow
  initPopupWindow()
  // button click event
  btn.setOnClickListener {
   // show popup
   showPopWindow()
  }
 }

 private fun initPopupWindow() {
  // Load the popup layout
  pwView = LayoutInflater.from(this).inflate(R.layout.pw_search_engine, null, false)
  // Instantiate PopupWindow
  popupWindow = PopupWindow(
    pwView,
    ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.WRAP_CONTENT
  )
  // Initialize the popup list
  initRecyclerView()
  // set popupWindow
  popupWindow.isOutsideTouchable = true
  popupWindow.isTouchable = true
  popupWindow.isFocusable = true
  // Load popup animation
  popupWindow.animationStyle = R.style.pw_bottom_anim_style
  // Set the pop-up window to close the monitor - restore the brightness
  popupWindow.setOnDismissListener {
   backgroundAlpha(1f)
  }
 }

 private fun showPopWindow() {
  val rootView = LayoutInflater.from(this).inflate(
    R.layout.activity_main,
    null
  )
  // set the popup position
  popupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, 0)
  // dim the background brightness
  backgroundAlpha(0.7f)
 }

 // control background brightness
 private fun backgroundAlpha(bgAlpha: Float) {
  val lp = window.attributes
  lp.alpha = bgAlpha //0.0-1.0
  window.attributes = lp
 }

 private fun initRecyclerView() {
  mAdapter = AdapterForSearchEngine(engines)
  pwView.recyclerView?.adapter = mAdapter
  mAdapter.setOnItemClickListener(object : AdapterForSearchEngine.OnItemClickListener{
   override fun click(engine: SearchEngine) {
    Toast.makeText([email protected], engine.title, Toast.LENGTH_SHORT).show()
    popupWindow.dismiss()
   }
  })
 }

 private fun initData() {
  // initialize the engine list
  engines = mutableListOf()
  // Get the array of engine names from arrays.xml
  val titleList = resources.getStringArray(R.array.search_engine_title_list)
  // Since the resource id is an integer, but a string is stored in arrays.xml,
  // So first initialize an array of resource ids, the element type is integer
  val iconResList : MutableList<Int> = mutableListOf()
  // Load the related engine resource list through the type array, traverse the elements, pass in the index value,
  // Get the resource id of the icon by calling getResourceId(index,0) and store it in the id array just initialized
  val resList: TypedArray =
    resources.obtainTypedArray(R.array.search_engine_res_list)
  for (index in 0 until resList.length()) {
   iconResList.add(resList.getResourceId(index,0))
  }
  // Remember to call recycle() in time to recycle the TypedArray object
  resList.recycle()
  // Loop, use the obtained title and id to generate the corresponding search engine object and store it in the search engine list
  for (index in titleList.indices){
   if (index < iconResList.size){
    engines.add(SearchEngine(titleList[index],iconResList[index]))
   }
  }
 }

}

So far, this article about Android using PopupWindow to realize the bottom pop-up function is introduced. For more related Android PopupWindow bottom pop-ups, please search for previous articles of developpaer or continue to browse related articles below. I hope you will support developpaer more in the future!