ActionItem के लिए एनिमेटेड आइकन


91

मैं अपनी समस्या के उचित समाधान के लिए हर जगह खोज रहा हूं और मुझे अभी तक कोई भी नहीं मिल रहा है। मेरे पास 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 के पूरे क्षेत्र को कवर करने के लिए विस्तारित है और वास्तव में ऐप आइकन को छोड़कर सब कुछ ब्लॉक करता है, जो कि मेरे मामले में वह नहीं है जो मैं देख रहा था ।

इसलिए मेरा अगला प्रयास था कि एनिमेशनड्रायबल का उपयोग करने और मेरे एनीमेशन फ्रेम-बाय-फ्रेम को परिभाषित करने के लिए, मेनू आइटम के लिए आइकन के रूप में ड्रॉबल को सेट करें, और फिर onOptionsItemSelected(MenuItem item)आइकन प्राप्त करें और उपयोग करना शुरू करें ((AnimationDrawable)item.getIcon()).start()। यह हालांकि असफल रहा। किसी को भी इस प्रभाव को पूरा करने के लिए किसी भी तरह का पता है?

जवाबों:


173

आप सही रास्ते पर हैं। यहां बताया गया है कि GitHub Gaug.es ऐप इसे कैसे लागू करेगा।

पहले वे एक एनीमेशन को परिभाषित करते हैं XML:

<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" />

अब एक्शन दृश्य के लिए एक लेआउट को परिभाषित करें:

<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" />

जब भी आइटम पर क्लिक किया जाता है, तो हमें केवल इस दृश्य को सक्षम करना होगा:

 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
 }

जब लोड हो रहा हो, तो बस एनीमेशन बंद करें और दृश्य साफ़ करें:

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

और आपने कल लिया!

कुछ अतिरिक्त बातें करने के लिए:

  • कैश एक्शन व्यू लेआउट मुद्रास्फीति और एनीमेशन मुद्रास्फीति। वे धीमे हैं इसलिए आप केवल उन्हें एक बार करना चाहते हैं।
  • में nullचेक जोड़ेंcompleteRefresh()

यहाँ एप्लिकेशन पर पुल अनुरोध है: https://github.com/github/gauges-android/pull/13/files


2
महान जवाब, हालांकि getActivity () किसी भी अधिक सुलभ नहीं है, इसके बजाय getApplication () का उपयोग करें।
theAlse

8
@ अलबोर्ज़ जो आपके ऐप के लिए पूरी तरह से विशिष्ट है और सामान्य नियम नहीं है। यह सब उस पर निर्भर है जहाँ आप ताज़ा विधि रख रहे हैं।
जेक व्हार्टन

1
क्या यह सामान्य ActionBar के साथ भी (ActionBar Sherlock के बिना) काम करता है? मेरे लिए आइकन बाईं ओर कूदता है जब एनीमेशन शुरू होता है और बाद में क्लिक करने योग्य नहीं होता है। संपादित करें: बस यह पता चला है कि एक्शन व्यू को सेट करना इसका कारण है, न कि एनीमेशन।
नाम प्रदर्शित करें

2
अगर आपकी छवि वर्गाकार है और एक्शन आइटम के लिए सही आकार नहीं है, तो कोई जम्प नहीं होना चाहिए।
जेक व्हार्टन

13
इसे लागू करते समय मुझे बटन के साइड में कूदने की भी समस्या थी। ऐसा इसलिए है क्योंकि मैं Widget.Sherlock.ActionButton शैली का उपयोग नहीं कर रहा था। मैं जोड़कर इस को सही android:paddingLeft="12dp"और android:paddingRight="12dp"मेरे अपने विषय के लिए।
विलियम कार्टर

16

मैंने ActionBarSherlock का उपयोग करके समाधान पर थोड़ा काम किया है, मैं इसके साथ आया हूं:

रेस / लेआउट / 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>

रेस / लेआउट-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>

रेस / 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>

गतिविधि में कोड (मेरे पास यह गतिविधिश्रेष्ठ मूल वर्ग में है)

// 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
drawable-xhdpi / ic_menu_navigation_refresh.png

इसमें पाया जा सकता है http://developer.android.com/design/downloads/index.html#action-bar-icon-pack


