我知道色带是一个老问题,之前已经讨论过很多次,提供了各种解决方案(基本上归结为始终使用 32 位,或使用抖动)。事实上,不久前我问过并随后回答了我自己关于这个的 SO 问题。那时,我认为我在该问题的答案中提出的解决方案(适用setFormat(PixelFormat.RGBA_8888)
于a 的情况下Window
也适用于Holder
a SurfaceView
)已经解决了我的应用程序中的问题。至少该解决方案使渐变在我当时开发的设备(很可能是 Android 2.2)上看起来非常漂亮。
我现在正在使用 HTC One X (Android 4.0) 和 Asus Nexus 7 (Android 4.1) 进行开发。我试图做的是对 a 的整个区域应用灰色渐变SurfaceView
。即使我应该确保包含Window
和Holder
配置为 32 位颜色,但我得到了可怕的条带伪影。事实上,在 Nexus 7 上,我什至可以看到工件在移动。这不仅发生在SurfaceView
当然连续绘制的情况下,而且发生在View
我为测试目的而添加的法线中,以绘制完全相同的渐变,这本来会绘制一次。这些伪影存在并且似乎四处移动的方式当然看起来非常糟糕,实际上就像观看信号不佳的模拟电视一样。这俩View
并SurfaceView
展示完全相同的文物,它们一起移动。
我的意图是始终使用 32 位,而不是使用抖动。我的印象是Window
早在 Android 4.0 之前默认是 32 位的。通过应用RGBA_8888
,SurfaceView
我本来希望一切都是 32 位的,因此避免了任何伪影。
我确实注意到关于 SO 的其他一些问题,人们观察到RGBA_8888
在 4.0 / 4.1 平台上似乎不再有效。
这是我的 Nexus 7 的屏幕截图,View
顶部为法线,SurfaceView
下方为法线,两者都将相同的渐变应用于Canvas
. 当然,它不会像查看显示器时那样显示伪影,因此显示此屏幕抓取可能毫无意义。我想强调的是,在 Nexus 的屏幕上,条带确实看起来很糟糕。编辑:事实上,屏幕截图根本没有显示工件。我在 Nexus 7 上看到的伪影不是统一的条带;它本质上看起来是随机的。
Activity
用于创建上述测试的测试:
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Shader;
import android.os.Bundle;
import android.os.Handler;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.SurfaceHolder.Callback;
import android.widget.LinearLayout;
public class GradientTest extends Activity {
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
getWindow().setFormat(PixelFormat.RGBA_8888);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(getWindow().getAttributes());
lp.format = PixelFormat.RGBA_8888;
getWindow().setAttributes(lp);
LinearLayout ll = new LinearLayout(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500,500);
params.setMargins(20, 0, 0, 0);
ll.addView(new GradientView(this), params);
ll.addView(new GradientSurfaceView(this), params);
ll.setOrientation(LinearLayout.VERTICAL);
setContentView(ll);
}
public class GradientView extends View {
public GradientView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(false);
paint.setFilterBitmap(false);
paint.setDither(false);
Shader shader = new LinearGradient(
0,
0,
0,
500,
//new int[]{0xffafafaf, 0xff414141},
new int[]{0xff333333, 0xff555555},
null,
Shader.TileMode.CLAMP
);
paint.setShader(shader);
canvas.drawRect(0,0,500,500, paint);
}
}
public class GradientSurfaceView extends SurfaceView implements Callback {
public GradientSurfaceView(Context context) {
super(context);
getHolder().setFormat(PixelFormat.RGBA_8888); // Ensure no banding on gradients
SurfaceHolder holder = getHolder();
holder.addCallback(this);
}
Paint paint;
private GraphThread thread;
@Override
public void surfaceCreated(SurfaceHolder holder) {
holder.setFormat(PixelFormat.RGBA_8888); // Ensure no banding on gradients
paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(false);
paint.setFilterBitmap(false);
paint.setDither(false);
Shader shader = new LinearGradient(
0,
0,
0,
500,
//new int[]{0xffafafaf, 0xff414141},
new int[]{0xff333333, 0xff555555},
null,
Shader.TileMode.CLAMP
);
paint.setShader(shader);
thread = new GraphThread(holder, new Handler() );
thread.setName("GradientSurfaceView_thread");
thread.start();
}
class GraphThread extends Thread {
/** Handle to the surface manager object we interact with */
private SurfaceHolder mSurfaceHolder;
public GraphThread(SurfaceHolder holder, Handler handler) {
mSurfaceHolder = holder;
holder.setFormat(PixelFormat.RGBA_8888); // Ensure no banding on gradients
}
@Override
public void run() {
Canvas c = null;
while (true) {
try {
c = mSurfaceHolder.lockCanvas();
synchronized (mSurfaceHolder) {
if (c != null){
c.drawRect(0,0,500,500, paint);
}
}
}
finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
}
我从 Google Play 安装了一个名为 Display Tester 的应用程序。此应用程序可用于在屏幕上创建测试梯度。虽然它的渐变看起来并不完美,但它们似乎比我能够实现的要好一些,这让我想知道是否可以采取进一步措施来防止条带化。
我注意到的另一件事是 Display Tester 应用程序报告我的 Nexus 屏幕是 32 位的。
有关信息,我明确启用硬件加速。我的 SDK 级别是:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15"></uses-sdk>
我注意到的另一件事是,Activity
我理解为 Holo 的一个功能的默认渐变背景也是非常带状的。这也根本没有显示在屏幕截图中。而且,我还注意到我的 Nexus 7 上的背景条带短暂移动,这与我的两个Views
. 如果我使用默认的 'empty' 创建一个全新的 Android 项目,Activity
我Activity
的 Nexus 和 HTC One X 上都会显示令人讨厌的带状渐变背景。这正常吗?Activity
我知道如果启用硬件加速,这个黑色/紫色渐变默认背景是应该有的。好吧,不管是否启用了硬件加速,我都看到了同样令人讨厌的带状Activity
背景渐变。这甚至发生在我的空测试项目中,其目标 SDK 为 15。为了澄清,我启用或禁用硬件加速的方式是显式使用android:hardwareAccelerated="true"
and android:hardwareAccelerated="false"
。
我不确定我对 Holo 黑色/紫色Activity
渐变背景的观察是否与我的主要问题有关,但它似乎确实相关。奇怪的是它看起来质量如此差(即带状)并且无论是否打开硬件加速看起来都一样。所以,第二个问题是:当你有一个Activity
默认的 Holo 渐变背景,并且对于硬件加速被明确启用然后禁用的每种情况,这个渐变背景 (a) 是否存在并且 (b) 看起来非常平滑?我会在一个单独的问题中问这个问题,但同样,它似乎是相关的。
所以,总而言之:SurfaceView
我遇到的基本问题是,在我的 Nexus 7 上似乎根本无法将渐变背景应用于 a 。这不仅仅是条带问题(如果它只是那); 实际上,每次抽签时,条带本质上都是随机的。这意味着SurfaceView
不断重绘的 a 最终会有一个移动的、模糊的背景。