Я везде искал подходящее решение своей проблемы и пока не могу его найти. У меня есть ActionBar (ActionBarSherlock) с меню, которое создается из XML-файла, и это меню содержит один элемент, и этот один элемент отображается как ActionItem.
меню:
<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>
деятельность:
[...]
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.mymenu, menu);
return true;
}
[...]
ActionItem отображается со значком и без текста, однако, когда пользователь нажимает на ActionItem, я хочу, чтобы значок начал анимацию, точнее, вращение на месте. Рассматриваемый значок - это значок обновления.
Я понимаю, что ActionBar поддерживает использование настраиваемых представлений ( добавление представления действий ), однако это настраиваемое представление расширено, чтобы покрыть всю область ActionBar, и фактически блокирует все, кроме значка приложения, что в моем случае не то, что я искал. .
Итак, моя следующая попытка заключалась в том, чтобы попытаться использовать AnimationDrawable и определить мою анимацию покадрово, установить drawable как значок для пункта меню, а затем onOptionsItemSelected(MenuItem item)
получить значок и начать анимацию с использованием ((AnimationDrawable)item.getIcon()).start()
. Однако это было безуспешно. Кто-нибудь знает, как добиться такого эффекта?
android:paddingLeft="12dp"
иandroid:paddingRight="12dp"
в свою тему.Я немного поработал над решением, используя ActionBarSherlock, я придумал следующее:
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>
рез / вытяжка / ротация_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>
Код в действии (у меня он в родительском классе 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); } }
в действии создание пунктов меню
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(); }
И значок, это
drawable-xhdpi/ic_menu_navigation_refresh.png
Это можно найти в http://developer.android.com/design/downloads/index.html#action-bar-icon-pack.
источник
В дополнение к тому, что сказал Джейк Уортон, вам, вероятно, следует сделать следующее, чтобы анимация останавливалась плавно и не перескакивала сразу после завершения загрузки.
Сначала создайте новое логическое значение (для всего класса):
private boolean isCurrentlyLoading;
Найдите метод, с которого начинается загрузка. Установите логическое значение true, когда действие начнет загружаться.
isCurrentlyLoading = true;
Найдите метод, который запускается после завершения загрузки. Вместо того, чтобы очищать анимацию, установите логическое значение false.
isCurrentlyLoading = false;
Установите AnimationListener для вашей анимации:
animationRotate.setAnimationListener(new AnimationListener() {
Затем каждый раз, когда анимация выполнялась один раз, это означает, что когда ваш значок сделал один поворот, проверьте состояние загрузки, и, если больше не загружается, анимация остановится.
@Override public void onAnimationRepeat(Animation animation) { if(!isCurrentlyLoading) { refreshItem.getActionView().clearAnimation(); refreshItem.setActionView(null); } }
Таким образом, анимация может быть остановлена только в том случае, если она уже повернута до конца, и будет повторяться в ближайшее время И она больше не загружается.
По крайней мере, это то, что я сделал, когда хотел реализовать идею Джейка.
источник
Также есть возможность создать ротацию в коде. Полный фрагмент:
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);
источник
С помощью библиотеки поддержки мы можем анимировать значок без настраиваемого actionView.
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(); }
Мы не можем напрямую анимировать drawable, поэтому используйте DrawableWrapper (из android.support.v7 для 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.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; } }
Я взял идею DrawableWrapper отсюда: https://stackoverflow.com/a/39108111/5541688
источник
мое очень простое решение (например, требуется некоторый рефакторинг) работает со стандартным MenuItem, вы можете использовать его с любым количеством состояний, значков, анимации, логики и т. д.
в классе активности:
private enum RefreshMode {update, actual, outdated}
стандартный слушатель:
public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_refresh: { refreshData(null); break; } } }
в refreshData () сделайте что-то вроде этого:
setRefreshIcon(RefreshMode.update); // update your data setRefreshIcon(RefreshMode.actual);
метод определения цвета или анимации для значка:
void setRefreshIcon(RefreshMode refreshMode) { LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); Animation rotation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.rotation); FrameLayout iconView; switch (refreshMode) { case update: { iconView = (FrameLayout) inflater.inflate(R.layout.refresh_action_view,null); iconView.startAnimation(rotation); toolbar.getMenu().findItem(R.id.menu_refresh).setActionView(iconView); break; } case actual: { toolbar.getMenu().findItem(R.id.menu_refresh).getActionView().clearAnimation(); iconView = (FrameLayout) inflater.inflate(R.layout.refresh_action_view_actual,null); toolbar.getMenu().findItem(R.id.menu_refresh).setActionView(null); toolbar.getMenu().findItem(R.id.menu_refresh).setIcon(R.drawable.ic_refresh_24dp_actual); break; } case outdated:{ toolbar.getMenu().findItem(R.id.menu_refresh).setIcon(R.drawable.ic_refresh_24dp); break; } default: { } } }
есть 2 макета со значком (R.layout.refresh_action_view (+ "_actual")):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="48dp" android:layout_height="48dp" android:gravity="center"> <ImageView android:src="@drawable/ic_refresh_24dp_actual" // or ="@drawable/ic_refresh_24dp" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_margin="12dp"/> </FrameLayout>
стандартная анимация поворота в этом случае (R.anim.rotation):
<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:repeatCount="infinite" />
источник
лучший способ здесь:
public class HomeActivity extends AppCompatActivity { public static ActionMenuItemView btsync; public static RotateAnimation rotateAnimation; @Override protected void onCreate(Bundle savedInstanceState) { rotateAnimation = new RotateAnimation(360, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateAnimation.setDuration((long) 2*500); rotateAnimation.setRepeatCount(Animation.INFINITE);
а потом:
private void sync() { btsync = this.findViewById(R.id.action_sync); //remember that u cant access this view at onCreate() or onStart() or onResume() or onPostResume() or onPostCreate() or onCreateOptionsMenu() or onPrepareOptionsMenu() if (isSyncServiceRunning(HomeActivity.this)) { showConfirmStopDialog(); } else { if (btsync != null) { btsync.startAnimation(rotateAnimation); } Context context = getApplicationContext(); context.startService(new Intent(context, SyncService.class)); } }
Помните, что вы не можете получить доступ к "btsync = this.findViewById (R.id.action_sync);" в onCreate () или onStart () или onResume () или onPostResume () или onPostCreate () или onCreateOptionsMenu () или onPrepareOptionsMenu (), если вы хотите получить его сразу после начала активности, поместите его в postdelayed:
public static void refreshSync(Activity context) { Handler handler = new Handler(Looper.getMainLooper()); handler.postDelayed(new Runnable() { public void run() { btsync = context.findViewById(R.id.action_sync); if (btsync != null && isSyncServiceRunning(context)) { btsync.startAnimation(rotateAnimation); } else if (btsync != null) { btsync.clearAnimation(); } } }, 1000); }
источник