FYI करें मुझे लेआउट-tvdpi-v11 / indeterminate_progress_action.xml को भी ठीक से प्रदर्शित करने के लिए एक android: layout_marginRight = "16dp" के साथ जोड़ना था। मुझे नहीं पता कि यह असंगतता मेरे कोड, एबीएस, या एसडीके में एक बग है।
इराकलीस

मैंने अपने कुछ अनुप्रयोगों के साथ इस समाधान का परीक्षण किया है, और वे सभी एक ही कोड का उपयोग करते हैं। इसलिए मुझे लगता है कि यह आपके कोड में कुछ असंगतता है, क्योंकि ABS (4.2.0) और SDK (API 14 और उच्चतर) साझा किए गए हैं; ;-)
मारेक सेबेरा

क्या आपने इसे नेक्सस 7 में आजमाया है? (इमू नहीं, एक वास्तविक उपकरण) इसका एकमात्र उपकरण इसका ठीक से प्रदर्शित नहीं होना है, इसलिए टीवीडीपीआई सेटिंग्स।
इराक्लिस

@Iraklis नहीं, मेरे पास ऐसा कोई उपकरण नहीं है। तो हाँ, अब मैं देखता हूँ, आपने क्या बहस की है। महान, जवाब देने के लिए इसे जोड़ने के लिए स्वतंत्र महसूस करें।
मर्क सेबेरा

6

जेक व्हार्टन ने जो कहा, इसके अलावा, आपको यह सुनिश्चित करने के लिए निम्नलिखित करना चाहिए कि एनीमेशन आसानी से बंद हो जाए और लोडिंग समाप्त होते ही इधर-उधर न जाए

सबसे पहले, एक नया बूलियन बनाएं (पूरी कक्षा के लिए):

private boolean isCurrentlyLoading;

वह विधि खोजें जो आपकी लोडिंग शुरू करती है। गतिविधि लोड होने पर अपने बूलियन को सही पर सेट करें।

isCurrentlyLoading = true;

उस विधि को खोजें जो आपके लोडिंग समाप्त होने पर शुरू की गई है। एनीमेशन को साफ़ करने के बजाय, अपने बूलियन को झूठे पर सेट करें।

isCurrentlyLoading = false;

अपने एनिमेशन पर एक एनिमेशनलिस्ट सेट करें:

animationRotate.setAnimationListener(new AnimationListener() {

फिर, हर बार एनीमेशन को एक बार निष्पादित किया गया, इसका मतलब है कि जब आपके आइकन ने एक घुमाव बनाया, तो लोडिंग स्थिति की जांच करें, और यदि अब लोड नहीं हो रहा है, तो एनीमेशन बंद हो जाएगा।

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

इस तरह, एनीमेशन को केवल तभी रोका जा सकता है जब यह पहले से ही अंत तक घुमाया जाए और शीघ्र ही दोहराया जाएगा और यह अब लोड नहीं हो रहा है।

यह कम से कम मैंने तब किया जब मैं जेक के विचार को लागू करना चाहता था।


2

कोड में रोटेशन बनाने के लिए एक विकल्प भी है। पूर्ण स्निप:

    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);

इसके उपयोग से और टैप ऑनऑन इटसेलेक्टेड को कॉल नहीं किया जाता है।
स्यूडोज़ाच

1

समर्थन पुस्तकालय के साथ हम कस्टम कार्रवाई के बिना आइकन चेतन कर सकते हैं।

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();
}

हम सीधे ड्रॉ करने योग्य नहीं हो सकते, इसलिए ड्राएबलवेपर (API <21 के लिए android.support.v7 से) का उपयोग करें:

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


0

इसका मेरा बहुत ही सरल समाधान है (उदाहरण के लिए, कुछ रिफ्लेक्टर की जरूरत है) standart MenuItem के साथ काम करता है, आप इसे किसी भी संख्या में राज्यों, आइकन, एनिमेशन, तर्क आदि के साथ उपयोग कर सकते हैं।

गतिविधि वर्ग में:

private enum RefreshMode {update, actual, outdated} 

standart श्रोता:

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.menu_refresh: {
            refreshData(null);
            break;
        }
    }
}

कुछ इस तरह से करें:

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>

इस मामले में standart घुमाएँ एनीमेशन (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"
/>

0

सबसे अच्छा तरीका यहाँ है:

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 () यदि u चाहते हैं, तो इसे शुरू करने के बाद ही इसे पोस्टडेलिएड में डाल दें:

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);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.