background

Many browsers use a half-screen display when displaying push messages, with a section at the top. At the same time, the page supports sliding up and down, and the page can be closed by sliding down. Here’s what the UC browser looks like:

I achieved the effect

Implementation scheme

Scheme 1: Inherit FrameLayout, override the event handling method, and put the WebView inside as a child View. Scheme 2: Inherit WebView and override event handling methods.

Scheme 1 code

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(a): 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
    }

    /** * Determine whether to click on 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(a)= 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(a) {
        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(a) {
        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(a)
}
Copy the code

Usage:

  • Layout file: activity_main.xml

      
<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>
Copy the code
  • Activity code:
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 / / set the WebView properties, The Javascript script settings.cacheMode = websettings. LOAD_NO_CACHE settings.layoutAlgorithm = can be executed WebSettings. LayoutAlgorithm. NORMAL Settings. AllowFileAccess = true / / set can access the file Settings. BuiltInZoomControls = false / / set support zoom 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 } } }Copy the code
  • Drawable resource: 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.59 L12, l4.59, 13.17-4.58 L18, 10 l - 6, 6 -, 6-1.41, 1.41 z"/>
</vector>
Copy the code

Scheme 2 code

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) {

        valanim = 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(a)
}
Copy the code

Usage:

  • Layout file: activity_main.xml

      
<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>
Copy the code
  • Activity code:
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 / / set the webView properties, The Javascript script settings.cacheMode = websettings. LOAD_NO_CACHE settings.layoutAlgorithm = can be executed WebSettings. LayoutAlgorithm. NORMAL Settings. AllowFileAccess = true / / set can access the file Settings. BuiltInZoomControls = false / / set support zoom 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 } } }Copy the code
  • Drawable resource: 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.59 L12, l4.59, 13.17-4.58 L18, 10 l - 6, 6 -, 6-1.41, 1.41 z"/>
</vector>
Copy the code

Complete source code

Gitee.com/hspbc/halfS…

Zero base series

Zero Basic Android Programming, Zero Basic Java Programming, Zero Basic Hongmeng Programming

About me

Xiamen university computer professional | before huawei engineers

Share programming techniques, no depth, but understand, suitable for beginners.

Java | android front | | small program | HongMeng

The public no. :Peanut skin programming