Icono animado para ActionItem
He estado buscando por todas partes una solución adecuada a mi problema y parece que no puedo encontrar una todavía. Tengo una ActionBar (ActionBarSherlock) con un menú que se infla de un archivo XML y ese menú contiene un elemento y ese elemento se muestra como un elemento ActionItem.
Menú:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/menu_refresh"
android:icon="@drawable/ic_menu_refresh"
android:showAsAction="ifRoom"
android:title="Refresh"/>
</menu>
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 el ActionItem, quiero que el icono comience a animar, más específicamente, rotando 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 cubrir toda el área de la barra de acción y en realidad bloquea todo excepto el icono de la aplicación, que en mi caso no es lo que estaba buscando.
Así que mi siguiente intento fue intentar usar AnimationDrawable y definir mi animación fotograma a fotograma, establecer el elemento de diseño como el icono para el elemento de menú, y luego en onOptionsItemSelected(MenuItem item)
obtener el icono y comenzar a animar utilizando ((AnimationDrawable)item.getIcon()).start()
. Sin embargo, esto no tuvo éxito. ¿Alguien sabe de alguna manera de lograr este efecto?
5 answers
Estás en el camino correcto. Así es como el GitHub Gaug.es la aplicación lo implementará.
Primero definen un XML de animación:
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:duration="1000"
android:interpolator="@android:anim/linear_interpolator" />
Ahora defina un diseño para la vista de acción:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_refresh"
style="@style/Widget.Sherlock.ActionButton" />
Todo lo que necesitamos 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 termine la carga, simplemente detenga la animación y borre la vista:
public void completeRefresh() {
refreshItem.getActionView().clearAnimation();
refreshItem.setActionView(null);
}
¡Y ya está!
Algunas cosas adicionales que hacer:
- Almacenar en caché la vista de acción inflación de diseño e inflación de animación. Son lentos, por lo que solo quieres hacerlos una vez.
- Añadir
null
controles encompleteRefresh()
Aquí está la solicitud de extracción en la aplicación: https://github.com/github/gauges-android/pull/13/files
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-26 13:32:39
He trabajado un poco en la solución usando ActionBarSherlock, se me ocurrió esto:
Res / layout / indeterminate_progress_action.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingRight="12dp" >
<ProgressBar
style="@style/Widget.Sherlock.ProgressBar"
android:layout_width="44dp"
android:layout_height="32dp"
android:layout_gravity="left"
android:layout_marginLeft="12dp"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/rotation_refresh"
android:paddingRight="12dp" />
</FrameLayout>
Res / layout-v11 / indeterminate_progress_action.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" >
<ProgressBar
style="@style/Widget.Sherlock.ProgressBar"
android:layout_width="32dp"
android:layout_gravity="left"
android:layout_marginRight="12dp"
android:layout_marginLeft="12dp"
android:layout_height="32dp"
android:indeterminateDrawable="@drawable/rotation_refresh"
android:indeterminate="true" />
</FrameLayout>
Res/drawable / rotation_refresh.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:drawable="@drawable/ic_menu_navigation_refresh"
android:repeatCount="infinite" >
</rotate>
Código en la 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 la 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
Esto se puede encontrar en http://developer.android.com/design/downloads/index.html#action-bar-icon-pack
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-11 07:48:46
Además de lo que dijo Jake Wharton, probablemente debería hacer lo siguiente para asegurarse de que la animación se detenga sin problemas y no salte alrededor de tan pronto como termine la carga.
Primero, crea un nuevo booleano (para toda la clase):
private boolean isCurrentlyLoading;
Encuentre el método que inicia su carga. Establezca su valor booleano en true cuando la actividad comience a cargarse.
isCurrentlyLoading = true;
Encuentre el método que se inicia cuando finaliza la carga. En lugar de borrar la animación, establecer tu booleano a falso.
isCurrentlyLoading = false;
Establece un AnimationListener en tu animación:
animationRotate.setAnimationListener(new AnimationListener() {
Entonces, cada vez que la animación se ejecutó una vez, eso significa que cuando su icono hizo una rotación, verifique el estado de carga, y si no carga más, la animación se detendrá.
@Override
public void onAnimationRepeat(Animation animation) {
if(!isCurrentlyLoading) {
refreshItem.getActionView().clearAnimation();
refreshItem.setActionView(null);
}
}
De esta manera, la animación solo se puede detener 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-19 16:08:54
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);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-27 14:48:12
Con la biblioteca de soporte podemos animar el icono sin 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 el drawable directamente, así que usa DrawableWrapper(desde Android.apoyo.v7 para API
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.restore();
}
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 de DrawableWrapper de aquí: https://stackoverflow.com/a/39108111/5541688
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-08 19:00:21