Android: как проверить, виден ли вид внутри ScrollView? - PullRequest
156 голосов
/ 07 января 2011

У меня есть ScrollView, который содержит серию Views.Я хотел бы иметь возможность определить, является ли представление в настоящее время видимым (если какая-либо его часть в настоящее время отображается ScrollView).Я ожидаю, что приведенный ниже код сделает это, на удивление, это не так:

Rect bounds = new Rect();
view.getDrawingRect(bounds);

Rect scrollBounds = new Rect(scroll.getScrollX(), scroll.getScrollY(), 
        scroll.getScrollX() + scroll.getWidth(), scroll.getScrollY() + scroll.getHeight());

if(Rect.intersects(scrollBounds, bounds))
{
    //is  visible
}

Ответы [ 13 ]

0 голосов
/ 25 июня 2019

В итоге я реализовал комбинацию из двух ответов Java (@ bill-mote https://stackoverflow.com/a/12428154/3686125 и @ denys-vasylenko https://stackoverflow.com/a/25528434/3686125) в своем проекте в виде набора расширений Kotlin, которые поддерживают либо стандартные вертикальные элементы управления ScrollView или HorizontalScrollView.

Я просто выбросил их в файл Kotlin с именем Extensions.kt, без класса, только методы.

Я использовал их, чтобы определить, к какому элементу привязываться, когда пользователь прекращает прокрутку в различных видах прокрутки в моем проекте:

fun View.isPartiallyOrFullyVisible(horizontalScrollView: HorizontalScrollView) : Boolean {
    @Suppress("CanBeVal") var scrollBounds = Rect()
    horizontalScrollView.getHitRect(scrollBounds)
    return getLocalVisibleRect(scrollBounds)
}

fun View.isPartiallyOrFullyVisible(scrollView: ScrollView) : Boolean {
    @Suppress("CanBeVal") var scrollBounds = Rect()
    scrollView.getHitRect(scrollBounds)
    return getLocalVisibleRect(scrollBounds)
}

fun View.isFullyVisible(horizontalScrollView: HorizontalScrollView) : Boolean {
    @Suppress("CanBeVal") var scrollBounds = Rect()
    horizontalScrollView.getDrawingRect(scrollBounds)
    val left = x
    val right = left + width
    return scrollBounds.left < left && scrollBounds.right > right
}

fun View.isFullyVisible(scrollView: ScrollView) : Boolean {
    @Suppress("CanBeVal") var scrollBounds = Rect()
    scrollView.getDrawingRect(scrollBounds)
    val top = y
    val bottom = top + height
    return scrollBounds.top < top && scrollBounds.bottom > bottom
}

fun View.isPartiallyVisible(horizontalScrollView: HorizontalScrollView) : Boolean = isPartiallyOrFullyVisible(horizontalScrollView) && !isFullyVisible(horizontalScrollView)
fun View.isPartiallyVisible(scrollView: ScrollView) : Boolean = isPartiallyOrFullyVisible(scrollView) && !isFullyVisible(scrollView)

Пример использования, итерация дочерних элементов scrollview LinearLayout и запись выходных данных:

val linearLayoutChild: LinearLayout = getChildAt(0) as LinearLayout
val scrollView = findViewById(R.id.scroll_view) //Replace with your scrollview control or synthetic accessor
for (i in 0 until linearLayoutChild.childCount) {
    with (linearLayoutChild.getChildAt(i)) {
        Log.d("ScrollView", "child$i left=$left width=$width isPartiallyOrFullyVisible=${isPartiallyOrFullyVisible(scrollView)} isFullyVisible=${isFullyVisible(scrollView)} isPartiallyVisible=${isPartiallyVisible(scrollView)}")
    }
}
0 голосов
/ 14 ноября 2017

Я знаю, что очень поздно.Но у меня есть хорошее решение.Ниже приведен фрагмент кода для получения процента видимости в представлении прокрутки.

Прежде всего установите сенсорный слушатель в представлении прокрутки для получения обратного вызова для остановки прокрутки.

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch ( event.getAction( ) ) {
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if(mScrollView == null){
                        mScrollView = (ScrollView) findViewById(R.id.mScrollView);
                    }
                    int childCount = scrollViewRootChild.getChildCount();

                    //Scroll view location on screen
                    int[] scrollViewLocation = {0,0};
                    mScrollView.getLocationOnScreen(scrollViewLocation);

                    //Scroll view height
                    int scrollViewHeight = mScrollView.getHeight();
                    for (int i = 0; i < childCount; i++){
                        View child = scrollViewRootChild.getChildAt(i);
                        if(child != null && child.getVisibility() == View.VISIBLE){
                            int[] viewLocation = new int[2];
                            child.getLocationOnScreen(viewLocation);
                            int viewHeight = child.getHeight();
                            getViewVisibilityOnScrollStopped(scrollViewLocation, scrollViewHeight,
                                    viewLocation, viewHeight, (String) child.getTag(), (childCount - (i+1)));
                        }
                    }
                }
            }, 150);
            break;
    }
    return false;
}

