【问题标题】:Force gridview to draw all tiles强制gridview绘制所有图块
【发布时间】:2011-10-15 06:10:44
【问题描述】:

我有一个 android gridview,我正在使用一些自定义滚动,让它在二维中滚动 - 这意味着不调用默认滚动。

我怀疑这可能是屏幕外的行不可见的原因。我知道它们在那里,它们会影响布局和一切,但它们从不绘制。

所以我的问题是 - 有没有办法强制 gridview 在加载时绘制它的所有图块,而不仅仅是可见的图块?

谢谢。

编辑:澄清一下 - 在我的 tileadapter 中,我将子计数设置为 225。在我的 gridview 中,对 getChildCount() 的调用返回 165。

再次编辑:仅当 gridview 的高度大于屏幕高度时才会发生这种情况 - y 轴上不在屏幕上的子项仅从子项计数中减去 - 将子项的大小设置为数字它们都紧贴在屏幕上的位置可以消除问题,但会破坏滚动的目的。

代码!

活动的XML布局:

  <LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:theme="@style/Theme.Custom"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <TextView android:id="@+id/logmessage"
  android:theme="@style/Theme.Custom"
  android:layout_width="fill_parent"
  android:layout_height="25dip"
  android:text="LogMessage"/>

  <RelativeLayout android:id="@+id/boardwrap"
  android:layout_weight="1"
  android:layout_height="fill_parent"
  android:layout_width="fill_parent"
  android:gravity="center_vertical">
  <com.MyProject.GameGrid 
    android:id="@+id/board"
    android:theme="@style/Theme.Custom"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:numColumns="15"
    android:stretchMode="none"
    android:verticalSpacing="0dip"
    android:horizontalSpacing="0dip"
    android:padding="0dip"
    android:columnWidth="20dip"
    android:scrollbars="none"/>
</RelativeLayout>
<RelativeLayout 
    android:id="@+id/toolbar"
    android:layout_width="fill_parent"
    android:layout_height="60dip"
    android:background="#FFFFFFFF"/>
</LinearLayout>

活动:

public class GameBoardActivity extends Activity {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.gameboard);

        GameGrid Board = (GameGrid)findViewById(R.id.board);
        Board.setAdapter(new TileAdapter(this));
    }
}

游戏网格:

public GameGrid(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setNumColumns(15);

        DisplayMetrics metrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
        scale = metrics.density;
        smallSize = Math.round(20 * scale);
        largeSize = Math.round(40 * scale);

        columnwidth = largeSize;
        this.setColumnWidth(columnwidth);
        Common.DebugMessage(Float.toString(columnwidth));

    }

您可能会注意到我在这里定义了一个小尺寸和一个大尺寸 - 双击屏幕可以在两者之间切换。

滚动(你之前帮过我的)

if (myState == TOUCH_STATE_SCROLLING) {
                    final int deltaX = (int) (mLastX - x);
                    final int deltaY = (int) (mLastY - y);
                    mLastX = x;
                    mLastY = y;

                    int xpos = this.getScrollX();
                    int ypos = this.getScrollY();

                    int maxX = (columnwidth * 15) - super.getWidth();
                    int maxY = (columnwidth * 15) - super.getHeight();

                    if (xpos + deltaX >= 0 && xpos + deltaX <= maxX && ypos + deltaY >= 0 && ypos + deltaY <= maxY )
                    {
                        this.scrollBy(deltaX, deltaY);
                    }
                    else {
                        this.scrollTo(xpos + deltaX <= 0 ? 0 : xpos + deltaX >= maxX ? maxX : xpos + deltaX,
                                      ypos + deltaY <= 0 ? 0 : ypos + deltaY >= maxY ? maxY : ypos + deltaY);
                    }
                    Common.DebugMessage(this.getChildCount());

                }

Common.DebugMessage 只是将调试消息打印到 LogCat 的辅助方法

TileAdapter:

