ビューがウィンドウに表示されているかどうかを確認する最良の方法は何ですか?
SDKの一部であるCustomViewがあり、誰でも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.
私の場合、次のコードは、ビューが表示されているかどうかを確認するのに最適です。
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
Log.e(TAG, "is view visible?: " + (visibility == View.VISIBLE));
}
onDraw()
は、ビューを描画する必要があるたびに呼び出されます。ビューが画面外にある場合、onDraw()は呼び出されません。ビューのごく一部がユーザーに表示されると、onDraw()が呼び出されます。これは理想的ではありませんが、同じことをしたいので、使用する別の呼び出しを見ることができません。 super.onDrawを呼び出すことを忘れないでください。そうしないと、ビューが描画されません。 onDrawで別の呼び出しが発生するため、ビューが無効になるような変更には注意してください。
リストビューを使用している場合は、リストビューがユーザーに表示されるたびにgetViewを使用できます。
明らかに、アクティビティonPause()
は呼び出され、すべてのビューが隠蔽され、ユーザーには表示されません。おそらく親でinvalidate()を呼び出し、ondraw()が呼び出されない場合、それは表示されません。
これは、私がアプリでかなり使用していて、非常にうまく機能している方法です。
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;
}
これを使用するには、任意のビューまたはビューのサブクラス(つまり、Androidの画面に描画されるほぼすべてのもの)を渡すだけです。画面に表示されている場合はtrue
を返し、表示されている場合はfalse
を返します。そうではありません...かなり直感的だと思います。
上記のメソッドを静的メソッドとして使用していない場合は、おそらく別の方法でコンテキストを取得できますが、静的メソッドからアプリケーションコンテキストを取得するには、次の2つのことを行う必要があります。
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;
}
}
View.getVisibility()に加えて、view.isShown()があります。
isShownは、ビューツリーをチェックして、すべての祖先も表示されているかどうかを判断します。
これは遮られたビューを処理しませんが、自分自身またはその親の1つに隠されているか消えているビューのみを処理します。
ビューの上に他のウィンドウがあるかどうかを知る必要がある同様の問題に対処する際に、カスタムビューでこれを使用しました。
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
} else {
}
}
これは、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())
このソリューションでは、ステータスバーとツールバーによって遮られているビューと、ウィンドウの外側のビュー(画面からスクロールアウトするなど)が考慮されます。
/**
* 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);
}
カスタムビューで、リスナーを設定します。
getViewTreeObserver().addOnScrollChangedListener(this);
getViewTreeObserver().addOnGlobalLayoutListener(this);
このコードを使用して、ユーザーに表示されたときにビューを一度アニメーション化します。
2つのケースを考慮する必要があります。
あなたのビューは画面にありません。ただし、ユーザーがスクロールすると表示されます
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);
}
}
customViewのコンストラクターに ViewTreeObserverのonScrollChangedListener を追加できます。
したがって、ビューが画面からスクロールされている場合は、view.getLocalVisibleRect()を呼び出して、ビューが部分的に画面外にあるかどうかを判断できます...
あなたは私のライブラリのコードを見ることができます: PercentVisibleLayout
それが役に立てば幸い!