From 7b5b6abf852c039983eded25ebe43a70fef5a4ab Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Mon, 14 Mar 2011 18:05:08 -0700 Subject: [PATCH] Fix rendering artifact in edge fades. Bug #4092053 The problem always existed but was made visible by partial invalidation. When saving a layer, the renderer would try to postpone glClear() operations until the next drawing command. This however does not work since the clip might have changed. The fix is rather simple and simply gets rid of this "optimization" (that turned out to be usless anyway given how View issues saveLayer() calls.) This change also fixes an issue with gradients (color stops where not properly computed when using a null stops array) and optimizes display lists rendering (quickly rejects larger portions of the tree to avoid executing unnecessary code.) Change-Id: I0f5b5f6e1220d41a09cc2fa84c212b0b4afd9c46 --- core/java/android/view/GLES20Canvas.java | 7 +-- core/java/android/view/HardwareCanvas.java | 4 +- core/java/android/view/HardwareRenderer.java | 3 +- core/java/android/view/View.java | 16 +++--- core/java/android/view/ViewGroup.java | 2 +- core/java/android/widget/AbsListView.java | 1 + core/jni/android/graphics/Shader.cpp | 20 ++++++-- core/jni/android_view_GLES20Canvas.cpp | 7 +-- libs/hwui/DisplayListRenderer.cpp | 13 +++-- libs/hwui/DisplayListRenderer.h | 12 ++++- libs/hwui/OpenGLRenderer.cpp | 49 ++++++------------- libs/hwui/OpenGLRenderer.h | 12 +---- libs/hwui/ProgramCache.h | 2 + .../res/drawable/gradient.xml | 22 +++++++++ .../android/test/hwui/GradientsActivity.java | 17 +++++++ .../test/hwui/TransparentListActivity.java | 2 +- 16 files changed, 120 insertions(+), 69 deletions(-) create mode 100644 tests/HwAccelerationTest/res/drawable/gradient.xml diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index fa5479bf1a82d..14f2e9dcc406c 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -245,12 +245,13 @@ class GLES20Canvas extends HardwareCanvas { private static native void nDestroyDisplayList(int displayList); @Override - public boolean drawDisplayList(DisplayList displayList, Rect dirty) { + public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) { return nDrawDisplayList(mRenderer, - ((GLES20DisplayList) displayList).mNativeDisplayList, dirty); + ((GLES20DisplayList) displayList).mNativeDisplayList, width, height, dirty); } - private static native boolean nDrawDisplayList(int renderer, int displayList, Rect dirty); + private static native boolean nDrawDisplayList(int renderer, int displayList, + int width, int height, Rect dirty); /////////////////////////////////////////////////////////////////////////// // Hardware layer diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index cb1003ab077c2..caa7b743cd7e5 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -53,13 +53,15 @@ public abstract class HardwareCanvas extends Canvas { * Draws the specified display list onto this canvas. * * @param displayList The display list to replay. + * @param width The width of the display list. + * @param height The height of the display list. * @param dirty The dirty region to redraw in the next pass, matters only * if this method returns true, can be null. * * @return True if the content of the display list requires another * drawing pass (invalidate()), false otherwise */ - abstract boolean drawDisplayList(DisplayList displayList, Rect dirty); + abstract boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty); /** * Draws the specified layer onto this canvas. diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 0cf7ae60efa31..8584bf22126f9 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -608,7 +608,8 @@ public abstract class HardwareRenderer { DisplayList displayList = view.getDisplayList(); if (displayList != null) { - if (canvas.drawDisplayList(displayList, mRedrawClip)) { + if (canvas.drawDisplayList(displayList, view.getWidth(), + view.getHeight(), mRedrawClip)) { if (mRedrawClip.isEmpty() || view.getParent() == null) { view.invalidate(); } else { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 32c9e27048d52..5a96efd9baa47 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -59,7 +59,6 @@ import android.util.Pools; import android.util.SparseArray; import android.util.TypedValue; import android.view.ContextMenu.ContextMenuInfo; -import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; @@ -4651,6 +4650,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @return True if the event was handled by the view, false otherwise. */ public boolean dispatchGenericMotionEvent(MotionEvent event) { + //noinspection SimplifiableIfStatement if (mOnGenericMotionListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnGenericMotionListener.onGenericMotion(this, event)) { return true; @@ -9326,7 +9326,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } final ScrollabilityCache scrollabilityCache = mScrollCache; - int length = scrollabilityCache.fadingEdgeLength; + final float fadeHeight = scrollabilityCache.fadingEdgeLength; + int length = (int) fadeHeight; // clip the fade length if top and bottom fades overlap // overlapping fades produce odd-looking artifacts @@ -9341,16 +9342,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (verticalEdges) { topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength())); - drawTop = topFadeStrength > 0.0f; + drawTop = topFadeStrength * fadeHeight > 1.0f; bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength())); - drawBottom = bottomFadeStrength > 0.0f; + drawBottom = bottomFadeStrength * fadeHeight > 1.0f; } if (horizontalEdges) { leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength())); - drawLeft = leftFadeStrength > 0.0f; + drawLeft = leftFadeStrength * fadeHeight > 1.0f; rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength())); - drawRight = rightFadeStrength > 0.0f; + drawRight = rightFadeStrength * fadeHeight > 1.0f; } saveCount = canvas.getSaveCount(); @@ -9388,7 +9389,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final Paint p = scrollabilityCache.paint; final Matrix matrix = scrollabilityCache.matrix; final Shader fade = scrollabilityCache.shader; - final float fadeHeight = scrollabilityCache.fadingEdgeLength; if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); @@ -9438,6 +9438,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return The known solid color background for this view, or 0 if the color may vary */ + @ViewDebug.ExportedProperty(category = "drawing") public int getSolidColor() { return 0; } @@ -11644,6 +11645,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @return true if scrolling was clamped to an over-scroll boundary along either * axis, false otherwise. */ + @SuppressWarnings({"UnusedParameters"}) protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f9692dad0a30c..8dc86ac63dc67 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2585,7 +2585,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } else { child.mPrivateFlags &= ~DIRTY_MASK; - ((HardwareCanvas) canvas).drawDisplayList(displayList, null); + ((HardwareCanvas) canvas).drawDisplayList(displayList, cr - cl, cb - ct, null); } } } else if (cache != null) { diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index eca39fc95cba6..d39271edd9a6a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -5226,6 +5226,7 @@ public abstract class AbsListView extends AdapterView implements Te * * @return The cache color hint */ + @ViewDebug.ExportedProperty(category = "drawing") public int getCacheColorHint() { return mCacheColorHint; } diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 0ea8225c91f67..7fdad10c07445 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -119,7 +119,7 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject o, const jint* colorValues = env->GetIntArrayElements(colorArray, NULL); SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0); - SkScalar* pos = NULL; + SkScalar* pos = NULL; if (posArray) { AutoJavaFloatArray autoPos(env, posArray, count); @@ -164,7 +164,11 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* } } else { storedPositions[0] = 0.0f; - storedPositions[1] = 1.0f; + const jfloat step = 1.0f / (count - 1); + for (size_t i = 1; i < count - 1; i++) { + storedPositions[i] = step * i; + } + storedPositions[count - 1] = 1.0f; } SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors, @@ -289,7 +293,11 @@ static SkiaShader* RadialGradient_postCreate1(JNIEnv* env, jobject o, SkShader* } } else { storedPositions[0] = 0.0f; - storedPositions[1] = 1.0f; + const jfloat step = 1.0f / (count - 1); + for (size_t i = 1; i < count - 1; i++) { + storedPositions[i] = step * i; + } + storedPositions[count - 1] = 1.0f; } SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors, @@ -384,7 +392,11 @@ static SkiaShader* SweepGradient_postCreate1(JNIEnv* env, jobject o, SkShader* s } } else { storedPositions[0] = 0.0f; - storedPositions[1] = 1.0f; + const jfloat step = 1.0f / (count - 1); + for (size_t i = 1; i < count - 1; i++) { + storedPositions[i] = step * i; + } + storedPositions[count - 1] = 1.0f; } SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, count, diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index a78f66029add2..5116f09c9d911 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -498,9 +498,10 @@ static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, } static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, - jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList, jobject dirty) { + jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList, + jint width, jint height, jobject dirty) { android::uirenderer::Rect bounds; - bool redraw = renderer->drawDisplayList(displayList, bounds); + bool redraw = renderer->drawDisplayList(displayList, width, height, bounds); if (redraw && dirty != NULL) { env->CallVoidMethod(dirty, gRectClassInfo.set, int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom)); @@ -664,7 +665,7 @@ static JNINativeMethod gMethods[] = { { "nGetDisplayList", "(I)I", (void*) android_view_GLES20Canvas_getDisplayList }, { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, { "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer }, - { "nDrawDisplayList", "(IILandroid/graphics/Rect;)Z", + { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_drawDisplayList }, { "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt }, diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 737fa025e2508..868290bf0f8b1 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -285,9 +285,12 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) break; case DrawDisplayList: { DisplayList* displayList = getDisplayList(); - DISPLAY_LIST_LOGD("%s%s %p, %d", (char*) indent, OP_NAMES[op], - displayList, level + 1); - needsInvalidate |= renderer.drawDisplayList(displayList, dirty, level + 1); + uint32_t width = getUInt(); + uint32_t height = getUInt(); + DISPLAY_LIST_LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op], + displayList, width, height, level + 1); + needsInvalidate |= renderer.drawDisplayList(displayList, width, height, + dirty, level + 1); } break; case DrawLayer: { @@ -674,11 +677,13 @@ bool DisplayListRenderer::clipRect(float left, float top, float right, float bot return OpenGLRenderer::clipRect(left, top, right, bottom, op); } -bool DisplayListRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level) { +bool DisplayListRenderer::drawDisplayList(DisplayList* displayList, + uint32_t width, uint32_t height, Rect& dirty, uint32_t level) { // dirty is an out parameter and should not be recorded, // it matters only when replaying the display list addOp(DisplayList::DrawDisplayList); addDisplayList(displayList); + addSize(width, height); return false; } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index f24545d32926a..6fc315c0c1d60 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -144,6 +144,10 @@ private: return mReader.readInt(); } + inline uint32_t getUInt() { + return mReader.readU32(); + } + SkMatrix* getMatrix() { return (SkMatrix*) getInt(); } @@ -238,7 +242,8 @@ public: bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - bool drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level = 0); + bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + Rect& dirty, uint32_t level = 0); void drawLayer(Layer* layer, float x, float y, SkPaint* paint); void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); @@ -323,6 +328,11 @@ private: mWriter.writeInt(value); } + inline void addSize(uint32_t w, uint32_t h) { + mWriter.writeInt(w); + mWriter.writeInt(h); + } + void addInts(const int32_t* values, uint32_t count) { mWriter.writeInt(count); for (uint32_t i = 0; i < count; i++) { diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 1f652010e627b..5d9522e29fa0e 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -403,10 +403,7 @@ bool OpenGLRenderer::createLayer(sp snapshot, float left, float top, // Window coordinates of the layer Rect bounds(left, top, right, bottom); - if (fboLayer) { - // Clear the previous layer regions before we change the viewport - clearLayerRegions(); - } else { + if (!fboLayer) { mSnapshot->transform->mapRect(bounds); // Layers only make sense if they are in the framebuffer's bounds @@ -464,8 +461,14 @@ bool OpenGLRenderer::createLayer(sp snapshot, float left, float top, glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); } - // Enqueue the buffer coordinates to clear the corresponding region later - mLayers.push(new Rect(bounds)); + + // Clear the framebuffer where the layer will draw + glScissor(bounds.left, mSnapshot->height - bounds.bottom, + bounds.getWidth(), bounds.getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + dirtyClip(); } } @@ -760,31 +763,6 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { #endif } -void OpenGLRenderer::clearLayerRegions() { - if (mLayers.size() == 0 || mSnapshot->isIgnored()) return; - - Rect clipRect(*mSnapshot->clipRect); - clipRect.snapToPixelBoundaries(); - - for (uint32_t i = 0; i < mLayers.size(); i++) { - Rect* bounds = mLayers.itemAt(i); - if (clipRect.intersects(*bounds)) { - // Clear the framebuffer where the layer will draw - glScissor(bounds->left, mSnapshot->height - bounds->bottom, - bounds->getWidth(), bounds->getHeight()); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - // Restore the clip - dirtyClip(); - } - - delete bounds; - } - - mLayers.clear(); -} - /////////////////////////////////////////////////////////////////////////////// // Transforms /////////////////////////////////////////////////////////////////////////////// @@ -870,7 +848,6 @@ bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setupDraw() { - clearLayerRegions(); if (mDirtyClip) { setScissorFromClip(); } @@ -1064,12 +1041,18 @@ void OpenGLRenderer::finishDrawTexture() { // Drawing /////////////////////////////////////////////////////////////////////////////// -bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level) { +bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + Rect& dirty, uint32_t level) { + if (quickReject(0.0f, 0.0f, width, height)) { + return false; + } + // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself if (displayList) { return displayList->replay(*this, dirty, level); } + return false; } diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 9d8638867342d..73624733df0a1 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -96,7 +96,8 @@ public: bool quickReject(float left, float top, float right, float bottom); virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level = 0); + virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + Rect& dirty, uint32_t level = 0); virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); @@ -246,12 +247,6 @@ private: */ void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false); - /** - * Clears all the regions corresponding to the current list of layers. - * This method MUST be invoked before any drawing operation. - */ - void clearLayerRegions(); - /** * Mark the layer as dirty at the specified coordinates. The coordinates * are transformed with the supplied matrix. @@ -499,9 +494,6 @@ private: // Various caches Caches& mCaches; - // List of rectangles to clear due to calls to saveLayer() - Vector mLayers; - // Indentity matrix const mat4 mIdentity; diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 3acd18a9e097d..ead5b920c1dc4 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -231,9 +231,11 @@ struct ProgramDescription { * Logs the specified message followed by the key identifying this program. */ void log(const char* message) const { +#if DEBUG_PROGRAMS programid k = key(); PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), uint32_t(k & 0xffffffff)); +#endif } private: diff --git a/tests/HwAccelerationTest/res/drawable/gradient.xml b/tests/HwAccelerationTest/res/drawable/gradient.xml new file mode 100644 index 0000000000000..756db0b23dbfd --- /dev/null +++ b/tests/HwAccelerationTest/res/drawable/gradient.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java index f8422f4a5ff64..90db818a75c49 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java @@ -193,6 +193,7 @@ public class GradientsActivity extends Activity { private final float mDrawWidth; private final float mDrawHeight; private final LinearGradient mGradient; + private final LinearGradient mGradientStops; private final Matrix mMatrix; ShadersView(Context c) { @@ -202,6 +203,9 @@ public class GradientsActivity extends Activity { mDrawHeight = 200; mGradient = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP); + mGradientStops = new LinearGradient(0, 0, 0, 1, + new int[] { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF }, null, Shader.TileMode.CLAMP); + mMatrix = new Matrix(); mPaint = new Paint(); @@ -255,6 +259,19 @@ public class GradientsActivity extends Activity { mGradient.setLocalMatrix(mMatrix); canvas.drawRect(left, top, left + mDrawWidth, bottom, mPaint); + right = left + mDrawWidth; + left = 40.0f; + top = bottom + 20.0f; + bottom = top + 50.0f; + + mPaint.setShader(mGradientStops); + + mMatrix.setScale(1, mDrawWidth); + mMatrix.postRotate(90); + mMatrix.postTranslate(right, top); + mGradientStops.setLocalMatrix(mMatrix); + canvas.drawRect(left, top, right, bottom, mPaint); + canvas.restore(); } } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java index 763169a56dac9..535f86514fa8d 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java @@ -79,7 +79,7 @@ public class TransparentListActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getWindow().setBackgroundDrawable(getResources().getDrawable(R.drawable.default_wallpaper)); + getWindow().setBackgroundDrawable(getResources().getDrawable(R.drawable.gradient)); setContentView(R.layout.list_activity); ListAdapter adapter = new SimpleListAdapter(this);