一、知识点

不详细展开 PopupWindow 或者视图动画的所有具体使用方式,仅仅介绍一下使用的一个大概流程和一些知识要点,具体的介绍在下面设计实现中讲述

(一)PopupWindow

1. 初始化

  • 加载弹窗的布局
  • 实例化 PopupWindow 传入布局和弹窗的宽高
  • 对布局里面的控件的操作
  • 对布局本身的一些设置
// 加载弹窗的布局
pwView = LayoutInflater.from(this).inflate(R.layout.pw_search_engine, null, false)
//实例化 PopupWindow
popupWindow = PopupWindow(
 pwView,
 ViewGroup.LayoutParams.MATCH_PARENT,
 ViewGroup.LayoutParams.WRAP_CONTENT
)
// 对布局里面的控件的操作
initRecyclerView()
// 对布局本身的一些设置
popupWindow.isOutsideTouchable = true
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = R.style.pw_bottom_anim_style
popupWindow.setOnDismissListener {
 backgroundAlpha(1f)
}

2. 展示弹窗

弹出弹窗修改背景亮度—变暗

// 弹出弹窗
val rootView = LayoutInflater.from(this).inflate(R.layout.activity_main,null)
popupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, 0)
// 修改背景亮度—变暗
backgroundAlpha(0.7f)

3. 关闭弹窗

  • 关闭弹窗
  • 修改背景亮度—变亮
// 关闭弹窗
popupWindow.dismiss() 
// 修改背景亮度—变亮
backgroundAlpha(1f)

4. 背景亮度修改

// 控制背景亮度
private fun backgroundAlpha(bgAlpha: Float) {
 val lp = window.attributes
 lp.alpha = bgAlpha //0.0-1.0
 window.attributes = lp
}

(二)视图动画

使用 XML 标签定义并使用视图动画:

1. XML 标签

  • alpha 渐变透明度
  • scale 渐变尺寸伸缩
  • translate 画面位置移动
  • rotate 画面转移旋转
  • set 定义动画集

2. 给 PopupWindow 添加动画

popupWindow.animationStyle = R.style.pw_bottom_anim_style

二、界面效果

底部弹窗

三、设计实现

(一)需求分析

  • 点击主页按钮弹出底部弹窗
  • 点击弹窗引擎,以Toast显示引擎名称并关闭弹窗
  • 点击弹窗外部可以关闭弹窗

(二)文件列表

文件列表

(三)布局设计

1. 主界面样式设计

(activity_main.xml)

主界面的样式十分简单,就是一个普通的按钮

<?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="点击——底部弹窗"
  android:textColor="@color/white"/>

</LinearLayout>

2. 弹窗样式设计

(pw_search_engine.xml)

弹窗样式的布局也十分简单,就是一个基本的线性布局的 RecyclerView
值得注意的是,最基本的 layoutManager 可以通过指定 app:layoutManager 来实现

<?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. 弹窗列表 item 样式设计

(item_search_engine.xml)

列表单项,因为是 Demo 示例,所以简单地用一个横向布局,内置一个图标 icon 和一个名称 TextView 来进行展示

<?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. 弹窗动画设计

