美文网首页安卓开发
模仿UC浏览半屏显示push消息过来的网页

模仿UC浏览半屏显示push消息过来的网页

作者: 蓝不蓝编程 | 来源:发表于2021-11-06 09:59 被阅读0次

    背景

    不少浏览器在显示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 | 小程序 | 鸿蒙
    全网可关注:花生皮编程

    相关文章

      网友评论

        本文标题:模仿UC浏览半屏显示push消息过来的网页

        本文链接:https://www.haomeiwen.com/subject/djtwaltx.html