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

背景

不少浏览器在显示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



    

        

        

            
        

    


  • 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

    

方案二代码

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



    

        

        
    

  • 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

    

完整源代码

https://gitee.com/hspbc/halfScreenWebView

零基础系列

《零基础学安卓编程》
《零基础学Java编程》
《零基础学鸿蒙编程》

关于我

厦门大学计算机专业 | 前华为工程师
专注《零基础学编程系列》,包含:Java | 安卓 | 前端 | Flutter | 小程序 | 鸿蒙
全网可关注:花生皮编程

你可能感兴趣的:(模仿UC浏览半屏显示push消息过来的网页)