Icono animado para ActionItem

He estado buscando en todas partes una solución adecuada a mi problema y parece que todavía no puedo encontrar uno. Tengo una ActionBar (ActionBarSherlock) con un menú que está inflado de un archivo XML y ese menú contiene un elemento y ese elemento se muestra como un ActionItem.

menú:

   

actividad:

 [...] @Override public boolean onCreateOptionsMenu(Menu menu) { getSupportMenuInflater().inflate(R.menu.mymenu, menu); return true; } [...] 

El ActionItem se muestra con un icono y sin texto, sin embargo, cuando un usuario hace clic en ActionItem, quiero que el icono comience a animar, más específicamente, que gire en su lugar. El icono en cuestión es un icono de actualización.

Me doy cuenta de que ActionBar tiene soporte para usar vistas personalizadas ( Agregar una Vista de Acción ); sin embargo, esta vista personalizada se expande para abarcar toda el área de ActionBar y bloquea todo, excepto el ícono de la aplicación, que en mi caso no es lo que estaba buscando .

Así que mi próximo bash fue intentar usar AnimationDrawable y definir mi animación cuadro por cuadro, establecer el dibujable como el ícono para el elemento del menú, y luego en onOptionsItemSelected(MenuItem item) obtener el ícono y comenzar a animar usando ((AnimationDrawable)item.getIcon()).start() . Sin embargo, esto no tuvo éxito. ¿Alguien sabe de alguna manera para lograr este efecto?

Estás en el camino correcto. Así es como la aplicación GitHub Gaug.es lo implementará.

Primero definen un XML de animación:

  

Ahora define un diseño para la vista de acción:

  

Todo lo que tenemos que hacer es habilitar esta vista cada vez que se haga clic en el elemento:

  public void refresh() { /* Attach a rotating ImageView to the refresh item as an ActionView */ LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); ImageView iv = (ImageView) inflater.inflate(R.layout.refresh_action_view, null); Animation rotation = AnimationUtils.loadAnimation(getActivity(), R.anim.clockwise_refresh); rotation.setRepeatCount(Animation.INFINITE); iv.startAnimation(rotation); refreshItem.setActionView(iv); //TODO trigger loading } 

Cuando finalice la carga, simplemente detenga la animación y borre la vista:

 public void completeRefresh() { refreshItem.getActionView().clearAnimation(); refreshItem.setActionView(null); } 

¡Y tu estas listo!

Algunas cosas adicionales que hacer:

  • Guarde en caché el inflado de la inflación y la inflación de la disposición de la vista de acción Son lentos así que solo quieres hacerlos una vez.
  • Agregue comprobaciones null en completeRefresh()

Aquí está la solicitud de extracción en la aplicación: https://github.com/github/gauges-android/pull/13/files

He trabajado un poco en la solución usando ActionBarSherlock, he llegado a esto:

res / layout / indeterminate_progress_action.xml

     

res / layout-v11 / indeterminate_progress_action.xml

     

res / drawable / rotation_refresh.xml

    

Código en actividad (lo tengo en la clase padre ActivityWithRefresh)

 // Helper methods protected MenuItem refreshItem = null; protected void setRefreshItem(MenuItem item) { refreshItem = item; } protected void stopRefresh() { if (refreshItem != null) { refreshItem.setActionView(null); } } protected void runRefresh() { if (refreshItem != null) { refreshItem.setActionView(R.layout.indeterminate_progress_action); } } 

