1

I'm trying to load an image into a layer-list programatically. I found some solutions but somehow it does not work. Here is what I did:

layer_list.xml: (Set as background of RelativeLayout)

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/red" />
    <item
        android:id="@+id/replace"
        android:drawable="@drawable/placeholder" />
    <item
        android:bottom="-250dp"
        android:left="0dp"
        android:right="-250dp"
        android:top="250dp">
        <rotate
            android:fromDegrees="-10"
            android:pivotX="0%"
            android:pivotY="100%">
            <shape android:shape="rectangle">
                <solid android:color="@color/white" />
            </shape>
        </rotate>
    </item>
</layer-list>

Code to load bitmap into layer-list:

Drawable drawable = new BitmapDrawable(getResources(), bitmap);
LayerDrawable layerDrawable = (LayerDrawable) ResourcesCompat.getDrawable(getResources(), R.drawable.layer_list, null);
layerDrawable.setDrawableByLayerId(R.id.replace, drawable);

But somehow the placeholder image is never replaced with the new bitmap. What am I doing wrong here?

EDIT: This is how I load the image:

    private void loadImageIntoLayerList(Context context, String url) {
    // To get image using Fresco
    ImageRequest imageRequest = ImageRequestBuilder
            .newBuilderWithSource(Uri.parse(url))
            .setProgressiveRenderingEnabled(true)
            .build();

    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline
            .fetchDecodedImage(imageRequest, context);

    BaseBitmapDataSubscriber dataSubscriber = new BaseBitmapDataSubscriber() {
        @Override
        public void onNewResultImpl(@Nullable Bitmap bitmap) {
            // You can use the bitmap in only limited ways
            // No need to do any cleanup.
            if (bitmap != null) {
                Drawable drawable = new BitmapDrawable(getResources(), bmp);
                LayerDrawable layerDrawable = (LayerDrawable) ResourcesCompat.getDrawable
                        (getResources(), R.drawable.layer_list, null);
                if (layerDrawable != null) {
                    layerDrawable.setDrawableByLayerId(R.id.cover_image, drawable);
                    header.setBackground(layerDrawable);
                }
            }
        }

        @Override
        public void onFailureImpl(DataSource dataSource) {
            // No cleanup required here.
        }
    };
    dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance());
anstaendig
  • 795
  • 5
  • 13

2 Answers2

2

Add the following line at the end:

myRelativeLayout.setBackground(layerDrawable);

Looks like you need to set the background once again after changing the drawable.

Here is the working code:

public class TestActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_test);
    final RelativeLayout RL = (RelativeLayout) findViewById(R.id.RL);
    Button btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.image7, null);
            LayerDrawable layerDrawable = (LayerDrawable) ResourcesCompat.getDrawable(getResources(), R.drawable.layer_list, null);
            layerDrawable.setDrawableByLayerId(R.id.replace, drawable);
            RL.setBackground(layerDrawable);

        }
    });
}
}

layer_list.xml is the same as yours, only with different drawables. activity_test.xml is simply a RelativeLayout with a button in middle.

Onur Çevik
  • 1,560
  • 13
  • 21
  • How did you name your drawable "layer-list"? You are not allowed to use "-" in names. Maybe that is your problem? Because I'm using the same code as yours, with a drawable named "layer_list" and mine is working right now. How do you call this image replacement code? – Onur Çevik Jul 02 '16 at 12:14
  • It's a name with underscores, no dashes. And I'm using the Fresco image pipeline to get a bitmap out of an image url and in the callback i do the image replacement. – anstaendig Jul 02 '16 at 12:17
  • So first try something simpler: add a plain RelativeLayout on your screen and simply change its background with the bitmap you've recieved. If that's working, then your bitmap is fine. I'll edit my answer with the working code I have, so you can modify it to solve the issue. – Onur Çevik Jul 02 '16 at 12:19
  • I added the code for the image loading and replacement – anstaendig Jul 02 '16 at 12:22
  • Never used Fresco, so can't help any further. Just note that my working code started working once I re-set the background. So try working around that. Good luck. – Onur Çevik Jul 02 '16 at 12:27
1

You cannot use the Bitmap you get in BaseBitmapDataSubscriber.onNewResultImpl(Bitmap) for this. If you take a look at the Javadoc for this method, you see that the Bitmap's memory will be freed shortly after.

It would be better if you would use SimpleDraweeView directly in your layout. It supports overlays, backgrounds and a placeholder, so it handles everything for you. You can split up your layer list in separate drawables, e.g. a background and an overlay and set it in XML or in code. Then you can just do draweeView.setImageURI(...) to load your image.

You can also implement a custom view for this to get the drawable (and set it as a background for example since you can get the Drawable itself with Drawable drawable = mDraweeHolder.getTopLevelDrawable(); according to the custom view guide).

If you really have to use the imagepipeline directly, you also have to handle the lifecycle of the Bitmap correctly. You get a CloseableReference for the bitmap and you have to manually release it. Take a look at http://frescolib.org/docs/using-image-pipeline.html for more information. You can also take a look at classes like PipelineDraweeController and AbstractDraweeController to see how this could be implemented (that's what happens when you use SimpleDraweeView).

Alexander Oprisnik
  • 1,212
  • 9
  • 9