Android 上拉菜单
2019-01-08 本文已影响0人
红酥手黄藤酒丶
Android 上拉菜单
Dialog 会置于顶层,项目需求是这样的:
image
要求点击购物车,弹出后面的视图,弹出的视图要置于 购物车按钮 下方
Dialog会将购物车盖住。
所以自己写了个自定义View实现该功能。
使用时实现 BottomPopupConstraintLayout.BottomConstraintLayoutAdap
接口:
class BottomAdapter(val context:Context):BottomPopupConstraintLayout.BottomConstraintLayoutAdapter{
override fun getMaxHeight(): Int {
val dp2px = DisplayUtil.dp2px(context, 462f)
return dp2px
}
override fun getDuration(): Long {
return 300
}
}
必须实现的方法:
getMaxHeight()
从来设置 弹出的高度
剩下还有一些可选的方法,查看BottomPopupConstraintLayout.BottomConstraintLayoutAdapter
即可
布局文件非常简单:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<zyf.com.selectdemo.BottomPopupConstraintLayout
android:id="@+id/likeDialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:background="#00ff00"
app:layout_constraintBottom_toBottomOf="parent">
</zyf.com.selectdemo.BottomPopupConstraintLayout>
<Button
android:id="@+id/btnTest"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="点我"
android:background="#ff0000"
android:textColor="#ffffff"
android:textSize="20sp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="20dp"/>
</android.support.constraint.ConstraintLayout>
下面是具体代码:
package zyf.com.selectdemo
import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Color
import android.support.constraint.ConstraintLayout
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
/**
* create by zyf on 2019/1/8 7:23 PM
*/
class BottomPopupConstraintLayout : ConstraintLayout {
private val mShadowView: View = View(context)
private var mIsShow = false
private var mIsSliding = false
private var mAlphaMin = 80
private var slideTop: ValueAnimator? = null
private var slideBottom: ValueAnimator? = null
private lateinit var mAdapter: BottomConstraintLayoutAdapter
constructor(context: Context) : this(context, null) {
}
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
}
constructor(context: Context, attrs: AttributeSet?, defstyleAttr: Int) : super(context, attrs, defstyleAttr) {
mInit(context)
}
private fun mInit(context: Context) {
}
private fun mDeployAnimator() {
slideTop = ValueAnimator.ofInt(mAdapter.getMinHeight(), mAdapter.getMaxHeight()).apply {
addUpdateListener {
val value = it.animatedValue as Int
layoutParams.height = value
requestLayout()
if (mAdapter.showShadow()) {
mShadowView.setBackgroundColor(Color.argb(mAlphaMin * value / mAdapter.getMaxHeight(), 0, 0, 0))
}
}
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
changeSlideStatus()
mShadowView.visibility = View.VISIBLE
visibility = View.VISIBLE
}
override fun onAnimationEnd(animation: Animator?) {
changeSlideStatus()
mAdapter.getShowCompleted().invoke()
}
})
duration = mAdapter.getDuration()
}
slideBottom = ValueAnimator.ofInt(mAdapter.getMaxHeight(), mAdapter.getMinHeight()).apply {
addUpdateListener {
val value = it.animatedValue as Int
layoutParams.height = value
if(value == mAdapter.getMinHeight()){
visibility = View.GONE
}
requestLayout()
if (mAdapter.showShadow()) {
if (value == mAdapter.getMinHeight()) {
mShadowView.setBackgroundColor(Color.argb(0, 0, 0, 0))
} else {
mShadowView.setBackgroundColor(Color.argb(mAlphaMin * value / maxHeight, 0, 0, 0))
}
}
}
addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
changeSlideStatus()
}
override fun onAnimationEnd(animation: Animator?) {
mShadowView.visibility = View.GONE
changeSlideStatus()
mAdapter.getHideCompleted().invoke()
}
})
duration = mAdapter.getDuration()
}
}
private fun mDeployShadow() {
if (mAdapter.showShadow()) {
mShadowView.apply {
layoutParams =
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
val viewGroup = parent as ViewGroup
viewGroup.addView(mShadowView, 0)
mShadowView.setOnClickListener {
if (mAdapter.clickOutSideClose()) {
hide()
}
}
}
}
private fun mDeployDefaultListener() {
setOnClickListener { }
}
private fun changeSlideStatus() {
mIsSliding = !mIsSliding
}
private fun changeShowStatus() {
mIsShow = !mIsShow
}
fun showOrHide() {
if (mIsShow) {
hide()
} else {
show()
}
}
fun show() {
if (!mIsShow && !mIsSliding) {
slideTop?.start()
changeShowStatus()
}
}
fun hide() {
if (mIsShow && !mIsSliding) {
slideBottom?.start()
changeShowStatus()
}
}
fun setAdapter(adapter: BottomConstraintLayoutAdapter) {
mAdapter = adapter
//配置动画
mDeployAnimator()
//配置阴影点击时间
mDeployShadow()
//配置点击默认点击时间,否则点击时间会穿过Constraint,使得Shadow响应
mDeployDefaultListener()
}
interface BottomConstraintLayoutAdapter {
fun getMaxHeight(): Int
fun getMinHeight(): Int = 1
fun showShadow(): Boolean = true
fun clickOutSideClose(): Boolean = true
fun getDuration(): Long = 500
fun getHideCompleted() = {}
fun getShowCompleted() = {}
}
}