public TileAdapter(Context c) {
        mContext = c;
    }

    @Override
    public int getCount() {
        return 225;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        int colWidth = ((GameGrid)parent).getColumnWidth();
        if (convertView == null) {
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(colWidth , colWidth));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(0, 0, 0, 0);
        }
        else {
            imageView = (ImageView)convertView;
        }
        imageView.setImageResource(R.drawable.tile);
        return imageView;
    }

【问题讨论】:

  • 安德烈亚斯,我刚刚发生了一些事情......尝试在你的onTouchEvent() 中添加一个myGridView.invalidate() 到你的ACTION_MOVE: 的末尾(在scrollBy()语句之后......)
  • 我之前尝试过 - 没有任何反应。之后调用 getChildCount 会得到相同的数字 (165),尽管在垂直空间较小的设备上运行应用程序会将该数字减少到可见行 * 15。
  • 安德烈亚斯,当你的赏金用完时,我会重新提供更高的赏金,这样我们就可以得到更多的关注。这样就不会浪费了。
  • 我给了你赏金——你已经得到了。我现在正在工作,但我会在今晚晚些时候继续测试。感谢您的辛勤工作。

标签: android android-gridview


【解决方案1】:

安德烈亚斯,

如果您的问题只是 onDraw() 问题。在GridView 中覆盖Draw(canvas) 可以很容易地做到这一点。这具有在加载 Activity 时增加处理器需求的副作用,但可以创建所需的效果。这样的覆盖如下:

 //This may be used in your GridView or Activity -- whichever provides the best result.
    public void draw(Canvas canvas)
    {   int _num = myGridView.getChildCount();
        for (int _i = _num; --_i >= 0; )
        {   View _child = (View)myGridView.getChildAt(_i);
            if (_child != null)
                _child.draw(canvas);
        }
    }

附加技术(编辑)

有时覆盖draw() 可能会产生不利影响。我们真正想做的是在对象可用时触发对象进行绘制。以类似方式覆盖invalidate() 通常会产生影响,具体取决于问题所在。由于我们已经确定覆盖 draw() 会得到奇怪的结果,这似乎是下一步的行动。

//This definitely goes in your GridView
public void invalidate()
{   int _num = myGridView.getChildCount();
    for (int _i = _num; --_i >= 0; )
    {   View _child = (View)myGridView.getChildAt(_i);
        if (_child != null)
            _child.invalidate();
    }
}

必须理解,这种技术可能不会在您需要时强制绘制,而只是让操作系统知道它已准备好在可用时重新绘制。因为失效不会自动向下级联View 树,如果您的ImageViews 嵌套比GridView 更深,您将不得不进行调整。这可以与draw() 结合使用或单独使用。此外,当与 invalidate() 语句的适当位置一起使用时,可能会降低响应,但应该有助于保持图像的绘制。

问题可能只是延迟布局或绘制问题。如果是这种情况,延迟加载解决方案可能是最好的。惰性加载器是一种在需要时加载内容的方法,因此它通常使用更少的内存和处理,并在需要时显示需要的内容。现在我在延迟加载方面并不出色,因为我很少需要。但是this site 有一个很好的代码示例。它也是为 GridView 量身定制的。

认为不适用(但可能对其他人有用)

我认为可能存在的唯一其他类型的问题是内存不足问题,它不会导致强制关闭(是的,它们确实存在)。这些是特别难以捉摸和痛苦的照顾。发现这些问题的唯一方法是在加载或滚动期间在 LogCat 中选择错误视图。或者,您可以检查整个 LogCat 转储(我建议您在首先运行应用程序之前清除日志)。

对于此类问题,了解图像的生命周期如何工作变得很重要。如果您的图像被缩小到缩略图大小以便显示,我会考虑在全尺寸图像旁边制作真正的缩略图。因为在运行时缩小图像暂时需要比保持原始大小更多的内存。由于这一切都发生在一次大扫除中,这可能是一个暂时的问题,会永久地表现出来。这将大大降低内存需求。 (例如,2x2 图像需要 16 字节加上 Header,而 4x4 图像需要 64 Bytes 加上 Header [400% 为两倍大小!!]。)

