The Amateur Coder
The Amateur Coder

Reputation: 839

How to programmatically show/hide only the background/image of an ImageView in Android?

I have an ImageView, and its alpha is set to 0 in the XML. The XML looks like this:

<LinearLayout

            android:layout_width="match_parent"
            android:layout_height="<height>"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/<id>"
                android:layout_width="<width>"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:alpha="0"
                android:background="#ff00ff"
                android:clickable="true"
                android:focusable="true"
                app:srcCompat="@drawable/<drawable>" />

            <ImageView
                android:id="@+id/<id>"
                android:layout_width="<width>"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:alpha="0"
                android:background="#0000ff"
                android:clickable="true"
                android:focusable="true"
                app:srcCompat="@drawable/<drawable>" />

</LinearLayout>

I tried searching how to do this, but most questions are about how to hide the background or the entire ImageView, and not just the background. Also, the alpha has to be set to "0". Is there any way to hide only the image so that only the background (solid color) is visible? Only the background colors of both ImageViews have to be displayed, so the container (LinearLayout)'s background cannot be used to do this for both (the background colors are different).

Changing the image's source to an image which is a solid color, the same as the background color, would work, but I need to change this color programmatically, so it might be inefficient. I've also tried different combinations of ImageView.- setAlpha(float), setImageAlpha(int), setImageResource(int) (to 0) and setImageDrawable(Drawable) (to show the image again, with the background [color]), but they produce weird results. Using setImageResource(0) (with alpha 1f) hides the image, showing only the solid background, but I'm not able to display the image again. Is there any easy method I'm unaware of?

I need to do this programmatically, so methods like in Java android Linearlayout set two colors wouldn't work. What is the best way to do this programmatically?

Upvotes: 0

Views: 343

Answers (2)

Dev4Life
Dev4Life

Reputation: 3317

Well, it's not directly possible to work it as expected with two views, and as you said, you have multiple views like that & that's why it's not memory efficient.

But We can achieve that functionality using CustomView designed according to our needs.

If you know a little bit of drawing on the canvas and how it works, then it'll be easier.

Here, I made the custom view to achieve exactly what you want.

public class ImageAlphaBGView extends View {
    private Bitmap bitmap;
    Paint mPaint;
    Paint mPaintBG;
    Rect bgRect;

    private int mWidth;
    private int mHeight;
    float centreX;
    float centreY;

    public ImageAlphaBGView(Context context) {
        super(context);
        init();
    }

    public ImageAlphaBGView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ImageAlphaBGView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public ImageAlphaBGView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);

        mPaintBG = new Paint();
        mPaintBG.setColor(Color.parseColor("#000000"));
        mPaintBG.setStyle(Paint.Style.FILL);
        mPaintBG.setAntiAlias(true);
    }

    public void setImageBitmap(Bitmap bitmap) {
        this.bitmap = bitmap;
        try {
            centreX = (mWidth - bitmap.getWidth()) / 2;
            centreY = (mHeight - bitmap.getHeight()) / 2;
        } catch (Exception e) {
            e.printStackTrace();
        }
        invalidate();
    }

    public void setBGColor(int color) {
        mPaintBG.setColor(color);
        invalidate();
    }

    public void setImageAlpha(@IntRange(from = 0, to = 255) int alpha) {
        mPaint.setAlpha(alpha);
        invalidate();
    }

    public void setBGAlpha(@IntRange(from = 0, to = 255) int alpha) {
        mPaintBG.setAlpha(alpha);
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(bgRect, mPaintBG);
        if (bitmap != null)
            canvas.drawBitmap(bitmap, centreX, centreY, mPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;

        bgRect = new Rect(0, 0, mWidth, mHeight);
        invalidate();
    }
}

Simply put this view in your xml & then you can programmatically pass a bitmap and alpha of the background.

It supports setting up image alpha, background alpha seperately as well as background color.

Here is what you need to do :

In the xml,

<com.demo.example.views.ImageAlphaBGView
            android:id="@+id/alphaView"
            android:layout_width="150dp"
            android:layout_height="150dp"/>

In the activity or fragment,

ImageAlphaBGView imageView = findViewById(R.id.alphaView)
imageView.setImageBitmap(bitmap)
imageView.setBGColor(Color.RED)
imageView.setImageAlpha(255)

