# 仿UC浏览器半屏网页展示halfScreenWebView **Repository Path**: hspbc/halfScreenWebView ## Basic Information - **Project Name**: 仿UC浏览器半屏网页展示halfScreenWebView - **Description**: 仿UC浏览器半屏网页展示 支持上下拖拽,滑动关闭 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-11-05 - **Last Updated**: 2025-10-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 背景 不少浏览器在显示push消息时,采用半屏显示,顶部会露出一截,同时页面支持上下滑动,下滑还可以关闭页面。 下面是UC浏览器的效果: ![UC浏览器效果](https://img-blog.csdnimg.cn/img_convert/c67a8909c987c1ab95c279beb225405b.gif) # 我实现的效果 ![实现效果](https://img-blog.csdnimg.cn/img_convert/feb450f39d0b6e24e93be87a61f2858f.gif) # 实现方案 方案一:继承FrameLayout,覆写事件处理方法,然后把WebView当做子View放到里面。 方案二:继承WebView,覆写事件处理方法。 # 方案一代码 ```kotlin 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 ``` - 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 ```xml ``` # 方案二代码 ```kotlin 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 ``` - 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 ```xml ``` # 完整源代码 https://gitee.com/hspbc/halfScreenWebView # 零基础系列 [《零基础学安卓编程》](https://cxyxy.blog.csdn.net/category_11409895.html) [《零基础学Java编程》](https://cxyxy.blog.csdn.net/category_11331310.html) [《零基础学鸿蒙编程》](https://cxyxy.blog.csdn.net/category_11409093.html) # 关于我 厦门大学计算机专业 | 前华为工程师 分享编程技术,没啥深度,但看得懂,适合初学者。 Java | 安卓 | 前端 | 小程序 | 鸿蒙 公众号:**花生皮编程** ![](https://img-blog.csdnimg.cn/img_convert/123a1c3355c3ca04d3b88ab5e5648127.png)