(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">
 <!--
  平移动画
  duration--动画持续时间
  android:fromXDelta,android:fromYDelta--起始 x,y
  android:toXDelta,android:toYDelta--终点 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>

(四)数据存储与加载

1. 数据存储(UIData.kt 与 arrays.xml)

// 搜索引擎的数据实体类,包含名称和 icon 资源 id 两个属性
data class SearchEngine(
 val title : String,
 val res : Int
)

以字符串数组的形式存储搜索引擎的名称以及对应的图标资源

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <string-array name="search_engine_title_list">
  <item>百度</item>
  <item>搜狗</item>
  <item>360</item>
  <item>必应</item>
  <item>神马</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. 数据加载(MainActivity.kt)

private lateinit var engines : MutableList<SearchEngine>

private fun initData() {
	// 初始化引擎列表
 engines = mutableListOf()
	// 从 arrays.xml 获取引擎名称数组
 val titleList = resources.getStringArray(R.array.search_engine_title_list)
	// 由于资源 id 是整型,但是在 arrays.xml 中存储的是字符串,
	// 所以这里先初始化一个资源 id 的数组,元素类型为整型
 val iconResList : MutableList<Int> = mutableListOf()
 // 通过类型数组加载相关引擎资源列表,遍历其中元素,传入索引值,
 // 通过调用 getResourceId(index,0) 获取 icon 的资源 id 存入刚才初始化的 id 数组中
 val resList: TypedArray = 
 resources.obtainTypedArray(R.array.search_engine_res_list)
 for (index in 0 until resList.length()) {
  iconResList.add(resList.getResourceId(index,0))
 }
 // 记得及时调用 recycle() 回收 TypedArray 对象
 resList.recycle()
	// 循环,用获得的 title 和 id 生成对应的搜索引擎对象,存入搜索引擎列表中
 for (index in titleList.indices){
  if (index < iconResList.size){
   engines.add(SearchEngine(titleList[index],iconResList[index]))
  }
 }
}

(五)剩余内容

上述提及的内容代码,此处将不再进行展示;因为重点是介绍底部弹窗的实现,弹窗布局中的 RecyclerView 的实现就不过多介绍

1. AdapterForSearchEngine.kt 弹窗列表适配器

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

 // 搜索引擎数据集合
 private val mDataList: MutableList<SearchEngine> = mutableListOf()

 init {
  // 初始化 主要是对数据进行初始化
  mDataList.clear()
  mDataList.addAll(dataList)
 }

 // ViewHolder 方便 item 复用
 class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {}

 // 获取列表 item 数量
 override fun getItemCount(): Int {
  return mDataList.size
 }
 
 // 绑定视图与数据
 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)
  }
 }

 // 创建 ViewHolder 实例
 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)
 }

 // 点击事件
 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)
  // 初始化数据
  initData()
  // 初始化 PopupWindow
  initPopupWindow()
  // 按钮点击事件
  btn.setOnClickListener {
   // 显示弹窗
   showPopWindow()
  }
 }

 private fun initPopupWindow() {
  // 加载弹窗布局
  pwView = LayoutInflater.from(this).inflate(R.layout.pw_search_engine, null, false)
  // 实例化 PopupWindow
  popupWindow = PopupWindow(
    pwView,
    ViewGroup.LayoutParams.MATCH_PARENT,
    ViewGroup.LayoutParams.WRAP_CONTENT
  )
  // 初始化弹窗列表
  initRecyclerView()
  // 设置 popupWindow
  popupWindow.isOutsideTouchable = true
  popupWindow.isTouchable = true
  popupWindow.isFocusable = true
  // 加载弹窗动画
  popupWindow.animationStyle = R.style.pw_bottom_anim_style
  // 设置弹窗关闭监听——恢复亮度
  popupWindow.setOnDismissListener {
   backgroundAlpha(1f)
  }
 }

 private fun showPopWindow() {
  val rootView = LayoutInflater.from(this).inflate(
    R.layout.activity_main,
    null
  )
  // 设置弹窗位置
  popupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, 0)
  // 使得背景亮度变暗
  backgroundAlpha(0.7f)
 }

 // 控制背景亮度
 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(this@MainActivity, engine.title, Toast.LENGTH_SHORT).show()
    popupWindow.dismiss()
   }
  })
 }

 private fun initData() {
  // 初始化引擎列表
  engines = mutableListOf()
  // 从 arrays.xml 获取引擎名称数组
  val titleList = resources.getStringArray(R.array.search_engine_title_list)
  // 由于资源 id 是整型,但是在 arrays.xml 中存储的是字符串,
  // 所以这里先初始化一个资源 id 的数组,元素类型为整型
  val iconResList : MutableList<Int> = mutableListOf()
  // 通过类型数组加载相关引擎资源列表,遍历其中元素,传入索引值,
  // 通过调用 getResourceId(index,0) 获取 icon 的资源 id 存入刚才初始化的 id 数组中
  val resList: TypedArray =
    resources.obtainTypedArray(R.array.search_engine_res_list)
  for (index in 0 until resList.length()) {
   iconResList.add(resList.getResourceId(index,0))
  }
  // 记得及时调用 recycle() 回收 TypedArray 对象
  resList.recycle()
  // 循环,用获得的 title 和 id 生成对应的搜索引擎对象,存入搜索引擎列表中
  for (index in titleList.indices){
   if (index < iconResList.size){
    engines.add(SearchEngine(titleList[index],iconResList[index]))
   }
  }
 }

}

到此这篇关于Android使用 PopupWindow 实现底部弹窗功能的文章就介绍到这了,更多相关Android PopupWindow底部弹窗内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android使用 PopupWindow 实现底部弹窗功能的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部