Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


Snackbar en la biblioteca de soporte no incluye OnDismissListener ()?

Me gustaría implementar la nueva Snackbar incluida en la última biblioteca de soporte de diseño, pero la forma en que se ofrece parece contra-intuitivo para mi, y supongo que muchos otros, el uso.

Cuando el usuario hace una acción importante, quiero permitir que lo deshaga a través de la Snackbar, pero parece que no hay forma de detectar cuándo es despedido para hacer la acción. Tiene sentido para mí hacerlo de la siguiente manera:

  1. El usuario hace la acción.
  2. Mostrar Snackbar y actualizar la interfaz de usuario como si la acción se ha completado (es decir, parece que los datos se envían a la base de datos, pero en realidad no es todavía).
  3. Si el usuario pulsa "undo", revertirá los cambios de la interfaz de usuario. Si no, cuando la Snackbar es descartada, entonces enviará los datos.

Pero como no veo ningún OnDismissListener accesible, tendría que:

  1. El usuario hace la acción.
  2. Enviar información a la base de datos de inmediato y actualizar la interfaz de usuario.
  3. Si el usuario presiona "deshacer", envía otra llamada a la base de datos para eliminar los datos recién agregados y revertir los cambios de la interfaz de usuario.

Realmente me gustaría evitar tener que hacer las dos llamadas a la base de datos, y sólo enviar una cuando la aplicación sabe que es seguro (el usuario ha evitado presionar "deshacer"). Me doy cuenta de que hay alguna implementación de esto en una biblioteca de terceros a través de un EventListener, pero me gustaría realmente pegarse a la biblioteca de Google.

7 Solutions collect form web for “Snackbar en la biblioteca de soporte no incluye OnDismissListener ()?”

Ahora lo hace

Snackbar.make(getView(), "Hi there!", Snackbar.LENGTH_LONG).setCallback( new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { switch(event) { case Snackbar.Callback.DISMISS_EVENT_ACTION: Toast.makeText(getActivity(), "Clicked the action", Toast.LENGTH_LONG).show(); break; case Snackbar.Callback.DISMISS_EVENT_TIMEOUT: Toast.makeText(getActivity(), "Time out", Toast.LENGTH_LONG).show(); break; } } @Override public void onShown(Snackbar snackbar) { Toast.makeText(getActivity(), "This is my annoying step-brother", Toast.LENGTH_LONG).show(); } }).setAction("Go away!", new View.OnClickListener() { @Override public void onClick(View v) { } }).show(); 

Echa un vistazo a mi clase de ayuda para snackbars.

 public class SnackbarUtils { private static final String LOG_TAG = SnackbarUtils.class.getSimpleName(); private SnackbarUtils() { } public interface SnackbarDismissListener { void onSnackbarDismissed(); } public static void showSnackbar(View rootView, String message, String actionMessage, View.OnClickListener callbacks, final SnackbarDismissListener dismissListener){ Snackbar snackbar = Snackbar.make(rootView,message,Snackbar.LENGTH_LONG); snackbar.setAction(actionMessage,callbacks); if (dismissListener != null){ snackbar.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { dismissListener.onSnackbarDismissed(); } }); } snackbar.show(); } } 

Tengo el mismo problema, pero estoy proporcionando un 'deshacer' para eliminar datos.
Así es como yo lo trato:

  • Fingir que los datos se eliminan de db (ocultar de la interfaz de usuario, que es una lista de elementos)
  • Espere hasta que el snack bar 'should' haya desaparecido
  • Enviar la llamada de eliminación a la base de datos
  • IF, el usuario utilizó la acción de deshacer, bloquear la llamada de DB pendiente

