如何在使用EditText时避免全视图层次结构重新布局?

时间:2016-02-01 12:50:07

标签: android android-layout android-edittext

背景

我在活动中向用户显示了相当复杂的布局。

其中一个观点是EditText。

由于我不得不让其中一个视图留在软键盘后面,但其余部分仍然在它上面,我不得不听取视图布局的变化(写一下here)。

问题

我注意到只要EditText具有焦点并显示其插入符号,整个视图层次结构就会重新布局。

您可以通过查看我创建的监听器的日志或通过开发人员设置启用“show surface updates”来查看它。

这会导致某些设备的性能下降,尤其是当活动的布局很复杂或者片段具有复杂的布局时。

代码

我不打算显示原始代码,但有一种简单的方法可以重现这个问题:

activity_main.xml中

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.user.myapplication.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="just some text"/>

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="phone"
            android:text="write here"
            android:textSize="18dp"/>


        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="just some text 2"/>
    </LinearLayout>
</FrameLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(android.R.id.content).getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {

            @Override
            public boolean onPreDraw() {
                Log.d("AppLog", "onPreDraw");
                return true;
            }
        });
    }
}

我尝试了什么

当禁用插入符号时(使用“cursorVisible”,由于某种原因,它被称为“游标”),我可以看到问题不存在。

我试图找到内置插入符号行为的替代方法,但我找不到。我发现的只有this post,但它似乎使它静止,我不确定它的表现如何(性能和兼容性与普通插入符号相比)。

我试图强制设置EditText的大小,这样就不需要导致包含它的布局失效。它不起作用。

我还注意到,在原始应用中,即使应用程序进入后台,日志也可以(出于某种原因)继续写入。

我已经报道了这个问题(包括示例和视频)here,希望Google会显示错误或解决此问题。

问题

有没有办法避免重新布局整个视图层次结构?仍然让EditText具有与普通EditText相同的外观和感觉的方法?

也许可以自定义EditText与插入符号的行为方式?

2 个答案:

答案 0 :(得分:1)

  

我注意到只要EditText有焦点并显示其插入符号,   整个视图层次结构得到重新布局。

事实并非如此。 EditText的大小和位置是不变的 - 没有重新布局。您可以使用以下代码进行检查。

findViewById(android.R.id.content).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    Log.d("AppLog", "Layout phase");
  }
});

由于插入符号闪烁 - EditText会不断调用invalidate()。这迫使GPU再次绘制EditText。

在我的nexus 5(棉花糖)上,我看到只有EditText正在重绘(显示GPU视图更新 - 已启用)。

enter image description here

答案 1 :(得分:1)

如何覆盖dispatchOnPreDraw()您在活动中使用的所有视图并使用标记来检查特定视图是否需要重绘?

因为只有在文本视图处于焦点时才需要禁用所有其他视图的重绘。因此,当文本视图处于焦点时,会有一个标志来禁用重绘其他视图。

如果dispatchOnPreDraw()方法返回false,那么将继续刷新视图。我不知道您的布局有多复杂以及使用了多少视图,但是这里一个单独的类应该扩展使用的视图并覆盖该方法,并且还需要一个机制/变量来区分当前焦点中的对象。

希望这种方法有所帮助!