Android 检查视图是否在窗口上可见的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3644030/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the best way to check if the view is visible on the window?
提问by bhups
What's the best way to check if the view is visible on the window?
检查视图是否在窗口上可见的最佳方法是什么?
I have a CustomView which is part of my SDK and anybody can add CustomView to their layouts. My CustomView is taking some actions when it is visible to the user periodically. So if view becomes invisible to the user then it needs to stop the timer and when it becomes visible again it should restart its course.
我有一个 CustomView,它是我的 SDK 的一部分,任何人都可以将 CustomView 添加到他们的布局中。当我的 CustomView 定期对用户可见时,它正在执行一些操作。因此,如果视图对用户不可见,那么它需要停止计时器,当它再次可见时,它应该重新开始它的进程。
But unfortunately there is no certain way of checking if my CustomView becomes visible or invisible to the user. There are few things that I can check and listen to:
但不幸的是,没有某种方法可以检查我的 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.
因此,如果有人遇到过此类问题,请提出一些意见。回答by goRGon
In my case the following code works the best to listen if the View is visible or not:
在我的情况下,以下代码最适合监听视图是否可见:
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
Log.e(TAG, "is view visible?: " + (visibility == View.VISIBLE));
}
回答by Louis Barman
onDraw()
is called each time the view needs to be drawn. When the view is off screen then onDraw() is never called. When a tiny bit of the view is becomes visible to the user then onDraw() is called. This is not ideal but I cannot see another call to use as I want to do the same thing. Remember to call the super.onDraw or the view won't get drawn. Be careful of changing anything in onDraw that causes the view to be invalidate as that will cause another call to onDraw.
onDraw()
每次需要绘制视图时都会调用。当视图离开屏幕时,永远不会调用 onDraw()。当视图的一小部分对用户可见时,就会调用 onDraw()。这并不理想,但我无法看到另一个使用调用,因为我想做同样的事情。记得调用 super.onDraw 否则视图不会被绘制。小心更改 onDraw 中导致视图无效的任何内容,因为这将导致再次调用 onDraw。
If you are using a listview then getView can be used whenever your listview becomes shown to the user.
如果您使用的是列表视图,那么只要您的列表视图显示给用户,就可以使用 getView。
obviously the activity onPause()
is called all your views are all covered up and are not visible to the user. perhaps calling invalidate() on the parent and if ondraw() is not called then it is not visible.
显然该活动onPause()
被称为您的所有视图都被掩盖并且对用户不可见。也许在父级上调用 invalidate() ,如果没有调用 ondraw() 则它不可见。
回答by ArtOfWarfare
This is a method that I have used quite a bit in my apps and have had work out quite well for me:
这是一种我在我的应用程序中使用了很多并且对我来说效果很好的方法:
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;
}
To use it, just pass in any view or subclass of view (IE, just about anything that draws on screen in Android.) It'll return true
if it's on screen or false
if it's not... pretty intuitive, I think.
要使用它,只需传入任何视图或视图的子类(IE,几乎所有在 Android 屏幕上绘制的内容。)true
如果它在屏幕上或false
不在屏幕上,它将返回......我认为非常直观。
If you're not using the above method as a static, then you can probably get a context some other way, but in order to get the Application context from a static method, you need to do these two things:
如果您不将上述方法用作静态方法,那么您可能可以通过其他方式获取上下文,但是为了从静态方法中获取应用程序上下文,您需要执行以下两件事:
1 - Add the following attribute to your application
tag in your manifest:
1 - 将以下属性添加到application
清单中的标签:
android:name="com.package.MyApplication"
2 - Add in a class that extends Application, like so:
2 - 添加一个扩展应用程序的类,如下所示:
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;
}
}
回答by NebulaSleuth
In addition to the view.getVisibility() there is view.isShown().
isShown checks the view tree to determine if all ancestors are also visible.
除了 view.getVisibility() 还有 view.isShown()。
isShown 检查视图树以确定是否所有祖先也可见。
Although, this doesn't handle obstructed views, only views that are hidden or gone in either themselves or one of its parents.
虽然,这不处理受阻视图,仅处理隐藏或消失在其自身或其父级之一中的视图。
回答by Peter Hale
In dealing with a similar issue, where I needed to know if the view has some other window on top of it, I used this in my custom View:
在处理类似的问题时,我需要知道视图上面是否有其他窗口,我在自定义视图中使用了它:
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
} else {
}
}
回答by Oleksandr
This can be checked using getGlobalVisibleRect
method. If rectangle returned by this method has exactly the same size as View
has, then current View
is completely visible on the Screen.
这可以使用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();
}
If you need to calculate visibility percentage you can do it using square calculation:
如果您需要计算可见度百分比,您可以使用平方计算来完成:
float visiblePercentage = (visibleRect.height() * visibleRect.width()) / (float)(view.getHeight() * view.getWidth())
回答by ?tarke
This solution takes into account view obstructed by statusbar and toolbar, also as view outside the window (e.g. scrolled out of screen)
此解决方案考虑了状态栏和工具栏阻碍的视图,也考虑了窗口外的视图(例如滚动到屏幕外)
/**
* 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);
}
回答by cihan turkay
in your custom view, set the listeners:
在您的自定义视图中,设置侦听器:
getViewTreeObserver().addOnScrollChangedListener(this);
getViewTreeObserver().addOnGlobalLayoutListener(this);
I am using this code to animate a view once when it is visible to user.
我正在使用此代码在用户可见时为视图设置动画。
2 cases should be considered.
应考虑 2 种情况。
Your view is not in the screen. But it will be visible if user scrolled it
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); } }
Your view is initially in screen.(Not in somewhere else invisible to user in scrollview, it is in initially on screen and visible to user)
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); } }
您的视图不在屏幕上。但如果用户滚动它,它将是可见的
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); } }
您的视图最初在屏幕中。(不是在滚动视图中用户不可见的其他地方,它最初在屏幕上并且对用户可见)
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); } }
回答by tzanou
you can add to your CustomView's constractor a an onScrollChangedListener from ViewTreeObserver
您可以从 ViewTreeObserver 中添加一个onScrollChangedListener到您的 CustomView 的 构造函数
so if your View is scrolled of screen you can call view.getLocalVisibleRect() and determine if your view is partly offscreen ...
因此,如果您的视图滚动屏幕,您可以调用 view.getLocalVisibleRect() 并确定您的视图是否部分离屏...
you can take a look to the code of my library : PercentVisibleLayout
你可以看看我的库的代码:PercentVisibleLayout
Hope it helps!
希望能帮助到你!