В приведенном фрагменте кода,мы получаем обратные вызовы для событий касания просмотра прокрутки и публикуем работоспособность через 150 миллис (не обязательно) после прекращения обратного вызова прокрутки.В этом runnable мы получим расположение вида прокрутки на экране и высоту вида прокрутки.Затем получите прямой дочерний экземпляр группы просмотра представления прокрутки и получите количество дочерних элементов.В моем случае прямым потомком представления прокрутки является LinearLayout с именем scrollViewRootChild .Затем выполните итерацию всех дочерних представлений scrollViewRootChild .В приведенном выше фрагменте кода вы можете видеть, как я получаю местоположение дочернего элемента на экране в массиве целых чисел с именем viewLocation , получить высоту представления в имени переменной viewHeight .Затем я вызвал закрытый метод getViewVisibilityOnScrollStopped .Вы можете получить представление о внутренней работе этого метода, прочитав документацию.

/**
 * getViewVisibilityOnScrollStopped
 * @param scrollViewLocation location of scroll view on screen
 * @param scrollViewHeight height of scroll view
 * @param viewLocation location of view on screen, you can use the method of view claas's getLocationOnScreen method.
 * @param viewHeight height of view
 * @param tag tag on view
 * @param childPending number of views pending for iteration.
 */
void getViewVisibilityOnScrollStopped(int[] scrollViewLocation, int scrollViewHeight, int[] viewLocation, int viewHeight, String tag, int childPending) {
    float visiblePercent = 0f;
    int viewBottom = viewHeight + viewLocation[1]; //Get the bottom of view.
    if(viewLocation[1] >= scrollViewLocation[1]) {  //if view's top is inside the scroll view.
        visiblePercent = 100;
        int scrollBottom = scrollViewHeight + scrollViewLocation[1];    //Get the bottom of scroll view 
        if (viewBottom >= scrollBottom) {   //If view's bottom is outside from scroll view
            int visiblePart = scrollBottom - viewLocation[1];  //Find the visible part of view by subtracting view's top from scrollview's bottom  
            visiblePercent = (float) visiblePart / viewHeight * 100;
        }
    }else{      //if view's top is outside the scroll view.
        if(viewBottom > scrollViewLocation[1]){ //if view's bottom is outside the scroll view
            int visiblePart = viewBottom - scrollViewLocation[1]; //Find the visible part of view by subtracting scroll view's top from view's bottom
            visiblePercent = (float) visiblePart / viewHeight * 100;
        }
    }
    if(visiblePercent > 0f){
        visibleWidgets.add(tag);        //List of visible view.
    }
    if(childPending == 0){
        //Do after iterating all children.
    }
}

Если вы чувствуете какие-либо улучшения в этом коде, пожалуйста, внесите свой вклад.

0 голосов
/ 13 июля 2016

Используя ответ @Qberticus, который был кстати, но здорово, между прочим, я собрал кучу кодов, чтобы проверить, вызывает ли всякий раз, когда вызывается прокрутка, и прокручивается, запускается ли ответ @Qberticus, и вы можете делать все, что захотите, в моем случае У меня есть социальная сеть, содержащая видео, поэтому, когда изображение отображается на экране, я воспроизводю видео по той же идее, что и Facebook и Instagram. Вот код:

mainscrollview.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {

                    @Override
                    public void onScrollChanged() {
                        //mainscrollview is my scrollview that have inside it a linearlayout containing many child views.
                        Rect bounds = new Rect();
                         for(int xx=1;xx<=postslayoutindex;xx++)
                         {

                          //postslayoutindex is the index of how many posts are read.
                          //postslayoutchild is the main layout for the posts.
                        if(postslayoutchild[xx]!=null){

                            postslayoutchild[xx].getHitRect(bounds);

                        Rect scrollBounds = new Rect();
                        mainscrollview.getDrawingRect(scrollBounds);

                        if(Rect.intersects(scrollBounds, bounds))
                        {
                            vidPreview[xx].startPlaywithoutstoppping();
                         //I made my own custom video player using textureview and initialized it globally in the class as an array so I can access it from anywhere.
                        }
                        else
                        {

                        }


                        }
                    }
                    }
                });
...