Android 在具有每项背景的ListView中消除overdraw

Android 在具有每项背景的ListView中消除overdraw,android,listview,optimization,Android,Listview,Optimization,我正在使用一个列表视图,其中列表项有一个后台资源。我想尽可能地减少透支。我知道Romain Guy的博客上有这篇文章,但我很难完全优化列表视图。简化示例的代码显示在本文的底部。该示例基本上只是“新建活动”向导,其中包含一个列表视图。我的问题基于这个例子。这是一个屏幕截图,带有和不带有初始未优化案例的透支标记: 见证页面有灰色背景(在我的真实项目中是纹理),列表项有白色背景(在我的真实项目中是九块补丁)。透支是戏剧性的,屏幕的任何部分都不会只绘制一次,列表项在显示内容字母之前会绘制三次 在窗口中

我正在使用一个
列表视图
,其中列表项有一个后台资源。我想尽可能地减少透支。我知道Romain Guy的博客上有这篇文章,但我很难完全优化列表视图。简化示例的代码显示在本文的底部。该示例基本上只是“新建活动”向导,其中包含一个列表视图。我的问题基于这个例子。这是一个屏幕截图,带有和不带有初始未优化案例的透支标记:

见证页面有灰色背景(在我的真实项目中是纹理),列表项有白色背景(在我的真实项目中是九块补丁)。透支是戏剧性的,屏幕的任何部分都不会只绘制一次,列表项在显示内容字母之前会绘制三次

窗口
中很容易去除装饰视图背景,并切割一个完整的透支层。如果列表包含的项目足够填满整个屏幕,我可以在一个非常好的位置删除
ListView
背景:

不幸的是,当列表中的项目少于整个屏幕中的项目时,这不起作用。如果我没有设置任何背景,列表项下就没有任何内容。如果我在列表视图(或任何父视图)上设置了背景,我将获得所有确实存在的列表项的完全透支:

这是较小的邪恶,但令人不满,因为大多数时候我的列表都会从屏幕上消失。是否有可靠的方法可以在列表视图中最后一项之后获得背景,而不引入overdraw

此问题中使用的示例代码: 活动:

//MainActivity.java
公共类MainActivity扩展了活动{
私有静态最终字符串[]数据={“Alpha”,“Bravo”,“Charlie”,“Delta”,“Echo”,“Foxtrot”,“Golf”,“Hotel”,“India”,“Juliet”};
@在创建时覆盖受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//优化1:getWindow().setBackgroundDrawableResource(android.R.color.transparent);
ListView lv=(ListView)findViewById(android.R.id.list);
lv.setAdapter(新阵列适配器)(此,R.layout.activity\u main\u列表项,
android.R.id.text1,数据);
}
}
布局:



将ListView置于线性布局中,将ListView的高度设置为包裹内容,并在其下方放置另一个布局为“权重”的视图。把你的背景放在这个视图上。

我想出了一个自己的解决方案,避免了j_um的嵌套权重问题。另一方面,它不能轻松地与其他ListView自定义组合。对于列表视图的高度,这可能比包含
WRAP\u内容的解决方案更有效,因为在测量过程中,列表视图不必
getView(…)
其子视图

当然,
ListView#onMeasure()
实现非常聪明,一旦它有足够的高度来填充其父视图,并且在
onDraw()
期间它将进入
getView()
这些相同的位置,它就可以停止测量子视图,因此效率参数似乎没有什么意义。然而,Romain Guy认为ListView不应该使用
WRAP\u CONTENT

/**
*一个列表视图,用于具有自己可绘制背景的列表项。这样的列表视图
*遭受GPU对列表视图(祖先)背景顶部的项目背景的透支。
*
*此子类检测其数据集何时包含足够的元素以填充所有可用空间
*然后开始滚动。如果是这种情况,它会将自己的背景设置为透明。
*

*限制:页眉和页脚视图被忽略。如果列表视图的 *足够多的项目使其无法滚动,但页眉和/或页脚足够大,足以使其滚动 *无论如何滚动,背景不会隐藏,并且会显示overdraw。 *

*来源:https://stackoverflow.com/q/15625930/49489 CC-BY-SA

*/ 公共类BackgroundListView扩展了ListView{ /**我们需要我们自己的实例,因为它稍后会用作哨兵值*/ 私有静态最终可绘制透明=新可绘制彩色(0x00000000); /**如果为true,则对{@code onDraw(Canvas)}的下一个调用将计算是显示还是隐藏背景*/ 私有布尔值mNeedsBackgroundCheck=true; /**列表缩小时要恢复的背景*/ 私人可牵引mOriginalBackground; public BackgroundListView(上下文、属性集属性、int-defStyle){ 超级(上下文、属性、定义样式); } public BackgroundListView(上下文、属性集属性){ 这(上下文,属性,0); } 公共背景列表视图(上下文){ 这个(上下文,null,0); } @凌驾 充气时受保护的空隙(){ 超级充气(); this.mOriginalBackground=this.getBackground(); } @凌驾 公共空间背景(可绘制背景){ 超级挫折背景(背景); 可绘制的newBackground=getBackground(); if(新背景!=透明){ this.mOriginalBackground=newBackground; } } @凌驾 公共资源(内部剩余){ 超级挫折资源(resid); 可绘制的newBackground=getBackground(); if(新背景!=透明){ this.mOriginalBackground=newBackground; } } @凌驾 受保护的void onDraw(画布){ 如果(mNeedsBackgroundCheck){ maybeHideBackground(); } super.onDraw(帆布); } @凌驾 已更改尺寸的受保护空心(整数w、整数h、整数oldw、整数oldh){ super.onSizeChanged(w,h,oldw,oldh); mNeedsBackgroundCh