【问题标题】:"requestLayout() improperly called by..." error on Android 4.3Android 4.3 上的“requestLayout() 不正确地被...调用”错误
【发布时间】:2013-10-03 23:37:24
【问题描述】:

我有一个简单的自定义 TextView,它在其构造函数中设置自定义字体,如下面的代码

public class MyTextView extends TextView {

    @Inject CustomTypeface customTypeface;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        RoboGuice.injectMembers(context, this);
        setTypeface(customTypeface.getTypeface(context, attrs));
        setPaintFlags(getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    }
}

从 Gingerbread 到 JB 4.2 都可以正常工作。但是当我在 Android 4.3 手机上显示我的自定义文本视图时,adb logcat 中充斥着以下消息。

10-05 16:09:15.225: WARN/View(9864): requestLayout() improperly called by com.cmp.views.MyTextView{42441b00 V.ED.... ......ID 18,218-456,270 #7f060085 app:id/summary} during layout: running second layout pass
10-05 16:09:15.225: WARN/View(9864): requestLayout() improperly called by com.cmp.views.MyTextView{423753d0 V.ED.... ......ID 26,176-742,278 #7f060085 app:id/summary} during layout: running second layout pass

我注意到,它确实会减慢 UI 速度。任何想法为什么会在 4.3 上发生?

感谢您的帮助。

【问题讨论】:

  • 您是否尝试过在视图生命周期的后期移动setTypeface() 和/或setPaintFlags(),例如onFinishInflate() 之类的?我的猜测是setTypeface() 正在触发requestLayout(),因为他们可能没想到它会在视图构造函数中被调用。
  • 我尝试将其移至 onFinishInflate(),但也无济于事。我在日志中看到了那些 requestLayout() 消息
  • MyTextView 的唯一目的是设置自定义字体吗?创建自定义视图来设置自定义字体不是一个好的解决方案。
  • 它还设置了绘制标志,例如添加 Paint.SUBPIXEL_TEXT_FLAG。我不想在多个位置设置绘制标志,因为此文本视图用于应用程序的许多布局中。
  • 我也看到了,我只打电话给setTypeface()

标签: android android-layout textview android-custom-view


【解决方案1】:

我在我的应用程序中找到了此错误发生的位置。虽然在您提供的代码中没有发现这种情况(如果您在代码的其他地方这样做可能会有所帮助),但它有望帮助其他人解决这个无法追踪的问题。

我有一条线(当然不是我添加的):

myView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        //this would then make a call to update another view's layout.
    }
});

在我的应用程序中,我不需要任何侦听器,因此删除整个块可以解决此问题。对于那些需要这样的东西的人,请记住在布局更改后(在此回调内部)删除侦听器。

【讨论】:

    【解决方案2】:

    Looking into the Android source,这个问题描述的更详细一点:

    requestLayout() 在布局期间被调用。如果没有在请求视图上设置布局请求标志,则没有问题。如果某些请求仍处于待处理状态,那么我们需要清除这些标志并执行完整的请求/测量/布局传递以处理这种情况。

    看来问题可能与 Roboguice 有关;见issue #88。建议的解决方案是使用@InjectView:

    您现在可以在视图类中使用@InjectView。填充视图后只需调用 Injector.injectMembers(),ala:

    public class InjectedView extends FrameLayout {
    
        @InjectView(R.id.view1) View v;
    
        public InjectedView(Context context) {
            super(context);
            final View child = new View(context);
            child.setId(R.id.view1);
            addView(child);
    
            RoboGuice.getInjector(context).injectMembers(this);
        }
    }
    

    也许您应该考虑使用@InjectView 注释将RoboGuice.injectMembers(context, this) 迁移到您的视图对象的声明中。

    【讨论】:

    • 我根本没有使用 Roboguice,只是在我的 onCreate 中设置了一个字体。关于为什么会在我的情况下发生这种情况的任何想法或任何解决方法?
    • Inspecting the AOSP source, setTypeface() 确实调用了requestLayout(),这是有道理的,因为尺寸可能需要更改。我不确定为什么这会导致布局传递冲突。也许您可以尝试简化布局?我对此感到困惑。
    【解决方案3】:

    我在我的自定义 ListView 项目(LinearLayout 子类)中修复了这些警告。这个类实现了Checkable,并且有一个setChecked(boolean checked)方法被调用来指示该项是否被选中:

    @Override
    public void setChecked(boolean checked) {
        mChecked = checked;
        TextView textView = (TextView)this.findViewById(R.id.drawer_list_item_title_text_view);
        if(mChecked) {
            textView.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "font/Andada-Bold.ttf"));
        }
        else {
            textView.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "font/Andada-Regular.ttf"));
        }
    }
    

    我通过在我的视图中的 textView 上调用 setTypeFace() 来直观地指示选中状态,在常规字体和粗体字体之间切换。这些setTypeFace() 调用导致了警告。

    为了解决这个问题,我在类构造函数中为Typefaces 创建了实例变量,然后在更改字体时使用它们,而不是每次都调用Typeface.createFromAsset(...)

    private Typeface mBoldTypeface;
    private Typeface mRegularTypeface;
    
    public DrawerView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initTypefaces();
    }
    
    private void initTypefaces() {
            this.mBoldTypeface = Typeface.createFromAsset(getContext().getAssets(), "font/Andada-Bold.ttf");
            this.mRegularTypeface = Typeface.createFromAsset(getContext().getAssets(), "font/Andada-Regular.ttf");
    }
    
    @Override
    public void setChecked(boolean checked) {
        mChecked = checked;
        TextView textView = (TextView)this.findViewById(R.id.drawer_list_item_title_text_view);
        if(mChecked) {
            textView.setTypeface(mBoldTypeface);
        }
        else {
            textView.setTypeface(mRegularTypeface);
        }
    }
    

    这是一个非常具体的场景,但我很惊喜地找到了解决方法,也许其他人也遇到了同样的情况。

    【讨论】:

      【解决方案4】:

      请检查天气,任何视图的 ID 在同一活动上下文中重复。我也得到了同样的警告,我反复使用 TextView 一个具有相同 ID 的循环。我通过每次使用不同的 id 解决了这个问题。

      【讨论】:

      • 我对文本视图有同样的问题,但不能更改每个视图的 id!你能发布一些代码吗?
      【解决方案5】:

      我遇到了同样的问题。那是因为我试图以 iOS 方式设置视图的位置。你知道在 iOS 中通过设置视图的左上角值和宽度、高度来设置视图的位置。但在 Android 中,它应该是(左、上、右、下)。我确实非常关注这一点。当我跳转到layout() 定义时,我阅读了该方法的注释,然后我找出了警告发生的原因。

      【讨论】:

        猜你喜欢
        • 2014-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-31
        • 2014-01-26
        相关资源
        最近更新 更多