How to decrease the quality of an image on android?

Asked

Viewed 3,512 times

2

I have a list of objects that have an array of images that are somewhat "heavy", at the beginning of the application I have a custom listview that displays an image (icone) of each object, I would like to decrease the quality of these images because when I slide over the list it is possible to notice slowness, I would need to make a copy of an image of each object to decrease the quality and show in the listview.

How can I do this in java code?

  • 1

    Wouldn’t it be more appropriate to do this outside the app? I mean, you can convert the images to a lower resolution manually, and then use them in the application (instead of the current ones, or together - keeping two copies of each image, one for thumbnail display and the other for normal display). The software conversion also takes time and you will end up having the same result (if not worse).

  • This is not feasible, I am creating an automated system, I can’t keep forcing the user to edit the image to put in the application.

  • Ah, it is the user who sends the image then. Sorry, it was not clear in the question.

3 answers

7


This is very common. And in addition to the slowness, you can catch the famous Exception OutOffMemory.

To prevent this, you can solve two (simple) steps within your Adapter:

1)- Place all image uploads inside a AsyncTask. This causes you to take away from your main thread the job of uploading heavy images and block the user experience. And in addition, you can cancel the execution of this AsyncTask case, for example, the user of a very fast scroll:

Add a AsyncTask inside your Viewholder:

...

private class ViewHolder{
    public ImageView image;
    public AsyncTask<Void, Void, Bitmap> asyncTask;
    ...
}

Within the method getView() of your Adapter:

...

//Previnindo o recycle de view
if (holder.asyncTask != null) {
    holder.asyncTask.cancel(true);
    holder.asyncTask = null;
}

//Previnido que a imagem "pisque" caso de um scroll muito rápido;
holder.image.setImageResource(R.drawable.someDrawable);

final ImageView image = holder.image;

holder.asyncTask = new AsyncTask<Void, Void, Bitmap>() {
    @Override
    protected Bitmap doInBackground(Void... params) {
        //Aqui você faz as implementações a seguir
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        image.setImageBitmap(bitmap);       
    }
};
holder.asyncTask.execute();

2) Within your Asynctask, you can use a property called inSampleSize, that makes the Code of your image at a lower resolution, as if it were a "sample" of your image. For example, a 2048x1536 image using inSampleSize as 4 produces an image of approximately 512x384. This loaded image uses only 0.75MB of memory instead of 12MB of the original image size. You can use this property within a BitmapFactory.Options:

//Dentro de sua AsyncTask criada
@Override
protected Bitmap doInBackground(Void... params) {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options();
        /*Reduzindo a qualidade da imagem para preservar memoria. 
        * Aqui você pode testar a redução que melhor atende sua necessidade
        */
        options.inSampleSize = 2;

        return BitmapFactory.decodeStream(new FileInputStream(imagePath), null, options);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    return null;
}

Sources:

Loading Large Bitmaps Efficiently

Processing Bitmaps Off the UI Thread

  • 1

    Perfect, much more than I asked, that part of uploading the image on Asynctask worked perfect, thank you.

2

According to the google itself, it is advisable to perform the processing of the images, so that your application does not exceed the memory.

            // Obter as dimensões do componente na tela
            File file = new File(Caminho da sua imagem)
             int targetW = imageview.getWidth();
             int targetH = imageview.getHeight();


            // Obter as dimensões do bitmap
            BitmapFactory.Options bmOptions = new BitmapFactory.Options();

            bmOptions.inJustDecodeBounds = true;

            BitmapFactory.decodeFile(file.getAbsolutePath(), bmOptions);

            int photoW = bmOptions.outWidth;
            int photoH = bmOptions.outHeight;

            // Determinar o quanto é necessario diminuir a imagem
            int scaleFactor = 1;
            if ((targetW > 0) || (targetH > 0)) {
                scaleFactor = Math.min(photoW/targetW, photoH/targetH); 
            }

            // Decodifica o arquivo de imagem em um Bitmap dimensionando para preencher o
            // ImagemView
            bmOptions.inJustDecodeBounds = false;
            bmOptions.inSampleSize = scaleFactor;
            bmOptions.inPurgeable = true;

            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), bmOptions);



            imageview.setImageBitmap(bitmap);

This way you create a bitmap of the exact size of the screen component. Source: http://developer.android.com/training/displaying-bitmaps/index.html

  • Thanks ctza I can use this procedure within the Asynctask as the colleague also answered, vlw by the help

1

You can resize Bitmap:

Bitmap yourBitmap;
Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);
// ou:
resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.3), (int)(yourBitmap.getHeight()*0.3), true);

Source: https://stackoverflow.com/a/25157279/194717

  • Thanks Tony..

Browser other questions tagged

You are not signed in. Login or sign up in order to post.