Какой лучший способ проверить, виден ли вид в окне? - PullRequest
22 голосов
/ 05 сентября 2010

Какой лучший способ проверить, виден ли вид в окне?

У меня есть CustomView, который является частью моего SDK, и любой может добавить CustomView в свои макеты.Мой CustomView предпринимает некоторые действия, когда он периодически виден пользователю.Поэтому, если представление становится невидимым для пользователя, ему необходимо остановить таймер, а когда оно снова станет видимым, оно должно возобновить свой ход.

Но, к сожалению, нет определенного способа проверить, становится ли мой CustomView видимым или невидимым для пользователя.Есть несколько вещей, которые я могу проверить и выслушать:

onVisibilityChange //it is for view's visibility change, and is introduced in new API 8 version so has backward compatibility issue
onWindowVisibilityChange //but my CustomView can be part of a ViewFlipper's Views so it can pose issues
onDetachedFromWindows //this not as useful
onWindowFocusChanged //Again my CustomView can be part of ViewFlipper's views.
Так что, если кто-то сталкивался с такими проблемами, пожалуйста, пролите немного света.

Ответы [ 9 ]

7 голосов
/ 07 января 2015

В моем случае следующий код работает лучше всего для прослушивания, если представление является видимым или нет:

@Override
protected void onWindowVisibilityChanged(int visibility) {
    super.onWindowVisibilityChanged(visibility);
    Log.e(TAG, "is view visible?: " + (visibility == View.VISIBLE));
}
7 голосов
/ 19 ноября 2010

onDraw() вызывается каждый раз, когда необходимо нарисовать вид. Когда представление выключено, тогда onDraw () никогда не вызывается. Когда небольшая часть представления становится видимой для пользователя, вызывается onDraw (). Это не идеально, но я не вижу другого вызова для использования, поскольку я хочу сделать то же самое. Не забудьте вызвать super.onDraw, иначе представление не будет отображено. Будьте осторожны, изменяя в onDraw все, что делает представление недействительным, поскольку это вызовет еще один вызов onDraw.

Если вы используете просмотр списка, тогда getView можно использовать всякий раз, когда ваш просмотр списка показывается пользователю.

очевидно, что действие onPause() называется, все ваши представления скрыты и не видны пользователю. возможно, вызывает invalidate () для родителя, и если ondraw () не вызывается, то это не видно.

4 голосов
/ 04 декабря 2012

Это метод, который я довольно часто использовал в своих приложениях, и у меня все получилось:

static private int screenW = 0, screenH = 0;

@SuppressWarnings("deprecation") static public boolean onScreen(View view) {
    int coordinates[] = { -1, -1 };
    view.getLocationOnScreen(coordinates);

    // Check if view is outside left or top
    if (coordinates[0] + view.getWidth() < 0) return false;
    if (coordinates[1] + view.getHeight() < 0) return false;

    // Lazy get screen size. Only the first time.
    if (screenW == 0 || screenH == 0) {
        if (MyApplication.getSharedContext() == null) return false;
        Display display = ((WindowManager)MyApplication.getSharedContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        try {
            Point screenSize = new Point();
            display.getSize(screenSize); // Only available on API 13+
            screenW = screenSize.x;
            screenH = screenSize.y;
        } catch (NoSuchMethodError e) { // The backup methods will only be used if the device is running pre-13, so it's fine that they were deprecated in API 13, thus the suppress warnings annotation at the start of the method.
            screenW = display.getWidth();
            screenH = display.getHeight();
        }
    }

    // Check if view is outside right and bottom
    if (coordinates[0] > screenW) return false;
    if (coordinates[1] > screenH) return false;

    // Else, view is (at least partially) in the screen bounds
    return true;
}

Чтобы использовать это, просто передайте в любом представлении или подклассе представления (IE, почти все, что рисует на экране в Android.) Он вернет true, если он на экране, или false, если это не .. довольно интуитивно, я думаю.

Если вы не используете вышеупомянутый метод в качестве статического, то, возможно, вы можете получить контекст другим способом, но для того, чтобы получить контекст приложения из статического метода, вам нужно выполнить следующие две вещи:

1 - Добавьте следующий атрибут к вашему тегу application в манифесте:

android:name="com.package.MyApplication"

2 - добавить класс, расширяющий Application, примерно так:

public class MyApplication extends Application {
    // MyApplication exists solely to provide a context accessible from static methods.
    private static Context context;

    @Override public void onCreate() {
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }

    public static Context getSharedContext() {
        return MyApplication.context;
    }
}
3 голосов
/ 30 октября 2013

В дополнение к view.getVisibility () есть view.isShown ().
isShown проверяет дерево представлений, чтобы определить, все ли предки также видны.

Хотя это не относится к закрытым видам, а только к скрытым или скрытым представлениям либо самих себя, либо одного из его родителей.

1 голос
/ 25 ноября 2016

Это можно проверить с помощью метода getGlobalVisibleRect.Если прямоугольник, возвращаемый этим методом, имеет точно такой же размер, как View, то текущий View полностью виден на экране.

/**
 * Returns whether this View is completely visible on the screen
 *
 * @param view view to check
 * @return True if this view is completely visible on the screen, or false otherwise.
 */
public static boolean onScreen(@NonNull View view) {
    Rect visibleRect = new Rect();
    view.getGlobalVisibleRect(visibleRect);
    return visibleRect.height() == view.getHeight() && visibleRect.width() == view.getWidth();
}

Если вам нужно рассчитать процент видимости, вы можете сделать это, используяквадратный расчет:

float visiblePercentage = (visibleRect.height() * visibleRect.width()) / (float)(view.getHeight() * view.getWidth()) 
1 голос
/ 10 октября 2015

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

@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
   super.onWindowFocusChanged(hasWindowFocus);
   if (!hasWindowFocus) {

   } else {

   }
}
0 голосов
/ 28 июня 2018

