Я знаю, что цветовое полотно - это старый каштан проблемы, который обсуждался много раз, когда предлагались различные решения (которые, по существу, сводятся к использованию 32-битной версии или используют сглаживание). На самом деле не так давно я спросил и впоследствии ответил на мой собственный вопрос SO относительно этого. В то время я думал, что решение, которое я поставил в ответ на этот вопрос (а именно, применить setFormat(PixelFormat.RGBA_8888)
к Window
, а также к Holder
в случае SurfaceView
), решило проблему в мое выражение навсегда. По крайней мере, решение сделало градиент очень приятным на устройствах, которые я разрабатывал тогда (скорее всего, Android 2.2).
Сейчас я развиваюсь с помощью HTC One X (Android 4.0) и Asus Nexus 7 (Android 4.1). Я попытался применить серый градиент ко всей области SurfaceView
. Хотя я предположительно гарантировал, что содержащие Window
и Holder
настроены для 32-битного цвета, я получаю ужасные артефакты. На самом деле, на Nexus 7, я даже вижу, что артефакты перемещаются вокруг. Это происходит не только на SurfaceView
, который, конечно, непрерывно рисуется, но и в нормальном View
, который я добавил рядом, чтобы нарисовать ровно тот же градиент для целей тестирования, который был бы нарисован один раз. Способ, которым эти артефакты существуют, а также, кажется, движется вокруг, выглядит абсолютно ужасно, и на самом деле это похоже на просмотр аналогового телевизора с плохим сигналом. Оба View
и SurfaceView
демонстрируют те же артефакты, которые перемещаются вместе.
Мое намерение состоит в том, чтобы использовать 32-битную версию и не использовать сглаживание. У меня создается впечатление, что Window
был 32-битным по умолчанию задолго до Android 4.0. Применяя 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) {
}
}
}
Я установил приложение под названием Display Tester из Google Play. Это приложение можно использовать для создания тестовых градиентов на экране. Хотя его градиенты не выглядят идеально, они кажутся немного лучше, чем я мог достичь, что заставляет меня задаться вопросом, есть ли еще одна мера, которую я могу сделать, чтобы предотвратить бандажирование.
Другое замечание: приложение Display Tester сообщает, что экран Nexus 32-бит.
Для получения информации я могу явно включить аппаратное ускорение. Уровни SDK:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15"></uses-sdk>
Еще одна вещь, которую я замечаю, заключается в том, что фон градиента по умолчанию для Activity
, который, как я понимаю, является особенностью Holo, также очень ограничен. Это также совсем не отображается на скриншоте. И я также заметил, что на моем Nexus 7 коротко перемещается обтекание фона, сочувствуя движению бандажа в моих двух Views
. Если я создаю совершенно новый Android-проект со стандартным "пустым" Activity
, Activity
показывает неприятный ленточный фон с градиентом как на моем Nexus, так и на HTC One X. Это нормально? Я понимаю, что этот черный/фиолетовый градиент по умолчанию - это то, что имеет значение Activity
, если аппаратное ускорение включено. Ну, независимо от того, включено ли аппаратное ускорение или нет, я вижу тот же неприятный полосатый Activity
фоновый градиент. Это даже происходит в моем пустом тестовом проекте, целевой SDK которого составляет 15. Чтобы уточнить, способ включения или отключения аппаратного ускорения явно использует android:hardwareAccelerated="true"
и android:hardwareAccelerated="false"
.
Я не уверен, что мое наблюдение о фоне градиента Холо/фиолетового Activity
имеет какое-то отношение к моему основному вопросу, но это кажется странным. Также странно, что он выглядит таким низким качеством (т.е. С полосой) и выглядит одинаково независимо от того, включено ли ускорение аппаратного обеспечения. Таким образом, вторичный вопрос: Когда у вас есть Activity
с фоном градиента по умолчанию для Holo, и для каждого случая ускорения аппаратного обеспечения, который является включенным и затем отключен, должен ли этот фон градиента (a) присутствовать и (b) смотреть отлично гладкая? Я бы спросил об этом в отдельном вопросе, но опять же, похоже, это связано.
Итак, в резюме: Основная проблема, с которой я сталкиваюсь, заключается в том, что применение фона градиента к SurfaceView
просто не может быть сделано, по-моему, на моем Nexus 7. Это не просто объединение, что проблема (с которой я мог бы с радостью согласиться, если бы это было именно так); это на самом деле тот факт, что полоса является случайной по своей природе на каждой ничьей. Это означает, что a SurfaceView
, который постоянно перерисовывается, заканчивается наличием движущегося нечеткого фона.