27

我知道色带是一个老问题,之前已经讨论过很多次,提供了各种解决方案(基本上归结为始终使用 32 位,或使用抖动)。事实上,不久前我问过并随后回答了我自己关于这个的 SO 问题。那时,我认为我在该问题的答案中提出的解决方案(适用setFormat(PixelFormat.RGBA_8888)于a 的情况下Window也适用于Holdera SurfaceView)已经解决了我的应用程序中的问题。至少该解决方案使渐变在我当时开发的设备(很可能是 Android 2.2)上看起来非常漂亮。

我现在正在使用 HTC One X (Android 4.0) 和 Asus Nexus 7 (Android 4.1) 进行开发。我试图做的是对 a 的整个区域应用灰色渐变SurfaceView。即使我应该确保包含WindowHolder配置为 32 位颜色,但我得到了可怕的条带伪影。事实上,在 Nexus 7 上,我什至可以看到工件在移动。这不仅发生在SurfaceView当然连续绘制的情况下,而且发生在View我为测试目的而添加的法线中,以绘制完全相同的渐变,这本来会绘制一次。这些伪影存在并且似乎四处移动的方式当然看起来非常糟糕,实际上就像观看信号不佳的模拟电视一样。这俩ViewSurfaceView展示完全相同的文物,它们一起移动。

我的意图是始终使用 32 位,而不是使用抖动。我的印象是Window早在 Android 4.0 之前默认是 32 位的。通过应用RGBA_8888SurfaceView我本来希望一切都是 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 项目,ActivityActivity的 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 最终会有一个移动的、模糊的背景。

4

4 回答 4

4

只是用一个答案来结束这个问题,我得出的结论是 Nexus 7 只是有一些硬件/固件问题,这意味着它在渲染渐变方面完全不适应。

于 2013-05-10T14:53:46.730 回答
2

您是否尝试过为表面视图本身设置像素格式?

    final SurfaceHolder holder = getHolder();
    holder.setFormat(PixelFormat.RGBA_8888);
于 2015-05-12T09:47:29.440 回答
1

如果您发现在 ICS 及更高版本中设置像素格式没有任何效果,则很可能是由于硬件加速将始终呈现为原生像素格式。大多数时候应该只是 ARGB_8888。确保您还设置了活动的窗口像素格式,而不仅仅是 SurfaceView 上的像素格式。

您可以通过关闭加速来轻松验证是否是这种情况。您提到您对此进行了测试,但没有提及您是如何做到的。设置目标 sdk 级别并不是最明确的方法。

在我的脑海中,软件渲染在 HoneyComb (3.0) 中默认切换为 ARGB_8888,但您需要再次明确设置它以正确支持旧设备,而这不是默认设置。

于 2012-08-17T07:07:45.083 回答
1

你为什么要设置假油漆上的抖动。我建议激活抖动

paint.setDither(true);

Android doc 明确表示它将降低您的渲染:

设置或清除 DITHER_FLAG 位抖动会影响比设备精度更高的颜色如何被下采样。没有抖动通常更快,但精度更高的颜色会被截断(例如 8888 -> 565)。抖动尝试分配此过程中固有的错误,以减少视觉伪影。

您也可以尝试将 FLAG_DITHER 添加到窗口中:

window.setFormat(PixelFormat.RGBA_8888);
window.setFlags(WindowManager.LayoutParams.FLAG_DITHER, WindowManager.LayoutParams.FLAG_DITHER);
于 2012-10-22T17:49:14.830 回答