В этом решении учитывается вид, закрытый строкой состояния и панелью инструментов, а также вид за окном (например, прокрутка вне экрана)

/**
 * Test, if given {@code view} is FULLY visible in window. Takes into accout window decorations
 * (statusbar and toolbar)
 *
 * @param view
 * @return true, only if the WHOLE view is visible in window
 */
public static boolean isViewFullyVisible(View view) {
    if (view == null || !view.isShown())
        return false;

    //windowRect - will hold available area where content remain visible to users
    //Takes into account screen decorations (e.g. statusbar)
    Rect windowRect = new Rect();
    view.getWindowVisibleDisplayFrame(windowRect);

    //if there is toolBar, get his height
    int actionBarHeight = 0;
    Context context = view.getContext();
    if (context instanceof AppCompatActivity && ((AppCompatActivity) context).getSupportActionBar() != null)
        actionBarHeight = ((AppCompatActivity) context).getSupportActionBar().getHeight();
    else if (context instanceof Activity && ((Activity) context).getActionBar() != null)
        actionBarHeight = ((Activity) context).getActionBar().getHeight();

    //windowAvailableRect - takes into account toolbar height and statusbar height
    Rect windowAvailableRect = new Rect(windowRect.left, windowRect.top + actionBarHeight, windowRect.right, windowRect.bottom);

    //viewRect - holds position of the view in window
    //(methods as getGlobalVisibleRect, getHitRect, getDrawingRect can return different result,
    // when partialy visible)
    Rect viewRect;
    final int[] viewsLocationInWindow = new int[2];
    view.getLocationInWindow(viewsLocationInWindow);
    int viewLeft = viewsLocationInWindow[0];
    int viewTop = viewsLocationInWindow[1];
    int viewRight = viewLeft + view.getWidth();
    int viewBottom = viewTop + view.getHeight();
    viewRect = new Rect(viewLeft, viewTop, viewRight, viewBottom);

    //return true, only if the WHOLE view is visible in window
    return windowAvailableRect.contains(viewRect);
}
0 голосов
/ 10 ноября 2015

в вашем пользовательском представлении установите слушателей:

 getViewTreeObserver().addOnScrollChangedListener(this);
 getViewTreeObserver().addOnGlobalLayoutListener(this);

Я использую этот код для однократной анимации представления, когда оно видимо для пользователя.

2 случая должны быть рассмотрены.

  1. Ваше изображение отсутствует на экране.Но это будет видно, если пользователь прокрутит его

    public void onScrollChanged() {
        final int i[] = new int[2];
        this.getLocationOnScreen(i);
        if (i[1] <= mScreenHeight - 50) {
            this.post(new Runnable() {
                @Override
                public void run() {
                    Log.d("ITEM", "animate");
                    //animate once
                    showValues();
                }
            });
            getViewTreeObserver().removeOnScrollChangedListener(this);
            getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    }
    
  2. Ваш вид изначально находится на экране. (Не в другом месте, невидимом для пользователя в scrollview, он изначально находится на экране и виденпользователю)

    public void onGlobalLayout() {
     final int i[] = new int[2];
     this.getLocationOnScreen(i);
     if (i[1] <= mScreenHeight) {
        this.post(new Runnable() {
            @Override
            public void run() {
                Log.d("ITEM", "animate");
                //animate once
                showValues();
            }
        });
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
        getViewTreeObserver().removeOnScrollChangedListener(this);
     }
    }
    
0 голосов
/ 10 сентября 2015

Вы можете добавить в конструктор CustomView a onScrollChangedListener из ViewTreeObserver

поэтому, если ваш вид прокручивается на экране, вы можете вызвать view.getLocalVisibleRect () и определить, является ли ваш вид частично закадровым ...

Вы можете взглянуть на код моей библиотеки: PercentVisibleLayout

Надеюсь, это поможет!

...