Como posso passar um objeto Bitmap de uma atividade para outra

145

Na minha atividade, eu crio um Bitmapobjeto e, em seguida, preciso iniciar outro Activity. Como posso passar esse Bitmapobjeto da subatividade (a que será lançada)?

Michael
fonte

Respostas:

297

Bitmapimplementa Parcelable, para que você sempre possa transmiti-lo com a intenção:

Intent intent = new Intent(this, NewActivity.class);
intent.putExtra("BitmapImage", bitmap);

e recupere-o na outra extremidade:

Intent intent = getIntent(); 
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");
Erich Douglass
fonte
84
Se o bitmap existe como um arquivo ou um recurso, é sempre melhor passar o URIou ResourceIDdo bitmap e não o próprio bitmap. Passar o bitmap inteiro requer muita memória. Passar a URL requer muito pouca memória e permite que cada atividade carregue e dimensione o bitmap conforme necessário.
slayton
3
Não funciona para mim, mas este funciona: stackoverflow.com/questions/11010386/…
Houssem
1
@laylay como passamos imagens como URI / ResourceIDs? exemplo? obrigado!
WantIt
colocar um bitmap extra desse jeito não é uma prática recomendada, se o tamanho do objeto bitmap for maior, você obtém "java.lang.SecurityException: não foi possível encontrar o aplicativo para o chamador android.app.ApplicationThreadProxy ......". a maneira recomendada é como diz o @slayton, você precisa salvar o bitmap no armazenamento externo e passar apenas o URI.
AITAALI_ABDERRAHMANE 14/09
1
qual é o tamanho máximo do bitmap que pode ser transmitido?
AtifSayings
16

Passar o bitmap como parcelável no pacote entre atividades não é uma boa ideia devido à limitação de tamanho de Parceable (1mb). Você pode armazenar o bitmap em um arquivo no armazenamento interno e recuperar o bitmap armazenado em várias atividades. Aqui está um código de exemplo.

Para armazenar bitmap em um arquivo myImage no armazenamento interno:

public String createImageFromBitmap(Bitmap bitmap) {
    String fileName = "myImage";//no .png or .jpg needed
    try {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        FileOutputStream fo = openFileOutput(fileName, Context.MODE_PRIVATE);
        fo.write(bytes.toByteArray());
        // remember close file output
        fo.close();
    } catch (Exception e) {
        e.printStackTrace();
        fileName = null;
    }
    return fileName;
}

Na próxima atividade, você pode decodificar esse arquivo myImage em um bitmap usando o seguinte código:

//here context can be anything like getActivity() for fragment, this or MainActivity.this
Bitmap bitmap = BitmapFactory.decodeStream(context.openFileInput("myImage"));

Nota Muita verificação de bitmap nulo e de escala é omitida.

Argumento ilegal
fonte
Isso não será compilado - não pode resolver o método openFileOutput.
falcão
4

Se a imagem for muito grande e você não puder salvá-la e carregá-la no armazenamento, considere apenas usar uma referência estática global para o bitmap (dentro da atividade de recebimento), que será redefinida para nula no onDestory, apenas se "isChangingConfigurations" retorna verdadeiro.

desenvolvedor android
fonte
3

Porque Intent tem limite de tamanho. Eu uso o objeto estático público para passar o bitmap do serviço à transmissão ....

public class ImageBox {
    public static Queue<Bitmap> mQ = new LinkedBlockingQueue<Bitmap>(); 
}

passar em meu serviço

private void downloadFile(final String url){
        mExecutorService.submit(new Runnable() {
            @Override
            public void run() {
                Bitmap b = BitmapFromURL.getBitmapFromURL(url);
                synchronized (this){
                    TaskCount--;
                }
                Intent i = new Intent(ACTION_ON_GET_IMAGE);
                ImageBox.mQ.offer(b);
                sendBroadcast(i);
                if(TaskCount<=0)stopSelf();
            }
        });
    }

