Saya telah mencari di mana-mana untuk solusi yang tepat untuk masalah saya dan sepertinya saya belum dapat menemukannya. Saya memiliki ActionBar (ActionBarSherlock) dengan menu yang dinaikkan dari file XML dan menu itu berisi satu item dan satu item itu ditampilkan sebagai ActionItem.
Tidak bisa:
<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>
aktivitas:
[...]
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getSupportMenuInflater().inflate(R.menu.mymenu, menu);
return true;
}
[...]
ActionItem ditampilkan dengan ikon dan tidak ada teks, namun ketika pengguna mengklik ActionItem, saya ingin ikon mulai beranimasi, lebih khusus lagi, berputar di tempatnya. Ikon yang dimaksud adalah ikon segarkan.
Saya menyadari bahwa ActionBar memiliki dukungan untuk menggunakan tampilan kustom ( Menambahkan Tampilan Tindakan ) namun tampilan kustom ini diperluas untuk mencakup seluruh area ActionBar dan sebenarnya memblokir semuanya kecuali ikon aplikasi, yang dalam kasus saya bukan yang saya cari. .
Jadi upaya saya selanjutnya adalah mencoba menggunakan AnimationDrawable dan menentukan animasi frame-by-frame saya, mengatur drawable sebagai ikon untuk item menu, dan kemudian onOptionsItemSelected(MenuItem item)
mendapatkan ikon dan mulai menganimasikan menggunakan ((AnimationDrawable)item.getIcon()).start()
. Namun ini tidak berhasil. Adakah yang tahu cara untuk mencapai efek ini?
android:paddingLeft="12dp"
danandroid:paddingRight="12dp"
ke tema saya sendiri.Saya telah mengerjakan sedikit solusi menggunakan ActionBarSherlock, saya telah menemukan ini:
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>
Kode dalam aktivitas (saya memilikinya di kelas induk 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); } }
dalam aktivitas membuat item menu
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(); }
Dan ikonnya, ini
drawable-xhdpi/ic_menu_navigation_refresh.png
Ini dapat ditemukan di http://developer.android.com/design/downloads/index.html#action-bar-icon-pack
sumber
Selain apa yang dikatakan Jake Wharton, sebaiknya lakukan hal berikut untuk memastikan animasi berhenti dengan lancar dan tidak melompat-lompat segera setelah pemuatan selesai.
Pertama, buat boolean baru (untuk seluruh kelas):
private boolean isCurrentlyLoading;
Temukan metode yang memulai pemuatan Anda. Setel boolean Anda ke true saat aktivitas mulai memuat.
isCurrentlyLoading = true;
Temukan metode yang dimulai saat pemuatan Anda selesai. Alih-alih menghapus animasi, setel boolean Anda ke false.
isCurrentlyLoading = false;
Setel AnimationListener pada animasi Anda:
animationRotate.setAnimationListener(new AnimationListener() {
Kemudian, setiap animasi dieksekusi satu kali, artinya ketika ikon Anda berputar satu kali, periksa status loading, dan jika tidak dimuat lagi, animasi akan berhenti.
@Override public void onAnimationRepeat(Animation animation) { if(!isCurrentlyLoading) { refreshItem.getActionView().clearAnimation(); refreshItem.setActionView(null); } }
Dengan cara ini, animasi hanya dapat dihentikan jika sudah diputar hingga akhir dan akan diulangi segera DAN tidak dimuat lagi.
Setidaknya inilah yang saya lakukan ketika saya ingin menerapkan ide Jake.
sumber
Ada juga opsi untuk membuat rotasi dalam kode. Snip penuh:
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);
sumber
Dengan pustaka dukungan kita bisa menganimasikan ikon tanpa custom 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(); }
Kita tidak bisa menganimasikan drawable secara langsung, jadi gunakan DrawableWrapper (dari android.support.v7 untuk 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; } }
Saya mengambil ide untuk DrawableWrapper dari sini: https://stackoverflow.com/a/39108111/5541688
sumber
ini solusi saya yang sangat sederhana (misalnya, perlu beberapa refactor) bekerja dengan MenuItem standar, Anda dapat menggunakannya dengan sejumlah status, ikon, animasi, logika, dll.
di kelas Aktivitas:
private enum RefreshMode {update, actual, outdated}
pendengar standar:
public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_refresh: { refreshData(null); break; } } }
ke refreshData () lakukan sesuatu seperti ini:
setRefreshIcon(RefreshMode.update); // update your data setRefreshIcon(RefreshMode.actual);
metode untuk menentukan warna atau animasi untuk ikon:
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: { } } }
ada 2 tata letak dengan ikon (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>
animasi putar standar dalam hal ini (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" />
sumber
cara terbaik ada di sini:
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);
lalu:
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)); } }
Ingatlah bahwa Anda tidak dapat mengakses "btsync = this.findViewById (R.id.action_sync);" di onCreate () atau onStart () atau onResume () atau onPostResume () atau onPostCreate () atau onCreateOptionsMenu () atau onPrepareOptionsMenu () jika Anda ingin mendapatkannya tepat setelah aktivitas mulai, letakkan di 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); }
sumber