And you've got it. :)

Upvotes: 1

ΓDΛ
ΓDΛ

Reputation: 11110

Imageview can show a bitmap as you know. You can set the bitmap by deleting the background of the relevant bitmap.

Main Object

object BackgroundRemover {

    private val segment: Segmenter
    private var buffer = ByteBuffer.allocate(0)
    private var width = 0
    private var height = 0


    init {
        val segmentOptions = SelfieSegmenterOptions.Builder()
            .setDetectorMode(SelfieSegmenterOptions.SINGLE_IMAGE_MODE)
            .build()
        segment = Segmentation.getClient(segmentOptions)
    }


    /**
     * Process the image to get buffer and image height and width
     * @param bitmap Bitmap which you want to remove background.
     * @param trimEmptyPart After removing the background if its true it will remove the empty part of bitmap. by default its false.
     * @param listener listener for success and failure callback.
     **/
    fun bitmapForProcessing(
        bitmap: Bitmap,
        trimEmptyPart: Boolean? = false,
        listener: OnBackgroundChangeListener
    ) {
        //Generate a copy of bitmap just in case the if the bitmap is immutable.
        val copyBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
        val input = InputImage.fromBitmap(copyBitmap, 0)
        segment.process(input)
            .addOnSuccessListener { segmentationMask ->
                buffer = segmentationMask.buffer
                width = segmentationMask.width
                height = segmentationMask.height

                CoroutineScope(Dispatchers.IO).launch {
                    val time = measureTimeMillis {
                        val resultBitmap = if (trimEmptyPart == true) {
                            val bgRemovedBitmap = removeBackgroundFromImage(copyBitmap)
                            trim(bgRemovedBitmap)
                        } else {
                            removeBackgroundFromImage(copyBitmap)
                        }
                        withContext(Dispatchers.Main) {
                            listener.onSuccess(resultBitmap)
                        }
                    }
                    Log.e("TAG", "bitmapForProcessingTime: $time")
                }

            }
            .addOnFailureListener { e ->
                println("Image processing failed: $e")
                listener.onFailed(e)

            }
    }


    /**
     * Change the background pixels color to transparent.
     * */
    private suspend fun removeBackgroundFromImage(
        image: Bitmap
    ): Bitmap {
        val bitmap = CoroutineScope(Dispatchers.IO).async {
            for (y in 0 until height) {
                for (x in 0 until width) {
                    val bgConfidence = ((1.0 - buffer.float) * 255).toInt()
                    if (bgConfidence >= 100) {
                        image.setHasAlpha(true)
                        image.setPixel(x, y, 0)
                    }
                }
            }
            buffer.rewind()
            return@async image
        }
        return bitmap.await()
    }


    /**
     * trim the empty part of a bitmap.
     **/
    private suspend fun trim(
        bitmap: Bitmap
    ): Bitmap {
        val result = CoroutineScope(Dispatchers.Default).async {
            var firstX = 0
            var firstY = 0
            var lastX = bitmap.width
            var lastY = bitmap.height
            val pixels = IntArray(bitmap.width * bitmap.height)
            bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
            loop@ for (x in 0 until bitmap.width) {
                for (y in 0 until bitmap.height) {
                    if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                        firstX = x
                        break@loop
                    }
                }
            }
            loop@ for (y in 0 until bitmap.height) {
                for (x in firstX until bitmap.width) {
                    if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                        firstY = y
                        break@loop
                    }
                }
            }
            loop@ for (x in bitmap.width - 1 downTo firstX) {
                for (y in bitmap.height - 1 downTo firstY) {
                    if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                        lastX = x
                        break@loop
                    }
                }
            }
            loop@ for (y in bitmap.height - 1 downTo firstY) {
                for (x in bitmap.width - 1 downTo firstX) {
                    if (pixels[x + y * bitmap.width] != Color.TRANSPARENT) {
                        lastY = y
                        break@loop
                    }
                }
            }
            return@async Bitmap.createBitmap(bitmap, firstX, firstY, lastX - firstX, lastY - firstY)
        }
        return result.await()
    }

}

Listener

interface OnBackgroundChangeListener {

    fun onSuccess(bitmap: Bitmap)

    fun onFailed(exception: Exception)

}

Upvotes: 2

Related Questions