我有一个android gridview,我正在使用一些自定义滚动,让它在两个维度滚动 - 这意味着不会调用默认滚动。
我怀疑这可能是屏幕外的行不可见的原因。我知道他们在那里,他们会影响布局和一切,但他们从不画画。
所以我的问题是 - 有没有办法强制gridview在加载时绘制所有的tile,而不仅仅是可见的?
感谢。
编辑:澄清 - 在我的tileadapter中,我将子计数设置为225.在我的gridview中,对getChildCount()的调用返回165.
再次编辑:仅当网格视图的高度大于屏幕的高度时才会发生这种情况 - 只需从子计数中减去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));
}
}
GameGrid:
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;
}
答案 0 :(得分:5)
安德烈亚斯,
如果您的问题只是onDraw()
问题。您可以通过Draw(canvas)
中覆盖的GridView
轻松完成此操作。这会产生在加载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
树,如果您的ImageView
嵌套深度超过GridView
,则必须进行调整。这可以与draw()
或独立使用。此外,与适当放置invalidate()
语句一起使用时,可能会降低响应但有助于保持图像的绘制。
问题可能只是延迟布局或绘制问题。如果是这种情况,延迟加载解决方案可能是最好的。延迟加载器是一种在需要时加载内容的方法,因此它通常使用较少的内存和处理,并在需要时显示所需内容。现在我在Lazy Loading上并不是很棒,因为我很少有需要。但是this site有一个很好的代码示例。它也是为GridView量身定制的。
视为不适用(但可能对其他人有用)
我认为唯一的另一类问题是内存不足问题,不会导致强制关闭(是的,它们确实存在)。这些特别难以捉摸和痛苦照顾。在加载或滚动期间,在LogCat中发现这些的唯一方法是选择错误视图。或者您可以浏览整个LogCat转储(我建议您在首先运行应用程序之前清除日志)。
有了这样的问题,了解图像的生命周期是如何工作变得很重要。如果您的图像缩小到缩略图大小以便显示,我会考虑在全尺寸图像旁边制作真正的缩略图。因为在运行时缩小图像暂时需要比保持原始大小更多的内存。由于这一切都是一次性发生,这可能是一个暂时的问题,永久地展现出来。这将大大降低内存需求。 (例如,2x2图像需要16个字节加上标题,而4x4图像需要64个字节加上标题[400%,大小加倍!!]。
此外,将System.gc()添加到代码中的关键位置将强制进行垃圾回收,从而更频繁地释放更多内存。 (这不是释放内存的保证,但比不使用更常见。)
最佳解决方案可能是三者的组合,但需要的信息比我们对此问题的信息要多一些。例如,我们需要查看您是否已覆盖draw()
和onMeasure()
以及onLayout()
以及其他一些细节。
答案 1 :(得分:4)
老实说,我的建议是以一种显然不打算使用的方式停止对平台API的攻击。即使通过一些扭曲,你设法用基本的GridView代码播放足够的游戏,使它做你想做的事情......你对自己的代码将继续使用GridView实现作为平台的微小变化有多大的信心演变。
真的没有必要玩这类游戏。 GridView没有什么特别之处 - 它只是一个视图的实现,它将事物放在一个可以水平滚动的网格中?
如果GridView的行为不是您想要的,那么解决方案就是编写您自己的视图来执行您想要的操作。使用Android,这更容易,因为你可以直接从开源平台获取GridView代码作为基础,在你的应用程序中进行编译(你可能需要调整一些东西,因为它所代表的代码需要不需要纯粹针对SDK编写,所以可能不是......但是没有它正在做的事情,你不能在针对SDK的常规应用程序构建中做到),并且然后将您应用中的代码修改为您心中的内容,使其满足您的需求。不与内置的小部件打架,实际上并没有做你想要的。如果底层的GridView实现在未来发生变化,并且不用担心你精心构建的卡片会崩溃。
答案 2 :(得分:0)
我提交了另一个答案,因为在澄清之后,这个问题肯定与我最初认为的不同。不过,不应忘记上一个答案中的技巧,并且这些技术是其他相关问题的重要资源,因此我将其保留在那里。
<强>问题:强>
GridView现在绘制(有点),但是一遍又一遍地重复相同的图像,导致可怕的伪像。产生的伪像是如此糟糕,以至于它并不能真正表明其他视图是否存在。这个问题实际上是由“透明度太高”的问题引起的。
Android和透明度
Android在处理透明度方面做得非常出色,允许在 top 视图聚焦时绘制当前视图下的对象。但是,如果所有视图都是透明的,那么当需要刷新时,Android无需在所有视图后面绘制。通常,这不是问题。
一般来说,开发人员使用我们提供的工具,只是尝试用它们做一些巧妙的事情。 (YAY!)只要我们使用它们,Android就会说(差不多)“我知道该怎么办!!”然而,当我们开始使用自定义视图时,Android可能会有点吓坏,特别是在适当绘制时。
您的GridView实际上不是GridView
。这是扩展 GridView
。作为Android视图的扩展,Android不会假设它应该如何绘制,因此......因此,GridView的正常不透明背景不存在。有人会想,“但我在RelativeLayout
中有它。还不够吗?”答案是否定的。布局对象是布局对象。除非我们指定它们,否则它们没有背景。
当您进行滚动和其他类似的动作或动画时,这会变得更加严重。在这些情况下,Android会尝试缓存。缓存发生在视图的全部之下,但结果是因为它全部透明,所以您可以看到整个过程。在需要清除缓存之前,它不会重置。这就是为什么变得更加丑陋和丑陋。
换句话说,“窗口”可能显示为黑色,但实际上并不是黑色......
解决方案(第一部分):
所以答案是为扩展的GridView
或其父视图之一设置一个不透明的背景。这应该可以解决滚动时看到的工件。它应该允许您正确地查看其他视图是如何以及是否呈现。最终,您希望尝试将背景应用于包含所有视图的最顶层视图。背景可以像设置背景颜色一样简单(因为它创建了一个不透明的drawable)。
后续步骤:
我注意到你省略了一些相关的代码。我真的需要知道你的Adapter
扩展到什么类型TileAdapter
以及它如何得到它的基本信息。这可能会影响它如何获取和占用抽奖活动。一旦我们知道了,我们就可以着手解决其余问题。
我还需要知道你如何定位Tiles(没有定位代码)。由于没有定位代码,因此无法知道是否实际添加了视图。 ImageViews的默认行为是,如果它们中至少有一部分不可见,则它们不会被添加到层次结构中,直到它们为止。我们想强制解决这个问题
最终结果可能仍需要调整布局,测量或绘制,但我们不会知道,直到我们准确表示正在发生的事情。
替代结果
有些事情,我希望更频繁地滚动游戏,如果他们开始缩小的位置,我们可以“移动”到放大的位置..这可以很容易地解决你的ChildCount,如你说如果它整齐地放在屏幕上,它们都会画出来。一切都被加载后,可能会发生初始放大。然后你有一个很好的图形效果,表明加载完成。缩小只是一个普通的动画,因此易于实现。你知道你所有的孩子都装满了。此外,它可能会限制您必须输入的代码才能使其正常工作。最后,它可以使用相同的GameGrid对象完成。 :)
我不知道你是否想过这个,但我想,“嘿,这似乎是一石二鸟的简单方法。”
答案 3 :(得分:0)
覆盖适配器的getCount()
并返回适配器的基础数组的长度。然后使用mAdapter.getCount而不是mGrid.getChildCount。 getChildCount仅返回可见子项,而getCount将为您提供数据集中的子项。