Android测量视图可见区域

时间:2016-12-22 15:38:23

标签: android android-layout

我从这里搜索了很多类似的答案,但没有一个能够准确地运作。我想计算一个自定义视图的可见区域,该视图可以被屏幕边缘阻挡,或者被​​滚动视图的边缘阻挡,让我们看下面的图片:

enter image description here

如上所述,黑色是我的屏幕,红色是我的自定义视图并向上滚动一点,我想测量B的区域。

enter image description here

如上所述,黑色是我的屏幕,红色是我的自定义视图,蓝色是滚动视图。自定义视图是滚动视图的子视图,它向上滚动了一下。我想测量B的面积。

1)我尝试了View.getWindowVisibleDisplayFrameView.getLocalVisibleRectView.getGlobalVisibleRect,但没有一个能够准确地运作。乍一看它们看起来不错,但当我滚动我的视图从屏幕上消失时,它会以某种方式向我显示视图的完整高度和宽度,视图甚至不会显示在屏幕内。

2)我尝试View.getLocationOnScreen()getLocationInWindow()手动计算偏移量,得到XY坐标和加/减视图(和屏幕)的高度和宽度,但发现它并不容易因为屏幕的顶部总是有额外的菜单栏等,并且会弄乱结果。

3)虽然这在我的情况下不太可能,但我想知道,如果我的视图上有绝对布局并且部分阻挡它,我还能找到该区域吗? (两个布局都在相同的活动中)

我的问题是,有没有简单而准确的方法来计算我想要的面积?

2 个答案:

答案 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;
}
}

希望此代码也能帮助您