步骤1.使用Navigation Drawer模板创建一个新应用。

android - android app在PreDraw上无限运行-LMLPHP

步骤2.添加自定义按钮并覆盖onMeasure方法。

@CoordinatorLayout.DefaultBehavior(MyButton.Behavior.class)
public class MyButton extends android.support.v7.widget.AppCompatButton {

private ViewTreeObserver.OnPreDrawListener mPreDrawListener;

public MyButton(Context context) {
    super(context);
}

public MyButton(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (mPreDrawListener == null) {
        mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                offsetTopAndBottom(50);
                return true;
            }
        };

        ViewParent p = getParent();
        if (p instanceof View) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                ((View)p).getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
            }
        }
    }
}

public static class Behavior extends CoordinatorLayout.Behavior<MyButton> {

    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, MyButton child, int layoutDirection) {
        final List<View> dependencies = parent.getDependencies(child);
        return super.onLayoutChild(parent, child, layoutDirection);
    }

    @Override
    public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams lp) {
        if (lp.dodgeInsetEdges == Gravity.NO_GRAVITY) {
            // If the developer hasn't set dodgeInsetEdges, lets set it to BOTTOM so that
            // we dodge any Snackbars
            lp.dodgeInsetEdges = Gravity.BOTTOM;
        }
    }
}


}

步骤3.在app_bar_main布局中使用MyButton

android - android app在PreDraw上无限运行-LMLPHP

步骤4.在onPreDraw中设置一个断点,然后我可以看到它将被无限执行。如果我评论offsetTopAndBottom(50),一切都会很好。

android - android app在PreDraw上无限运行-LMLPHP

我还跟踪源代码,发现应用程序一次又一次接收到vsync信号,这导致Choreographer.java中的onVsync函数无限运行。为什么会这样?

更新资料

如果我按如下所示设置断点并注释onPreDraw,则最终将无法达到该断点,否则始终可以到达。

android - android app在PreDraw上无限运行-LMLPHP

最佳答案

在绘制每帧之前,将调用回调onPreDraw()。由于您通常会保持绘制帧(〜60fps),因此通常将其称为“无限”。

为了避免这种行为,通常的模式是将侦听器作为onPreDraw()中的第一条语句删除:

view.getViewTreeObserver().removeOnPreDrawListener(this);


其中view是您的情况下被the弃的父母。

您可以在this video中查看示例代码。工程师是Android Framework团队的成员。

10-06 00:32