判断View滑入或滑出屏幕可见区

        今天在用今日头条app的时候发现了一个现象,比如你正在看视频(未全屏),此时上下滑动使得视频滑出屏幕可见区,那刚才正在播放的视频就自动暂停了。

        这里思考下,肯定有某种方式能够知道View是否在屏幕可见区,于是我就去View类中找了下,发现没有直接的监听器。但是有这样一个方法:

    public final boolean getGlobalVisibleRect(Rect r) {
        return getGlobalVisibleRect(r, null);
    }

        往里面走:

/**
     * If some part of this view is not clipped by any of its parents, then
     * return that area in r in global (root) coordinates. To convert r to local
     * coordinates (without taking possible View rotations into account), offset
     * it by -globalOffset (e.g. r.offset(-globalOffset.x, -globalOffset.y)).
     * If the view is completely clipped or translated out, return false.
     *
     * @param r If true is returned, r holds the global coordinates of the
     *        visible portion of this view.
     * @param globalOffset If true is returned, globalOffset holds the dx,dy
     *        between this view and its root. globalOffet may be null.
     * @return true if r is non-empty (i.e. part of the view is visible at the
     *         root level.
     */
    public boolean getGlobalVisibleRect(Rect r, Point globalOffset) {
        int width = mRight - mLeft;
        int height = mBottom - mTop;
        if (width > 0 && height > 0) {
            r.set(0, 0, width, height);
            if (globalOffset != null) {
                globalOffset.set(-mScrollX, -mScrollY);
            }
            return mParent == null || mParent.getChildVisibleRect(this, r, globalOffset);
        }
        return false;
    }

        这里重点关注下注释,当返回为true时,代表View在全局坐标中有可见的部分。那其实就是说,当该方法返回为true的时后说明该View的部分或者全部在屏幕可见区域,返回false的时候则该View完全不在屏幕可见区域。当返回为true的时候,该View在屏幕中的可见区域信息保存在名为r的Rect对象中。

        那就明白了,其实Google早就想到了我们的使用场景,通过这种方式来让我们知道View到底是滑入了屏幕可见区域还是滑出了屏幕可见区域。

        之后我来写个Demo验证一下,布局非常简单。

        最外层是个可以垂直滑动的NestedScrollView,里面填满ConstraintLayout,约束布局里面首尾都是一个较高的颜色块,中间夹着一张图片。我们的目的就是看在滑动中这张图片进入屏幕离开屏幕调用getGlobalVisibleRect()方法时的表现。

        整体布局如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/scroll_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    android:orientation="vertical"
    tools:context=".testviewexposure.TestViewExposureActivity">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <View
            android:id="@+id/view_top"
            android:layout_width="0dp"
            android:layout_height="1000dp"
            android:background="@color/purple"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/img"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:src="@drawable/fruit_image6"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/view_top" />

        <View
            android:id="@+id/view_bottom"
            android:layout_width="0dp"
            android:layout_height="1000dp"
            android:background="@color/green"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/img" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

         然后页面代码如下,主要看滑动监听中的情况即可,在关键处打印了日志。

package com.openld.seniorui.testviewexposure

import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.ImageView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.NestedScrollView
import com.openld.seniorui.R

class TestViewExposureActivity : AppCompatActivity() {
    private lateinit var mScrollView: NestedScrollView

    private lateinit var mImg: ImageView;

    private var mImgWidth = 0
    private var mImgHeight = 0
    private var mImgArea = 0

    private var mRect = Rect()

    @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_view_visible)

        initWidgets()

        addListeners()

        mImg.post {
            mImgWidth = mImg.width
            mImgHeight = mImg.height
            mImgArea = mImgHeight * mImgWidth
            Log.d(
                "Exposure >>>>>>",
                "图片宽度为 ${mImgWidth} 高度为${mImgHeight} 面积为${mImgArea}"
            )
        }
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun addListeners() {
        mScrollView.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
            val isGlobalVisible = mImg.getGlobalVisibleRect(mRect)
            if (isGlobalVisible) {
                // 有些场景进来是要可见面积大于自身面积的五分之一
                // 出去是要可见面积小于自己面积的五分之一
                // 这个时候可使用mRect和本身组件的原始面积进行一些比较,这里略
                Log.d(
                    "Exposure >>>>>>",
                    "全局可见 可见宽度为 ${mRect.width()} 可见高度为${mRect.height()} 可见面积为${mRect.width() * mRect.height()}"
                )
            } else {
                Log.d(
                    "Exposure >>>>>>",
                    "全局不可见}"
                )
            }
        }
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun initWidgets() {
        mScrollView = findViewById(R.id.scroll_view)
        mImg = findViewById(R.id.img)
    }
}

        然后启动一下app,滑动到图片出现再把图片滑出屏幕。

        看下对应的logcat日志如下:

1.图片未滑入屏幕

 

2.图片滑入屏幕中

 

3.图片完全滑入屏幕

 

4.图片滑出屏幕中

 

5.图片完全滑出屏幕

 

         由此可见,遇到问题可以先去对应的类中ctrl+f12键看看类中对应的各种public方法,说不定就能找到需要的方法或者发现一些你不知道的好用的API。

        结合着这个API的功能,你完全可以发散着做出很多效果。比如曝光埋点时候你肯定要知道组件何时滑入屏幕何时滑出屏幕。比如做视频播放时,滑出屏幕时候暂停或者回收资源。比如滑出屏幕就停止动画,滑入屏幕才播放动画等等场景。可以举一反三,灵活运用。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值