en actividad creando elementos de menú

 private static final int MENU_REFRESH = 1; @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, "Refresh data") .setIcon(R.drawable.ic_menu_navigation_refresh) .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS); setRefreshItem(menu.findItem(MENU_REFRESH)); refreshData(); return super.onCreateOptionsMenu(menu); } private void refreshData(){ runRefresh(); // work with your data // for animation to work properly, make AsyncTask to refresh your data // or delegate work anyhow to another thread // If you'll have work at UI thread, animation might not work at all stopRefresh(); } 

Y el icono, esto es drawable-xhdpi/ic_menu_navigation_refresh.png
drawable-xhdpi / ic_menu_navigation_refresh.png

Esto se puede encontrar en http://developer.android.com/design/downloads/index.html#action-bar-icon-pack

Además de lo que dijo Jake Wharton, debes hacer lo siguiente para garantizar que la animación se detenga sin problemas y no se active tan pronto como termine la carga.

Primero, crea un nuevo booleano (para toda la clase):

 private boolean isCurrentlyLoading; 

Encuentra el método que inicia tu carga. Establezca su valor booleano en verdadero cuando la actividad comience a cargarse.

 isCurrentlyLoading = true; 

Encuentre el método que se inicia cuando finaliza su carga. En lugar de borrar la animación, configure su booleano en falso.

 isCurrentlyLoading = false; 

Establezca un AnimationListener en su animación:

 animationRotate.setAnimationListener(new AnimationListener() { 

Luego, cada vez que se ejecutó la animación una vez, eso significa que cuando el icono hizo una rotación, verifica el estado de carga y, si no se carga más, la animación se detendrá.

 @Override public void onAnimationRepeat(Animation animation) { if(!isCurrentlyLoading) { refreshItem.getActionView().clearAnimation(); refreshItem.setActionView(null); } } 

De esta forma, la animación solo puede detenerse si ya ha girado hasta el final y se repetirá en breve Y ya no se está cargando.

Esto es al menos lo que hice cuando quise implementar la idea de Jake.

También hay una opción para crear la rotación en el código. Recorte completo:

  MenuItem item = getToolbar().getMenu().findItem(Menu.FIRST); if (item == null) return; // define the animation for rotation Animation animation = new RotateAnimation(0.0f, 360.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(1000); //animRotate = AnimationUtils.loadAnimation(this, R.anim.rotation); animation.setRepeatCount(Animation.INFINITE); ImageView imageView = new ImageView(this); imageView.setImageDrawable(UIHelper.getIcon(this, MMEXIconFont.Icon.mmx_refresh)); imageView.startAnimation(animation); item.setActionView(imageView); 

Con la biblioteca de soporte, podemos animar el icono sin acción actionView personalizado.

 private AnimationDrawableWrapper drawableWrapper; @Override public boolean onCreateOptionsMenu(Menu menu) { //inflate menu... MenuItem menuItem = menu.findItem(R.id.your_icon); Drawable icon = menuItem.getIcon(); drawableWrapper = new AnimationDrawableWrapper(getResources(), icon); menuItem.setIcon(drawableWrapper); return true; } public void startRotateIconAnimation() { ValueAnimator animator = ObjectAnimator.ofInt(0, 360); animator.addUpdateListener(animation -> { int rotation = (int) animation.getAnimatedValue(); drawableWrapper.setRotation(rotation); }); animator.start(); } 

No podemos animar dibujables directamente, por lo tanto, use DrawableWrapper (de android.support.v7 para API <21):

 public class AnimationDrawableWrapper extends DrawableWrapper { private float rotation; private Rect bounds; public AnimationDrawableWrapper(Resources resources, Drawable drawable) { super(vectorToBitmapDrawableIfNeeded(resources, drawable)); bounds = new Rect(); } @Override public void draw(Canvas canvas) { copyBounds(bounds); canvas.save(); canvas.rotate(rotation, bounds.centerX(), bounds.centerY()); super.draw(canvas); canvas.restre(); } public void setRotation(float degrees) { this.rotation = degrees % 360; invalidateSelf(); } /** * Workaround for issues related to vector drawables rotation and scaling: * https://code.google.com/p/android/issues/detail?id=192413 * https://code.google.com/p/android/issues/detail?id=208453 */ private static Drawable vectorToBitmapDrawableIfNeeded(Resources resources, Drawable drawable) { if (drawable instanceof VectorDrawable) { Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); drawable.draw(c); drawable = new BitmapDrawable(resources, b); } return drawable; } } 

Tomé la idea para DrawableWrapper desde aquí: https://stackoverflow.com/a/39108111/5541688