【问题标题】:TextView: onDraw getting called only onceTextView:onDraw 只被调用一次
【发布时间】:2017-10-28 08:24:19
【问题描述】:

我有一种情况,我想调试对TextView.onDraw() 的调用,所以我将其子类化如下:

public class MyTextView extends AppCompatTextView {

    View parent;

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

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

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

    public void storeParent(View parent) {
        this.parent = parent;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }
}

并将其放在这样的层次结构中:(参见MyTextView

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

    <LinearLayout
        android:id="@+id/scroll_container"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.example.scrollviewtest1.MyTextView
            android:id="@+id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/large_text" />

        <TextView
            android:id="@+id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!" />

    </LinearLayout>


</ScrollView>

最后,我为这个TextView 放了一大段文字,这样它就可以滚动了。

现在,如果我在 onDraw() 方法中放置一个断点,我只会得到一个调用。根据我的理解,当我向上和向下滚动时,我应该会接到它的电话。为什么会这样?

旁注:我已经尝试设置setWillNotDraw(false),结果没有变化。

【问题讨论】:

  • 通常情况下,您不应该对onDraw() 方法的调用次数做出假设。为什么需要调用它?
  • @azizbekian :用于测试目的;不生产
  • @azizbekian :但是,当您向上或向下滚动时,它应该在逻辑上被调用
  • 你可以使用text2.invalidate();
  • But, it should get called logically, when you scroll up or down 同意,我也希望onDraw() 被调用,但可能是因为该视图在ScrollView 内部,而您只需执行滚动,那么只有TextViewDisplayList 是正在更新,导致优化不调用每个孩子的onDraw()方法,这个我不确定。

标签: java android textview android-custom-view


【解决方案1】:

感谢@pskink 建议为此关闭硬件加速;有效。以下是docs 对此的评价:

硬件加速绘图模型

Android系统仍然使用invalidate()和draw()请求 屏幕更新并渲染视图,但处理实际绘图 不同。而不是立即执行绘图命令, Android 系统将它们记录在显示列表中,其中包含 视图层次结构的绘图代码的输出。另一个优化 是Android系统只需要记录和更新显示 由 invalidate() 调用标记为脏的视图列表。具有的视图 未失效的可以通过重新发行简单地重新绘制 以前记录的显示列表。新的绘图模型包含三个 阶段:

Invalidate the hierarchy
Record and update display lists
Draw the display lists

使用此模型,您不能依赖与脏视图相交的视图 区域以执行其 draw() 方法。为确保安卓 系统记录视图的显示列表,必须调用 invalidate()。 忘记这样做会导致视图看起来相同,即使它已经 已更改。

使用显示列表也有利于动画性能,因为 设置特定属性(例如 alpha 或旋转)不会 要求使目标视图无效(它是自动完成的)。 此优化也适用于带有显示列表的视图(任何视图 当您的应用程序是硬件加速时。)例如,假设 有一个 LinearLayout 包含一个 ListView 上面的按钮。这 LinearLayout 的显示列表如下所示:

DrawDisplayList(ListView)
DrawDisplayList(Button)

现在假设您要更改 ListView 的不透明度。后 在 ListView 上调用 setAlpha(0.5f),显示列表现在包含 这个:

SaveLayerAlpha(0.5)
DrawDisplayList(ListView)
Restore
DrawDisplayList(Button)

ListView的复杂绘制代码没有执行。相反, 系统只更新了更简单的 LinearLayout 的显示列表。 在未启用硬件加速的应用程序中,绘图 再次执行列表及其父级的代码。

VS

基于软件的绘图模型

在软件绘图模型中,视图用以下两种方式绘制 步骤:

Invalidate the hierarchy
Draw the hierarchy

每当应用程序需要更新其 UI 的一部分时,它都会调用 invalidate() (或其变体之一)在任何已更改的视图上 内容。失效消息一直向上传播 视图层次结构来计算需要的屏幕区域 重绘(脏区)。然后 Android 系统在其中绘制任何视图 与脏区相交的层次结构。很遗憾, 这种绘图模型有两个缺点:

First, this model requires execution of a lot of code on every draw pass. For example, if your application calls invalidate() on a

按钮,该按钮位于另一个视图的顶部,Android 系统 重绘视图,即使它没有改变。 第二个问题是绘图模型可以隐藏应用程序中的错误。由于Android系统在他们重绘视图时 与脏区域相交,您更改其内容的视图可能是 即使没有调用 invalidate() 也会重绘。当这 发生时,您依赖于另一个无效的视图来获取 正确的行为。每次修改时,此行为都会发生变化 你的申请。因此,您应该始终调用 invalidate() 每当您修改影响的数据或状态时,在您的自定义视图上 视图的绘制代码。

注意: Android 视图会在其属性发生变化时自动调用 invalidate(),例如背景颜色或 文本视图。

【讨论】:

    猜你喜欢
    • 2021-01-04
    • 2019-08-23
    • 2017-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多