My BroadcastReceiver

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            LOG.d(TAG, "BroadcastReceiver get broadcast");

            String action = intent.getAction();
            if (DownLoadImageService.ACTION_ON_GET_IMAGE.equals(action)) {
                Bitmap b = ImageBox.mQ.poll();
                if(b==null)return;
                if(mListener!=null)mListener.OnGetImage(b);
            }
        }
    };
Deyu 瑜
fonte
2

Compactar e enviar Bitmap

A resposta aceita falhará quando Bitmapfor muito grande. Eu acredito que é um limite de 1 MB . Ele Bitmapdeve ser compactado em um formato de arquivo diferente, como um JPG representado por a ByteArray, para que possa ser transmitido com segurança por meio de um Intent.

Implementação

A função está contida em um thread separado usando Kotlin Coroutines porque a Bitmapcompactação é encadeada após a Bitmapcriação de um URL String. A Bitmapcriação requer um thread separado para evitar erros de ANR (Aplicativo Não Respondendo) .

Conceitos Utilizados

  • Notas da Kotlin Coroutines .
  • O padrão Carregamento, Conteúdo, Erro (LCE) é usado abaixo. Se estiver interessado, você pode aprender mais sobre isso nesta palestra e vídeo .
  • LiveData é usado para retornar os dados. Eu compilei meu recurso favorito do LiveData nestas notas .
  • Na Etapa 3 , toBitmap()é uma função de extensão Kotlin que exige que a biblioteca seja adicionada às dependências do aplicativo.

Código

1. Comprima Bitmapem JPG ByteArray depois de ter sido criado.

Repository.kt

suspend fun bitmapToByteArray(url: String) = withContext(Dispatchers.IO) {
    MutableLiveData<Lce<ContentResult.ContentBitmap>>().apply {
        postValue(Lce.Loading())
        postValue(Lce.Content(ContentResult.ContentBitmap(
            ByteArrayOutputStream().apply {
                try {                     
                    BitmapFactory.decodeStream(URL(url).openConnection().apply {
                        doInput = true
                        connect()
                    }.getInputStream())
                } catch (e: IOException) {
                   postValue(Lce.Error(ContentResult.ContentBitmap(ByteArray(0), "bitmapToByteArray error or null - ${e.localizedMessage}")))
                   null
                }?.compress(CompressFormat.JPEG, BITMAP_COMPRESSION_QUALITY, this)
           }.toByteArray(), "")))
        }
    }

ViewModel.kt

//Calls bitmapToByteArray from the Repository
private fun bitmapToByteArray(url: String) = liveData {
    emitSource(switchMap(repository.bitmapToByteArray(url)) { lce ->
        when (lce) {
            is Lce.Loading -> liveData {}
            is Lce.Content -> liveData {
                emit(Event(ContentResult.ContentBitmap(lce.packet.image, lce.packet.errorMessage)))
            }
            is Lce.Error -> liveData {
                Crashlytics.log(Log.WARN, LOG_TAG,
                        "bitmapToByteArray error or null - ${lce.packet.errorMessage}")
            }
        }
    })
}

2. Passe a imagem como ByteArrayatravés de um Intent.

Nesta amostra, ele é passado de um fragmento para um serviço . É o mesmo conceito se estiver sendo compartilhado entre duas atividades .

Fragment.kt

ContextCompat.startForegroundService(
    context!!,
    Intent(context, AudioService::class.java).apply {
        action = CONTENT_SELECTED_ACTION
        putExtra(CONTENT_SELECTED_BITMAP_KEY, contentPlayer.image)
    })

3. Converta de ByteArrayvolta para Bitmap.

Utils.kt

fun ByteArray.byteArrayToBitmap(context: Context) =
    run {
        BitmapFactory.decodeByteArray(this, BITMAP_OFFSET, size).run {
            if (this != null) this
            // In case the Bitmap loaded was empty or there is an error I have a default Bitmap to return.
            else AppCompatResources.getDrawable(context, ic_coinverse_48dp)?.toBitmap()
        }
    }
Adam Hurwitz
fonte
1