此外,将 System.gc() 添加到代码中的关键位置将强制进行垃圾收集,从而更频繁地释放更多内存。 (这并不能保证释放内存,但它的工作频率更高)。

最好的解决方案可能是三者的结合,但需要比我们在这个问题上获得的信息更多的信息。例如,我们需要查看您是否覆盖了 draw()onMeasure()onLayout() 以及其他一些细节。

【讨论】:

  • 好人,我会尝试你所有的建议。我确定问题不是内存不足异常。我已经在大量的 android 设备上尝试过该程序,但问题总是相同的 - 首次加载 gridview 时不可见的行不会绘制,不管它们中有多少在屏幕外。我很快就会上传一个插图。
  • 至于其他事情 - 我没有覆盖 draw、onMeasure 或 onLayout。但是,我在 GridView 的子类中进行编码,而不是在活动中进行编码,如果这有什么不同的话。我还刚刚尝试了您提供的覆盖绘制方法,可以说是最不有趣的结果 - this.getChildCount() 返回 165(与加载时屏幕上的子项数量一致),而实际数量为 225。另外它出于某种原因,仅在被覆盖时才绘制第一个图块,即使它确实对所有子图块调用了绘制。
  • 这确实很有趣......它应该绘制所有的孩子(不考虑视口)。好的,所以它不是内存不足。肯定是画的问题。很高兴知道...由于您的代码主要在您的GridView 中,我需要问... draw() 代码在GridView 还是Activity 中?我问是因为你的 draw() 对绘制的内容有很大的影响。例如,当父母 View 与其他语言一样时,我很惊讶地得知孩子 View 不会失效。 附言我讨厌平局问题... ROFL
  • 我刚刚尝试覆盖无效,子数仅为 165 的问题仍然存在,覆盖的唯一结果是整个屏幕上的伪影:(
  • 这里我们缺少一些东西。是否有指向您的代码的链接?我需要查看您的 TileAdapter、GridView、XML(如果有 GridView 和 Tiles)。以上都是标准技术,其中一项或多项本应提供更好的结果。
【解决方案2】:

老实说,我的建议是停止以明显不打算使用的方式对平台 API 进行攻击。即使通过一些扭曲,您设法使用基本 GridView 代码玩了足够多的游戏以使其完成您想做的事情......您对您的代码将继续使用作为平台的 GridView 实现的微小更改有多大信心进化。

而且真的没有必要玩这类游戏。 GridView 没有什么特别之处——它只是一个视图的实现,它把东西放在一个可以水平滚动的网格中?

如果 GridView 的行为不是您想要的,解决方案是编写您自己的视图来执行您想要的操作。而对于 Android,这更容易,因为您可以直接从开源平台获取 GridView 代码作为基础,在您的应用程序中进行编译(您可能需要调整一些东西,因为现在的代码需要不需要完全针对 SDK 编写,所以可能不是...但是 没有 它正在做的事情是您在针对 SDK 的常规应用程序构建中无法做到的),并且然后将您应用程序中的代码修改为您想要的内容,使其做任何想做的事情。无需与实际上无法执行您想要的操作的内置小部件进行斗争。如果将来底层 GridView 实现发生变化,您无需担心您精心构建的纸牌屋会倒塌。

【讨论】:

    【解决方案3】:

    我已经提交了另一个答案,因为在澄清之后,这个问题肯定与我最初认为的不同。但是,不应忘记上一个答案中的技术,并且是解决其他相关问题的重要资源,因此我将其保留在那里。

    问题:

    GridView 现在绘制(有点),但一遍又一遍地重复相同的图像,导致可怕的伪影。由此产生的工件非常糟糕,以至于它并不能很好地表明其他视图是否存在。这个问题实际上是由“透明度太高”的问题引起的。

    Android 和透明度

    Android 在处理透明度方面做得非常出色,允许在 top 视图处于焦点时绘制当前视图 below 的对象。但是,如果所有的 View 都是透明的,那么当需要刷新时,Android 就没有任何东西可以在后面绘制。通常,这不是问题。

    一般来说,开发人员会使用我们提供的工具,然后尝试用它们做一些简单的事情。 (耶!)只要我们使用它们,Android 就会说(几乎)“我知道该怎么做!!”然而,当我们从自定义视图开始时,Android 可能会有些吃惊,尤其是在适当地绘制时。

    您的 GridView 实际上不是 GridView。这是一个扩展 GridView。作为 Android View 的扩展,Android 不会对它应该如何绘制做出任何假设,太棒了......因此,您的 GridView 的正常不透明背景不存在。有人会想,“但我在RelativeLayout 中有它。这还不够吗?” 答案是否定的。布局对象是布局对象。除非我们指定他们,否则他们没有背景。

    当您进行滚动和其他类似的动作或动画时,这种情况会变得更加严重。在这些情况下,Android 会尝试缓存。缓存发生在所有的视图之下,但结果是,由于它是透明的,你可以看到整个过程。并且在需要清除缓存之前它不会重置。这就是为什么越来越丑陋。

    换句话说,“窗口”可能看起来是黑色的,但实际上并不是黑色的......

    解决方案(第一部分):

    所以答案是为扩展的GridView 或其父视图之一设置不透明背景。这应该可以解决您在滚动时看到的工件。它应该允许您正确查看其他视图如何以及是否正在呈现。最终,您想尝试将背景应用于包含所有视图的最顶层视图。背景可以像设置背景颜色一样简单(因为它创建了一个不透明的可绘制对象)。

    后续步骤:

    我注意到您省略了一些相关代码。我真的需要知道你的TileAdapter 扩展了什么样的Adapter 以及它如何获取它的基本信息。这可能会影响它如何获得和占用平局事件。一旦我们知道了这一点,我们就可以着手解决其余的问题。

    我还需要知道您是如何定位 Tiles 的(没有定位代码)。由于没有定位代码,因此无法知道 Views 是否真正被添加。 ImageViews 的默认行为是,如果它们中的至少一部分不可见,则它们不会被添加到层次结构中,直到它们可见。我们想强制解决这个问题

    最终结果可能仍需要调整布局、测量或绘图,但在我们准确表示正在发生的事情之前我们不会知道。

    另一种结果

    我希望滚动游戏更频繁地发生的事情是,如果它们开始是从缩小的位置开始的,我们可以“移动”到放大的位置。这可以很容易地解决您的 ChildCount,因为你说如果它整齐地放在屏幕上,他们都会画画。最初的放大可能会在所有内容都加载完毕后发生。然后你有一个很好的图形效果,表明加载完成。缩小只是一个普通的动画,很容易实现。你知道你所有的孩子都装满了。此外,它可能会限制您必须输入的代码才能使其正常工作。最后,这一切都可以用同一个 GameGrid 对象来完成。 :)

    我不知道你有没有想过这个,但我想,“嘿,这似乎是用一块石头杀死超过 2 只鸟的简单方法。”

    【讨论】:

    • 您好,再次感谢您对我的问题感兴趣。我现在可以回答的一件事是 TileAdapter 扩展了 BaseAdapter。从“缩小”的网格开始并没有做很多事情,我可以看到所有 225 个孩子,但是一旦我放大,它就会恢复到 165 个孩子。您可能已经注意到,我的缩放实际上只是所有孩子大小的两倍。您还没有看到任何位置代码,因为没有。我信任 GridView 来处理那部分,否则我可能会使用画布或其他东西。
    • 太棒了。那绝对没问题。我只需要知道在这些领域发生了什么,如果有的话。那么,当您放大时,这些孩子会从层次结构中移除自己吗?
    • 我想是的,他们之前是 225,当放大时,屏幕上放不下的行被删除了。
    • 你只是为你的缩放做一个简单的动画吗?还是你在重绘网格?
    【解决方案4】:

    覆盖适配器的getCount() 并返回适配器的底层数组长度。然后使用mAdapter.getCount 而不是mGrid.getChildCount。 getChildCount 仅返回可见的子项,而 getCount 会为您提供数据集中的子项总数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-03-02
      • 2015-12-27
      • 2011-02-11
      • 2014-05-09
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      相关资源
      最近更新 更多