2010-10-13 67 views
204

在iOS中,有一個非常簡單而強大的工具來動畫添加和刪除UITableView行,here's a clip from a youtube video顯示默認動畫。請注意周圍的行如何摺疊到刪除的行上。此動畫可幫助用戶跟蹤列表中更改的內容以及數據更改時他們正在查看的列表中的哪些內容。如何動態添加或刪除Android ListView行

由於我一直在Android上開發,我沒有發現在TableView中爲單個行設置動畫效果。在適配器上調用notifyDataSetChanged()會導致ListView立即使用新信息更新其內容。我想在數據發生變化時顯示一個新行的簡單動畫推入或滑出,但我找不到任何記錄的方式來執行此操作。它看起來像LayoutAnimationController可能持有一個關鍵讓這個工作,但是當我在我的ListView上設置一個LayoutAnimationController(類似於ApiDemo's LayoutAnimation2)並且在列表顯示後從我的適配器中刪除元素時,元素立即消失而不是動畫化。

我也試着喜歡的東西下面的動畫中的個別項目,當它被刪除:

@Override 
protected void onListItemClick(ListView l, View v, final int position, long id) { 
    Animation animation = new ScaleAnimation(1, 1, 1, 0); 
    animation.setDuration(100); 
    getListView().getChildAt(position).startAnimation(animation); 
    l.postDelayed(new Runnable() { 
     public void run() { 
      mStringList.remove(position); 
      mAdapter.notifyDataSetChanged(); 
     } 
    }, 100); 
} 

然而,圍繞動畫行,直到他們跳到其新位置不移動位置的行當調用notifyDataSetChanged()時。看來ListView一旦放置了元素就不會更新它的佈局。

雖然寫我自己的實施/叉的ListView已經超越了我的想法,這似乎是應該不那麼難。

謝謝!

+0

做了某人f對此的答案?謝謝 – AndroidGecko 2012-09-12 13:14:19

+0

@Alex:你做了那樣的動畫像youtube視頻(如iPhone),如果你有任何演示或Android的鏈接,那麼請給我,我已經嘗試在xml文件中使用animateLayoutChanges,但它不完全像iphone – Jayesh 2014-11-17 13:57:28

+0

@Jayesh,不幸的是我不再在Android開發上工作。我還沒有測試過2012年前後編寫的任何對這個問題的答案。 – 2014-11-18 16:44:01

回答

122
Animation anim = AnimationUtils.loadAnimation(
        GoTransitApp.this, android.R.anim.slide_out_right 
       ); 
anim.setDuration(500); 
listView.getChildAt(index).startAnimation(anim); 

new Handler().postDelayed(new Runnable() { 

    public void run() { 

     FavouritesManager.getInstance().remove(
      FavouritesManager.getInstance().getTripManagerAtIndex(index) 
     ); 
     populateList(); 
     adapter.notifyDataSetChanged(); 

    } 

}, anim.getDuration()); 

自頂至下的動畫使用:

<set xmlns:android="http://schemas.android.com/apk/res/android"> 
     <translate android:fromYDelta="20%p" android:toYDelta="-20" 
      android:duration="@android:integer/config_mediumAnimTime"/> 
     <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
      android:duration="@android:integer/config_mediumAnimTime" /> 
</set> 
+69

最好使用'anim.setAnimationListener(new AnimationListener(){...})'而不是使用Handler – 2012-12-07 22:50:38

+1

什麼是'populateList()'? – 2013-05-19 07:55:40

+0

@OlegVaskevich爲什麼? – 2013-10-21 20:50:50

2

由於ListViews高度優化,我認爲這是不可能的。你有沒有試圖通過代碼創建你的「ListView」(即通過從xml中擴充你的行並將它們附加到LinearLayout)並對它們進行動畫處理?

+6

重新實現listview只是爲了添加動畫是沒有意義的。 – Macarse 2010-10-17 02:10:32

+0

我當然可以使用'LinearLayout',但是對於非常大的數據集'LinearLayout'的性能會變得糟糕透頂。 ListView有很多圍繞視圖回收的智能優化,允許它處理大型數據集(iOS的UITableView具有相同的優化)。編寫我自己的視圖回收站屬於重新實現ListView :( – 2010-10-17 02:15:41

+0

我知道它沒有任何意義 - 但如果你的工作只有幾行,有好的進出動畫的好處可能是值得的嘗試 – Mannaz 2010-10-17 21:49:08

1

由於Android是開源的,您實際上並不需要重新實現ListView的優化。你可以抓取ListView的代碼,並試圖找到一種方法來破解動畫,你也可以在android bug跟蹤器中使用open a feature request(如果你決定實現它,不要忘記contribute a patch)。

僅供參考,ListView源代碼是here

+1

這是不是' 在此之前,你應該有AOSP項目的建設 - 這是一種非常笨手笨腳的方式來爲列表視圖添加動畫,請看各種開源庫中的實現 – OrhanC1 2014-06-16 16:11:37

2

您是否考慮過在右側掃描動畫?您可以執行一些操作,例如在列表項頂部繪製逐漸變大的白色條,然後將其從列表中移除。其他細胞仍然會挺身而出,但它總比沒有好。

2

我一起砍了另一種方法來做到這一點,而無需操縱列表視圖。不幸的是,常規的Android動畫似乎操縱行的內容,但在實際縮小視圖時效果不佳。所以,首先要考慮這個處理程序:

private Handler handler = new Handler() { 
@Override 
public void handleMessage(Message message) { 
    Bundle bundle = message.getData(); 

    View view = listView.getChildAt(bundle.getInt("viewPosition") - 
     listView.getFirstVisiblePosition()); 

    int heightToSet; 
    if(!bundle.containsKey("viewHeight")) { 
     Rect rect = new Rect(); 
     view.getDrawingRect(rect); 
     heightToSet = rect.height() - 1; 
    } else { 
     heightToSet = bundle.getInt("viewHeight"); 
    } 

    setViewHeight(view, heightToSet); 

    if(heightToSet == 1) 
     return; 

    Message nextMessage = obtainMessage(); 
    bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1); 
    nextMessage.setData(bundle); 
    sendMessage(nextMessage); 
} 

加入該集合到您的列表適配器:

private Collection<Integer> disabledViews = new ArrayList<Integer>(); 

,並添加

public boolean isEnabled(int position) { 
    return !disabledViews.contains(position); 
} 

接下來,無論它是要隱藏行,加入:

Message message = handler.obtainMessage(); 
Bundle bundle = new Bundle(); 
bundle.putInt("viewPosition", listView.getPositionForView(view)); 
message.setData(bundle); 
handler.sendMessage(message);  
disabledViews.add(listView.getPositionForView(view)); 

就是這樣!您可以通過更改一次縮小高度的像素數來更改動畫的速度。不真實複雜,但它的工作原理!

1

我還沒有嘗試過,但它看起來像animateLayoutChanges應該做你想要的。我在ImageSwitcher類中看到它,我認爲它也在ViewSwitcher類中?

+0

嘿謝謝,我會研究它,不幸的是,它看起來像[animateLayoutChanges](http://developer.android.com/reference/android/R.attr.html#animateLayoutChanges)是API Level 11中的新增功能,又名Honeycomb aka不能-use-this-on-any-phones-yet :( – 2011-06-22 16:34:50

0

我已經做了類似的措施。一種方法是在動畫時間內在行onMeasure內隨時間插入視圖的高度,同時爲listView發出requestLayout()。是的,它可能會更好地直接在listView代碼中執行,但它是一個快速解決方案(看起來不錯!)

1

正如我已經解釋我的方法在我的網站我共享該鏈接。反正這個想法是創建位圖 通過getdrawingcache。有兩幅位和動畫較低的位圖來創建移動效果

請參見下面的代碼:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() 
    { 
     public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id) 
     { 
      listView.setDrawingCacheEnabled(true); 
      //listView.buildDrawingCache(true); 
      bitmap = listView.getDrawingCache(); 
      myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom()); 
      myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight()); 
      listView.setDrawingCacheEnabled(false); 
      imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1)); 
      imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2)); 
      imgView1.setVisibility(View.VISIBLE); 
      imgView2.setVisibility(View.VISIBLE); 
      RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); 
      lp.setMargins(0, rowView.getBottom(), 0, 0); 
      imgView2.setLayoutParams(lp); 
      TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight()); 
      transanim.setDuration(400); 
      transanim.setAnimationListener(new Animation.AnimationListener() 
      { 
       public void onAnimationStart(Animation animation) 
       { 
       } 

       public void onAnimationRepeat(Animation animation) 
       { 
       } 

       public void onAnimationEnd(Animation animation) 
       { 
        imgView1.setVisibility(View.GONE); 
        imgView2.setVisibility(View.GONE); 
       } 
      }); 
      array.remove(positon); 
      adapter.notifyDataSetChanged(); 
      imgView2.startAnimation(transanim); 
     } 
    }); 

