我从这里搜索了很多类似的答案,但没有一个能够准确地运作。我想计算一个自定义视图的可见区域,该视图可以被屏幕边缘阻挡,或者被滚动视图的边缘阻挡,让我们看下面的图片:
如上所述,黑色是我的屏幕,红色是我的自定义视图并向上滚动一点,我想测量B的区域。
如上所述,黑色是我的屏幕,红色是我的自定义视图,蓝色是滚动视图。自定义视图是滚动视图的子视图,它向上滚动了一下。我想测量B的面积。
1)我尝试了View.getWindowVisibleDisplayFrame
,View.getLocalVisibleRect
,View.getGlobalVisibleRect
,但没有一个能够准确地运作。乍一看它们看起来不错,但当我滚动我的视图从屏幕上消失时,它会以某种方式向我显示视图的完整高度和宽度,视图甚至不会显示在屏幕内。
2)我尝试View.getLocationOnScreen()
和getLocationInWindow()
手动计算偏移量,得到XY坐标和加/减视图(和屏幕)的高度和宽度,但发现它并不容易因为屏幕的顶部总是有额外的菜单栏等,并且会弄乱结果。
3)虽然这在我的情况下不太可能,但我想知道,如果我的视图上有绝对布局并且部分阻挡它,我还能找到该区域吗? (两个布局都在相同的活动中)
我的问题是,有没有简单而准确的方法来计算我想要的面积?
答案 0 :(得分:3)
好的,我找到了其中一个开源广告框架的答案:
/**
* Whether the view is at least certain % visible
*/
boolean isVisible(@Nullable final View rootView, @Nullable final View view, final int minPercentageViewed) {
// ListView & GridView both call detachFromParent() for views that can be recycled for
// new data. This is one of the rare instances where a view will have a null parent for
// an extended period of time and will not be the main window.
// view.getGlobalVisibleRect() doesn't check that case, so if the view has visibility
// of View.VISIBLE but it's group has no parent it is likely in the recycle bin of a
// ListView / GridView and not on screen.
if (view == null || view.getVisibility() != View.VISIBLE || rootView.getParent() == null) {
return false;
}
if (!view.getGlobalVisibleRect(mClipRect)) {
// Not visible
return false;
}
// % visible check - the cast is to avoid int overflow for large views.
final long visibleViewArea = (long) mClipRect.height() * mClipRect.width();
final long totalViewArea = (long) view.getHeight() * view.getWidth();
if (totalViewArea <= 0) {
return false;
}
return 100 * visibleViewArea >= minPercentageViewed * totalViewArea;
}
我在使用View.getGlobalVisibleRect
时犯了一个错误,当视图从屏幕上消失时,此方法将返回false,尽管mClipRect
对象仍然提供值。以上是使用它的正确方法。
答案 1 :(得分:0)
在我Instabug的工作中实施新功能“ViewHierarchy”时,我遇到了同样的问题并通过以下代码解决了这个问题
这是执行所有逻辑的util类
public class ViewFrameInspector {
private static final String KEY_X = "x";
private static final String KEY_Y = "y";
private static final String KEY_W = "w";
private static final String KEY_H = "h";
/**
* Method emit inspected ViewFrame of passed view, the emit ViewFrame contains inspected ViewFrames fot its children and children of the children and so on
* by converting the emitted ViewFrame to list of View Frames you can find the a specific view and its frame with easily way
*
* @param view the root view
* @return return ViewFrame observable
*/
public static Observable<ViewFrame> inspectRootViewFrameRx(final View view) {
return Observable.defer(new Func0<Observable<ViewFrame>>() {
@Override
public Observable<ViewFrame> call() {
ViewFrame rootViewFrame = new ViewFrame();
rootViewFrame.setRoot(true);
rootViewFrame.setView(view);
return Observable.just(inspectVisibleViewFrame(rootViewFrame));
}
});
}
private static ViewFrame inspectVisibleViewFrame(final ViewFrame viewFrame) {
if (viewFrame.getView().getVisibility() == View.VISIBLE)
try {
viewFrame.setId(inspectViewResourceId(viewFrame.getView().getContext(), viewFrame.getView().getId()));
viewFrame.setType(ViewFrameInspector.inspectViewType(viewFrame.getView()));
viewFrame.setOriginalRect(ViewFrameInspector.inspectViewOriginalRect(viewFrame.getView()));
viewFrame.setVisibleRect(ViewFrameInspector.inspectViewVisibleRect(viewFrame));
viewFrame.setFrame(ViewFrameInspector.inspectViewFrame(viewFrame));
// inspect view children if exist
if (viewFrame.getView() instanceof ViewGroup) {
viewFrame.setHasChildren(true);
inspectViewChildren(viewFrame);
} else {
viewFrame.setHasChildren(false);
}
} catch (JSONException e) {
Log.e(ActivityViewInspector.class.getSimpleName(), "inspect view frame got error: " + e.getMessage() + ",view id:" + viewFrame.getId() + ", time in MS: " + System.currentTimeMillis(), e);
}
return viewFrame;
}
private static void inspectViewChildren(ViewFrame parentViewFrame) throws JSONException {
if (parentViewFrame.getView() instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) parentViewFrame.getView();
for (int i = 0; i < parent.getChildCount(); i++) {
ViewFrame childViewFrame = new ViewFrame();
childViewFrame.setRoot(false);
childViewFrame.setView(parent.getChildAt(i));
childViewFrame.setParent(parentViewFrame);
parentViewFrame.addNode(inspectVisibleViewFrame(childViewFrame));
}
}
}
private static String inspectViewType(View view) {
return view.getClass().getSimpleName();
}
private static String inspectViewResourceId(Context context, int id) throws JSONException {
try {
return context != null && context.getResources() != null && context.getResources().getResourceEntryName(id) != null ?
context.getResources().getResourceEntryName(id) : String.valueOf(id);
} catch (Resources.NotFoundException e) {
return String.valueOf(id);
}
}
private static Rect inspectViewOriginalRect(View view) {
int[] locationOnScreen = new int[2];
view.getLocationOnScreen(locationOnScreen);
return new Rect(locationOnScreen[0],
locationOnScreen[1],
locationOnScreen[0] + view.getWidth(),
locationOnScreen[1] + view.getHeight());
}
private static Rect inspectViewVisibleRect(ViewFrame viewFrame) {
if (viewFrame.isRoot()) {
return viewFrame.getOriginalRect();
} else {
Rect viewVisibleRect = new Rect(
viewFrame.getOriginalRect().left,
viewFrame.getOriginalRect().top,
viewFrame.getOriginalRect().right,
viewFrame.getOriginalRect().bottom);
Rect parentAvailableVisibleRect = new Rect(
inspectViewAvailableX(viewFrame.getParent()),
inspectViewAvailableY(viewFrame.getParent()),
inspectViewAvailableRight(viewFrame.getParent()),
inspectViewAvailableBottom(viewFrame.getParent()));
if (viewVisibleRect.intersect(parentAvailableVisibleRect)) {
return viewVisibleRect;
} else {
return new Rect(0, 0, 0, 0);
}
}
}
private static int inspectViewAvailableX(ViewFrame viewFrame) {
int visibleLeft, paddingLeft, originalLeft;
visibleLeft = viewFrame.getVisibleRect().left;
paddingLeft = viewFrame.getView().getPaddingLeft();
originalLeft = viewFrame.getOriginalRect().left;
if (paddingLeft == 0) {
return visibleLeft;
} else {
if (visibleLeft > (originalLeft + paddingLeft)) {
return visibleLeft;
} else {
return originalLeft + paddingLeft;
}
}
}
private static int inspectViewAvailableY(ViewFrame viewFrame) {
int visibleTop, paddingTop, originalTop;
visibleTop = viewFrame.getVisibleRect().top;
paddingTop = viewFrame.getView().getPaddingTop();
originalTop = viewFrame.getOriginalRect().top;
if (paddingTop == 0) {
return visibleTop;
} else {
if (visibleTop > (originalTop + paddingTop)) {
return visibleTop;
} else {
return originalTop + paddingTop;
}
}
}
private static int inspectViewAvailableRight(ViewFrame viewFrame) {
int visibleRight, paddingRight, originalRight;
visibleRight = viewFrame.getVisibleRect().right;
paddingRight = viewFrame.getView().getPaddingRight();
originalRight = viewFrame.getOriginalRect().right;
if (paddingRight == 0) {
return visibleRight;
} else {
if (visibleRight < (originalRight - paddingRight)) {
return visibleRight;
} else {
return originalRight - paddingRight;
}
}
}
private static int inspectViewAvailableBottom(ViewFrame viewFrame) {
int visibleBottom, paddingBottom, originalBottom;
visibleBottom = viewFrame.getVisibleRect().bottom;
paddingBottom = viewFrame.getView().getPaddingBottom();
originalBottom = viewFrame.getOriginalRect().bottom;
if (paddingBottom == 0) {
return visibleBottom;
} else {
if (visibleBottom < (originalBottom - paddingBottom)) {
return visibleBottom;
} else {
return originalBottom - paddingBottom;
}
}
}
private static JSONObject inspectViewFrame(ViewFrame viewFrame) throws JSONException {
return new JSONObject().put(KEY_X, viewFrame.getVisibleRect().left)
.put(KEY_Y, viewFrame.getVisibleRect().top)
.put(KEY_W, viewFrame.getVisibleRect().width())
.put(KEY_H, viewFrame.getVisibleRect().height());
}
public static List<ViewFrame> convertViewHierarchyToList(ViewFrame viewFrame) {
ArrayList<ViewFrame> viewFrameHierarchies = new ArrayList<>();
if (viewFrame != null) {
viewFrameHierarchies.add(viewFrame);
if (viewFrame.hasChildren()) {
for (ViewFrame childViewHierarchy : viewFrame.getNodes()) {
viewFrameHierarchies.addAll(convertViewHierarchyToList(childViewHierarchy));
}
}
}
return viewFrameHierarchies;
}
}
这是包含与检查视图相关的所有数据的模型类
public class ViewFrame {
private String id;
private String type;
private JSONObject frame;
private ViewFrame parent;
private ArrayList<ViewFrame> nodes;
private boolean hasChildren;
private boolean isRoot;
private Rect originalRect;
private Rect visibleRect;
private View view;
public ViewFrame() {
nodes = new ArrayList<>();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public JSONObject getFrame() {
return frame;
}
public void setFrame(JSONObject frame) {
this.frame = frame;
}
public ViewFrame getParent() {
return parent;
}
public void setParent(ViewFrame parent) {
this.parent = parent;
}
public ArrayList<ViewFrame> getNodes() {
return nodes;
}
public void addNode(ViewFrame childViewHierarchy) {
nodes.add(childViewHierarchy);
}
public boolean hasChildren() {
return hasChildren;
}
public void setHasChildren(boolean hasChildren) {
this.hasChildren = hasChildren;
}
public boolean isRoot() {
return isRoot;
}
public void setRoot(boolean root) {
isRoot = root;
}
public Rect getVisibleRect() {
return visibleRect;
}
public void setVisibleRect(Rect visibleRect) {
this.visibleRect = visibleRect;
}
public Rect getOriginalRect() {
return originalRect;
}
public void setOriginalRect(Rect originalRect) {
this.originalRect = originalRect;
}
public View getView() {
return view;
}
public void setView(View view) {
this.view = view;
}
}
希望此代码也能帮助您