背景
不少浏览器在显示push消息时,采用半屏显示,顶部会露出一截,同时页面支持上下滑动,下滑还可以关闭页面。
下面是UC浏览器的效果:
UC浏览器效果
我实现的效果
实现效果实现方案
方案一:继承FrameLayout,覆写事件处理方法,然后把WebView当做子View放到里面。
方案二:继承WebView,覆写事件处理方法。
方案一代码
class WebViewDragLayout : FrameLayout {
private var downY: Float = 0f
private var hasTouched = false
private var mHidePageListener: HidePageListener? = null
private val goUpAnimTime = 200L
private val goDownAnimTime = 200L
private val originalPaddingTop = 300
private var mWebView: WebView? = null
private lateinit var mToCloseLayout: View
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
fun setHidePageListener(hidePageListener: HidePageListener) {
mHidePageListener = hidePageListener
}
fun setChildViews(webView: WebView, toCloseLayout: View) {
mWebView = webView
mToCloseLayout = toCloseLayout
}
private fun shouldIntercept(): Boolean {
return true
}
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
if (!shouldIntercept()) {
return false
}
if (isTouchOnCloseLayout(event)) {
return false
}
when (event.action) {
MotionEvent.ACTION_DOWN -> {
downY = event.rawY
return !hasTouched
}
MotionEvent.ACTION_MOVE -> {
var newPadding = (event.rawY - downY).toInt()
if (!hasTouched) {
newPadding += originalPaddingTop
}
if (newPadding <= 0) {
newPadding = 0
}
return !hasTouched || (isMovingDown(newPadding) && isWebViewReachedTop())
}
}
return false
}
/**
* 判断是否点击在toCloseLayout上
*/
private fun isTouchOnCloseLayout(event: MotionEvent) =
event.y >= mToCloseLayout.y && event.y <= mToCloseLayout.y + mToCloseLayout.height
private fun isMovingDown(newPadding: Int) = newPadding > 0
private fun isWebViewReachedTop() = mWebView?.scrollY == 0
override fun onTouchEvent(event: MotionEvent): Boolean {
var newPadding: Int
if (event.action == MotionEvent.ACTION_DOWN) {
downY = event.rawY
} else if (event.action == MotionEvent.ACTION_MOVE) {
var yOffset = (event.rawY - downY).toInt()
newPadding = yOffset
if (!hasTouched) {
newPadding += originalPaddingTop
}
if (newPadding <= 0) {
newPadding = 0
}
updateTopPadding(newPadding)
} else if (event.action == MotionEvent.ACTION_UP) {
hasTouched = true
val yOffset = event.rawY - downY
if (yOffset > height / 4) {
hidePageWithAnim()
} else {
movePageToTopWithAnim()
}
}
return true
}
private fun movePageToTopWithAnim() {
val anim = ObjectAnimator.ofInt(paddingTop, 0)
anim.duration = goUpAnimTime
anim.addUpdateListener { valueAnimator ->
updateTopPadding(
valueAnimator.animatedValue.toString().toInt()
)
}
anim.start()
}
private fun updateTopPadding(paddingValue: Int) {
setPadding(0, paddingValue, 0, 0)
}
fun hidePageWithAnim() {
val anim = ObjectAnimator.ofInt(paddingTop, height)
anim.duration = goDownAnimTime
anim.addUpdateListener { valueAnimator ->
updateTopPadding(
valueAnimator.animatedValue.toString().toInt()
)
}
anim.addListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(p0: Animator?) {
mHidePageListener?.onHide()
visibility = GONE
}
override fun onAnimationStart(p0: Animator?) {}
override fun onAnimationCancel(p0: Animator?) {}
override fun onAnimationRepeat(p0: Animator?) {}
})
anim.start()
}
}
interface HidePageListener {
fun onHide()
}
使用方法:
- 布局文件:activity_main.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:background="#FF0">
<cn.hsp.halfscreenwebview.WebViewDragLayout
android:id="@+id/webViewDragLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="100dp">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="40dp" />
<LinearLayout
android:id="@+id/toCloseLayout"
android:layout_width="match_parent"
android:layout_height="40dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#FFF"
android:src="@drawable/ic_down" />
</LinearLayout>
</cn.hsp.halfscreenwebview.WebViewDragLayout>
</FrameLayout>
- activity代码代码:
package cn.hsp.halfscreenwebview
import android.os.Build
import android.os.Bundle
import android.webkit.WebSettings
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import cn.hsp.halfscreenwebview.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val url = "https://www.baidu.com"
binding.apply {
setSettings(webView)
webView.loadUrl(url)
webViewDragLayout.setChildViews(webView, toCloseLayout)
}
}
private fun setSettings(webView: WebView) {
val settings = webView.settings
settings.javaScriptEnabled = true//设置WebView属性,能够执行Javascript脚本
settings.cacheMode = WebSettings.LOAD_NO_CACHE
settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
settings.allowFileAccess = true //设置可以访问文件
settings.builtInZoomControls = false //设置支持缩放
settings.setSupportZoom(true)
settings.useWideViewPort = true
settings.loadWithOverviewMode = true
settings.setAppCacheEnabled(true)
settings.domStorageEnabled = true
settings.databaseEnabled = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
}
}
- drawable资源:ic_down.xml
<vector android:height="24dp" android:tint="#6A6A6A"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>
方案二代码
class DragWebView : WebView {
private var downY: Float = 0f
private var hasTouched = false
private var mHidePageListener: HidePageListener? = null
private val goUpAnimTime = 200L
private val goDownAnimTime = 200L
private val originalPaddingTop = 300
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
fun setHidePageListener(hidePageListener: HidePageListener) {
mHidePageListener = hidePageListener
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val parentView = parent as ViewGroup
var newPadding: Int
if (event.action == MotionEvent.ACTION_DOWN) {
downY = event.rawY
if (hasTouched) {
return super.onTouchEvent(event)
}
} else if (event.action == MotionEvent.ACTION_MOVE) {
var yOffset = (event.rawY - downY).toInt()
newPadding = yOffset
if (!hasTouched) {
newPadding += originalPaddingTop
}
if (newPadding <= 0) {
newPadding = 0
}
if (hasTouched) {
if (newPadding > 0 && scrollY <= 0) {
updateTopPadding(parentView, newPadding)
}
return super.onTouchEvent(event)
} else {
updateTopPadding(parentView, newPadding)
}
} else if (event.action == MotionEvent.ACTION_UP) {
hasTouched = true
val yOffset = event.rawY - downY
if (yOffset > parentView.height / 4) {
hidePageWithAnim(parentView)
} else {
movePageToTopWithAnim(parentView)
}
return super.onTouchEvent(event)
} else if (event.action == MotionEvent.ACTION_CANCEL) {
return super.onTouchEvent(event)
}
return true
}
private fun movePageToTopWithAnim(parentView: ViewGroup) {
val anim = ObjectAnimator.ofInt(parentView.paddingTop, 0)
anim.duration = goUpAnimTime
anim.addUpdateListener { valueAnimator -> updateTopPadding(parentView, valueAnimator.animatedValue.toString().toInt()) }
anim.start()
}
private fun updateTopPadding(parentView: ViewGroup, paddingValue: Int) {
parentView.setPadding(0, paddingValue, 0, 0)
}
private fun hidePageWithAnim(parentView: ViewGroup) {
val anim = ObjectAnimator.ofInt(parentView.paddingTop, parentView.height)
anim.duration = goDownAnimTime
anim.addUpdateListener { valueAnimator -> updateTopPadding(parentView, valueAnimator.animatedValue.toString().toInt()) }
anim.addListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(p0: Animator?) {
mHidePageListener?.onHide()
}
override fun onAnimationStart(p0: Animator?) {}
override fun onAnimationCancel(p0: Animator?) {}
override fun onAnimationRepeat(p0: Animator?) {}
})
anim.start()
}
}
interface HidePageListener {
fun onHide()
}
使用方法:
- 布局文件:activity_main.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:background="#FF0">
<FrameLayout
android:id="@+id/topLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="100dp">
<cn.hsp.demo.MyWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#FFF"
android:src="@drawable/ic_down" />
</FrameLayout>
</FrameLayout>
- activity代码代码:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val url = "https://www.baidu.com"
binding.apply {
setSettings(webView)
webView.loadUrl(url)
imageView.setOnClickListener {
hidePageWithAnim(topLayout)
}
}
}
private fun hidePageWithAnim(parentView: ViewGroup) {
val anim = ObjectAnimator.ofFloat(
parentView,
"translationY",
parentView.translationY,
parentView.height.toFloat()
)
anim.duration = 200
anim.addListener(object : Animator.AnimatorListener {
override fun onAnimationEnd(p0: Animator?) {
Log.i("MainActivity", "closed")
}
override fun onAnimationStart(p0: Animator?) {}
override fun onAnimationCancel(p0: Animator?) {}
override fun onAnimationRepeat(p0: Animator?) {}
})
anim.start()
}
private fun setSettings(webView: DragWebView) {
val settings = webView.settings
settings.javaScriptEnabled = true//设置WebView属性,能够执行Javascript脚本
settings.cacheMode = WebSettings.LOAD_NO_CACHE
settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL
settings.allowFileAccess = true //设置可以访问文件
settings.builtInZoomControls = false //设置支持缩放
settings.setSupportZoom(true)
settings.useWideViewPort = true
settings.loadWithOverviewMode = true
settings.setAppCacheEnabled(true)
settings.domStorageEnabled = true
settings.databaseEnabled = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
}
}
- drawable资源:ic_down.xml
<vector android:height="24dp" android:tint="#6A6A6A"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>
完整源代码
https://gitee.com/hspbc/halfScreenWebView
零基础系列
《零基础学安卓编程》
《零基础学Java编程》
《零基础学鸿蒙编程》
关于我
厦门大学计算机专业 | 前华为工程师
专注《零基础学编程系列》,包含:Java | 安卓 | 前端 | Flutter | 小程序 | 鸿蒙
全网可关注:花生皮编程
网友评论