的理解與圖片see this

釷anks。

2

call listView.scheduleLayoutAnimation(); 更改清單之前

9

查看Google解決方案。這裏只是一個刪除方法。

ListViewRemovalAnimation項目代碼和Video示範

它需要Android 4.1以上版本(API 16)。但我們在2014年以外。

2

向ListView插入新行後,我只是將ListView滾動到新的位置。

ListView.smoothScrollToPosition(position); 
14
+1

我認爲這是簡單的方法,當你需要一個簡單的動畫,但你也可以使用RecyclerView.setItemAnimator()方法定製動畫。 – qatz 2016-03-04 01:55:20

+2

最後一步是我的失蹤鏈接。穩定的Id。謝謝! – auval 2016-11-29 15:49:15

+0

這是一個很好的示例項目,可以很好地演示API。 – 2017-04-18 12:33:22

1

這裏的source code讓你刪除行並重新排序。

演示APK文件也可用。刪除行可以沿着Google的Gmail應用更多地完成,在刷完頂視圖後顯示底部視圖。底部視圖可以有一個撤消按鈕或任何你想要的。

0

公正地分享另一種方法:

首先設置列表視圖的安卓animateLayoutChanges真正

<ListView 
     android:id="@+id/items_list" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:animateLayoutChanges="true"/> 

然後我用一個處理添加項目,並更新列表視圖延遲:

Handler mHandler = new Handler(); 
    //delay in milliseconds 
    private int mInitialDelay = 1000; 
    private final int DELAY_OFFSET = 1000; 


public void addItem(final Integer item) { 
    mHandler.postDelayed(new Runnable() { 
     @Override 
     public void run() { 
      new Thread(new Runnable() { 
       @Override 
       public void run() { 
        mDataSet.add(item); 
        runOnUiThread(new Runnable() { 
         @Override 
         public void run() { 
          mAdapter.notifyDataSetChanged(); 
         } 
        }); 
       } 
      }).start(); 

     } 
    }, mInitialDelay); 
    mInitialDelay += DELAY_OFFSET; 
}