Esto puede no funcionar para usted, ya que está insertando datos y puede (?) Necesitarlo para estar disponible en la base de datos después de la primera acción, pero funcionará si "falsificar" los datos (en la interfaz de usuario) es factible. Mi código no es óptimo, y yo lo llamaría un hack, pero es lo mejor que pude encontrar mientras se quedaba dentro de las bibliotecas oficiales.

  // Control objects boolean canRemoveData = true; Object removedData = getData(id); UI.remove(id); // Snackbar code Snackbar snackbar = Snackbar.make(view, "Data removed", Snackbar.LENGTH_LONG); snackbar.setAction("Undo", new View.OnClickListener() { @Override public void onClick(View v){ canRemoveData = false; DB.remove(id); } }); // Handler to time the dismissal of the snackbar new Handler(getActivity().getMainLooper()).postDelayed(new Runnable() { @Override public void run() { if(canRemoveData){ DB.remove(id); } } }, (int)(snackbar.getDuration() * 1.05f)); // Here I am using a slightly longer delay before sending the db delete call, // just because I don't trust the accuracy of the Handler timing and I want // to be on the safe side. Either way this is dirty code, but the best I could do. 

Mi código actual es más complicado (tratar con el tema de canRemoveData no ser accesible dentro de las subclases sin ser definitivo, pero esto es básicamente cómo logré lograr lo que estás hablando.
Espero que alguien pueda encontrar una mejor solución.

 public class CustomCoordinatorLayout extends CoordinatorLayout { private boolean mIsSnackBar = false; private View mSnakBarView = null; private OnSnackBarListener mOnSnackBarListener = null; public CustomCoordinatorLayout(Context context) { super(context); } public CustomCoordinatorLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(mIsSnackBar){ // Check whether the snackbar is existed. // If it is not existed then index of the snackbar is -1. if(indexOfChild(mSnakBarView) == -1){ mSnakBarView = null; mIsSnackBar = false; if(mOnSnackBarListener != null) mOnSnackBarListener.onDismiss(); Log.d("NEOSARCHIZO","SnackBar is dismissed!"); } } } @Override public void onMeasureChild(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { super.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed); // onMeaureChild is called before onMeasure. // The view of SnackBar doesn't have an id. if(child.getId() == -1){ mIsSnackBar = true; // Store the view of SnackBar. mSnakBarView = child; if(mOnSnackBarListener != null) mOnSnackBarListener.onShow(); Log.d("NEOSARCHIZO","SnackBar is showed!"); } } public void setOnSnackBarListener(OnSnackBarListener onSnackBarListener){ mOnSnackBarListener = onSnackBarListener; } public interface OnSnackBarListener{ public void onShow(); public void onDismiss(); } } 

Yo uso coordinatorlayout personalizado. Cuando se muestra Snackbar, se llaman onMeasure y onMeasureChild de CoordinatorLayout. Así que anulé estos métodos.

Tenga en cuenta que debe establecer ids de los niños del diseño del coordinador personalizado. Porque encuentro la vista de SnackBar por id. El id de SnackBar es -1.

 CustomCoordinatorLayout layout = (CustomCoordinatorLayout)findViewById(R.id.main_content); layout.setOnSnackBarListener(this); Snackbar.make(layout, "Hello!", Snackbar.LENGTH_LONG).setAction("UNDO", new View.OnClickListener() { @Override public void onClick(View v) { //TODO something } }).show(); 

Implementar OnSnackBarListener en su actividad o fragmento. Cuando el snackbar se muestra, entonces se llamará onShow. Y el uno es despedido entonces llamará a Dísmis.

Para mejorar la respuesta de Hitch.united

  boolean mAllowedToRemove = true; Snackbar snack = Snackbar.make(mView, mSnackTitle, Snackbar.LENGTH_LONG); snack.setAction(getString(R.string.snackbar_undo), new OnClickListener() { @Override public void onClick(View v) { mAllowedToRemove = false; // undo ... } }); snack.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { if(!mAllowedToRemove){ // handle actions like http requests ... } } }); snack.show(); 

Esto se acaba de añadir en v23 .

Para ser notificado cuando se ha mostrado o rechazado un snackbar, puede proporcionar una Snackbar.Callback a través de setCallback(Callback) .

La respuesta de Francesco ( aquí ) es correcta, pero por desgracia sólo funciona en API> 12. He enviado una solicitud de funcionalidad a Android Issue Tracker. Usted puede comprobarlo aquí y star él si usted está interesado. Gracias.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.