Análise de Requisitos
O contêiner do componente de toast deve seguir um layout de cima para baixo. Quando um toast é adicionado, uma animação de desaparecimento deve ser reproduzida, consistindo em dois efeitos: primeiro, o layout é empurrado para cima e gradualmente se torna transparente até desaparecer; segundo, durante o desaparecimento, a posição dos toasts adicionados posteriormente é ajustada conforme a mudança de posição do toast em desapaercimento.
Implementação do Código
Layout Principal
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainAtividade">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Olá Mundo!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_teste"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Testar"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainAtividade.java
package com.exemplo.customtoast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
public class MainAtividade extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button botaoTeste = findViewById(R.id.btn_teste);
botaoTeste.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
UtilidadeToast.exibirToast(MainAtividade.this, "Mensagem de exemplo", 1500);
}
});
}
});
}
}
UtilidadeToast.java
package com.exemplo.customtoast;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class UtilidadeToast {
private static WeakReference<LinearLayout> layoutReferenciaFraca;
@SuppressLint({"RtlHardcoded", "UseCompatLoadingForDrawables"})
public static void exibirToast(Context contexto, String mensagem, int duracao) {
if (layoutReferenciaFraca == null) {
WindowManager gerenciadorJanela = (WindowManager) contexto.getSystemService(Context.WINDOW_SERVICE);
LinearLayout layoutPrincipal = new LinearLayout(contexto);
LinearLayout.LayoutParams paramsLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
layoutPrincipal.setLayoutParams(paramsLayout);
layoutPrincipal.setOrientation(LinearLayout.VERTICAL);
WindowManager.LayoutParams paramsJanela = new WindowManager.LayoutParams();
paramsJanela.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
paramsJanela.type = WindowManager.LayoutParams.TYPE_APPLICATION;
paramsJanela.width = WindowManager.LayoutParams.MATCH_PARENT;
paramsJanela.height = WindowManager.LayoutParams.MATCH_PARENT;
paramsJanela.flags = WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
paramsJanela.format = PixelFormat.RGBA_8888;
gerenciadorJanela.addView(layoutPrincipal, paramsJanela);
layoutReferenciaFraca = new WeakReference<>(layoutPrincipal);
exibirToast(contexto, mensagem, duracao);
return;
}
LinearLayout itemToast = new LinearLayout(contexto);
itemToast.setHorizontalGravity(Gravity.CENTER_HORIZONTAL);
LinearLayout.LayoutParams paramsItem = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 60);
paramsItem.setMargins(0, 0, 0, 20);
itemToast.setLayoutParams(paramsItem);
TextView textoConteudo = new TextView(contexto);
textoConteudo.setText(mensagem);
textoConteudo.setPadding(12, 12, 12, 12);
textoConteudo.setMinWidth(220);
textoConteudo.setHeight(60);
textoConteudo.setGravity(Gravity.CENTER);
textoConteudo.setBackground(contexto.getResources().getDrawable(R.drawable.fundo_toast));
textoConteudo.setTextColor(Color.parseColor("#FFFFFF"));
itemToast.addView(textoConteudo);
itemToast.animate()
.setStartDelay(duracao)
.setDuration(1300)
.alpha(0f)
.translationYBy(-30f)
.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private float alturaAnterior = 0;
@Override
public void onAnimationUpdate(ValueAnimator animacao) {
float progressoAtual = (float) animacao.getAnimatedValue();
if (alturaAnterior == 0) {
alturaAnterior = itemToast.getMeasuredHeight();
}
LinearLayout.LayoutParams paramsAtualizados = (LinearLayout.LayoutParams) itemToast.getLayoutParams();
int novaMargemInferior = (int) (-progressoAtual * alturaAnterior);
paramsAtualizados.setMargins(0, 0, 0, novaMargemInferior);
itemToast.post(new Runnable() {
@Override
public void run() {
itemToast.setLayoutParams(paramsAtualizados);
}
});
}
})
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animacao) {
super.onAnimationEnd(animacao);
if (layoutReferenciaFraca.get() != null) {
layoutReferenciaFraca.get().removeView(itemToast);
}
}
})
.start();
layoutReferenciaFraca.get().addView(itemToast);
}
}
fundo_toast.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#333333"/>
<corners android:radius="8dp"/>
<stroke android:color="#4A90E2" android:width="2dp"/>
</shape>
A funcionalidade demonstrada permite a exibição de toasts personalizados com animações de desaparecimento suaves. A implementação utiliza WindowManager para sobrepor a interface e animações do Android para manipular a transparência e o posicionamento dos elementos.