“在Android 4.3上错误地调用了'requestLayout()”错误

时间:2013-10-03 23:37:25

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

我有一个简单的自定义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?

感谢您的帮助。

5 个答案:

答案 0 :(得分:6)

我发现我的应用中发生了这个错误。虽然在您提供的代码中找不到这种情况(如果您在代码中的其他位置执行此操作可能会有所帮助),但它有望帮助其他人解决这个不可能跟踪的问题。

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

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.
    }
});

在我的应用程序中,我不需要任何监听器,因此删除整个块可以解决此问题。对于那些需要这样的东西的人,记得在布局发生变化后(在这个回调中)删除监听器。

答案 1 :(得分:2)

Looking into the Android source,更详细地描述了这个问题:

  在布局期间调用了{p> 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)迁移到View对象的声明。

答案 2 :(得分:1)

我在自定义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()次电话会引发警告。

为了解决这个问题,我在类构造函数中为Typeface创建了实例变量,并在以后更改字体时使用它们,而不是每次都调用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);
    }
}

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

相关问题