Pode ser tarde, mas pode ajudar. No primeiro fragmento ou atividade, declare uma classe ... por exemplo

   @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        description des = new description();

        if (requestCode == PICK_IMAGE_REQUEST && data != null && data.getData() != null) {
            filePath = data.getData();
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), filePath);
                imageView.setImageBitmap(bitmap);
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
                constan.photoMap = bitmap;
            } catch (IOException e) {
                e.printStackTrace();
            }
       }
    }

public static class constan {
    public static Bitmap photoMap = null;
    public static String namePass = null;
}

Então, na segunda classe / fragmento, faça isso.

Bitmap bm = postFragment.constan.photoMap;
final String itemName = postFragment.constan.namePass;

Espero que ajude.

Mwangi Njuguna
fonte
1

Todas as soluções acima não funcionam para mim; o envio de bitmap parceableByteArraytambém gera erro android.os.TransactionTooLargeException: data parcel size.

Solução

  1. Salve o bitmap no armazenamento interno como:
public String saveBitmap(Bitmap bitmap) {
        String fileName = "ImageName";//no .png or .jpg needed
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
            FileOutputStream fo = openFileOutput(fileName, Context.MODE_PRIVATE);
            fo.write(bytes.toByteArray());
            // remember close file output
            fo.close();
        } catch (Exception e) {
            e.printStackTrace();
            fileName = null;
        }
        return fileName;
    }
  1. e envie putExtra(String)como
Intent intent = new Intent(ActivitySketcher.this,ActivityEditor.class);
intent.putExtra("KEY", saveBitmap(bmp));
startActivity(intent);
  1. e receba-o em outra atividade como:
if(getIntent() != null){
  try {
           src = BitmapFactory.decodeStream(openFileInput("myImage"));
       } catch (FileNotFoundException e) {
            e.printStackTrace();
      }

 }

Ali Tamoor
fonte
0

Você pode criar uma transferência de bitmap. tente isso ....

Na primeira aula:

1) Criar:

private static Bitmap bitmap_transfer;

2) Criar getter e setter

public static Bitmap getBitmap_transfer() {
    return bitmap_transfer;
}

public static void setBitmap_transfer(Bitmap bitmap_transfer_param) {
    bitmap_transfer = bitmap_transfer_param;
}

3) Defina a imagem:

ImageView image = (ImageView) view.findViewById(R.id.image);
image.buildDrawingCache();
setBitmap_transfer(image.getDrawingCache());

Então, na segunda classe:

ImageView image2 = (ImageView) view.findViewById(R.id.img2);
imagem2.setImageDrawable(new BitmapDrawable(getResources(), classe1.getBitmap_transfer()));
Rômulo ZC Cunha
fonte
-2

No meu caso, a maneira mencionada acima não funcionou para mim. Toda vez que coloco o bitmap na intenção, a segunda atividade não é iniciada. O mesmo aconteceu quando passei o bitmap como byte [].

Eu segui este link e funcionou como um charme e muito rápido:

package your.packagename

import android.graphics.Bitmap;

public class CommonResources { 
      public static Bitmap photoFinishBitmap = null;
}

na minha primeira atividade:

Constants.photoFinishBitmap = photoFinishBitmap;
Intent intent = new Intent(mContext, ImageViewerActivity.class);
startActivity(intent);

e aqui está o onCreate () da minha segunda atividade:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Bitmap photo = Constants.photoFinishBitmap;
    if (photo != null) {
        mViewHolder.imageViewerImage.setImageDrawable(new BitmapDrawable(getResources(), photo));
    }
}
Camino2007
fonte
Eu tentei isso, não funcionou. Eu segui o link, e parece que você deveria ter usado em CommonResources.photoFinishBitmapvez de Constants.photoFinishBitmap.
Nathan Hutton
Má prática. O que acontecerá com o campo estático na classe Activity durante a recriação de todo o processo (por exemplo, devido à alteração de permissões para o aplicativo em tempo de execução)? A resposta é NPE.
Alexander Alexander