Android listview 行删除动画 [英] Android listview row delete animation

查看:40
本文介绍了Android listview 行删除动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为列表视图行设置动画的最佳方法是什么?

我一直在尝试类似的东西:

final Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.translate_up_fade_anim);动画.setAnimationListener(新动画监听器(){@覆盖公共无效onAnimationStart(动画动画){}@覆盖公共无效onAnimationRepeat(动画动画){}@覆盖公共无效onAnimationEnd(动画动画){dataSource.remove(index);适配器.notifyDataSetChanged();}});view.startAnimation(动画);

但动画并不是我想要的.我想我需要为除已删除的列表视图之外的所有其他列表视图行设置动画,但我愿意接受建议.

解决方案

这是源代码(我猜是 android 4.1).

http://developer.android.com/shareables/devbytes/ListViewRemovalAnimation.zip>

这是视频的链接

http://www.youtube.com/watch?list=PLWz5rJ2EKKc_XOgcRukSoKKKJew4一个>

这是 Google 工程师的博客

http://www.graphics-geek.blogspot.in.

您可以通过视频了解正在发生的事情.您还将了解如何移植回以前的版本.

示例:

ListViewRemovalAnimation.java

公共类 ListViewRemovalAnimation 扩展 Activity {StableArrayAdapter mAdapter;ListView mListView;BackgroundContainer mBackgroundContainer;布尔 mSwiping = 假;boolean mItemPressed = false;HashMapmItemIdTopMap = new HashMap();私有静态最终 int SWIPE_DURATION = 250;私有静态最终 int MOVE_DURATION = 150;@覆盖protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_list_view_deletion);mBackgroundContainer = (BackgroundContainer) findViewById(R.id.listViewBackground);mListView = (ListView) findViewById(R.id.listview);android.util.Log.d("Debug", "d=" + mListView.getDivider());最终的 ArrayListcheeseList = new ArrayList();for (int i = 0; i < Cheeses.sCheeseStrings.length; ++i) {cheeseList.add(Cheeses.sCheeseStrings[i]);}mAdapter = new StableArrayAdapter(this,R.layout.opaque_text_view, cheeseList,mTouchListener);mListView.setAdapter(mAdapter);}/***处理触摸事件以淡出/移动拖动的项目,因为它们被刷出*/私有 View.OnTouchListener mTouchListener = new View.OnTouchListener() {浮动 mDownX;私人 int mSwipeSlop = -1;@覆盖公共布尔 onTouch(最终视图 v,MotionEvent 事件){如果(mSwipeSlop <0){mSwipeSlop = ViewConfiguration.get(ListViewRemovalAnimation.this).getScaledTouchSlop();}开关(事件.getAction()){案例 MotionEvent.ACTION_DOWN:如果(mItemPressed){//未处理多项目滑动返回假;}mItemPressed = true;mDownX = event.getX();休息;案例 MotionEvent.ACTION_CANCEL:v.setAlpha(1);v.setTranslationX(0);mItemPressed = false;休息;案例 MotionEvent.ACTION_MOVE:{float x = event.getX() + v.getTranslationX();浮动 deltaX = x - mDownX;浮动 deltaXAbs = Math.abs(deltaX);如果(!mSwiping){如果(deltaXAbs > mSwipeSlop){mSwiping = true;mListView.requestDisallowInterceptTouchEvent(true);mBackgroundContainer.showBackground(v.getTop(), v.getHeight());}}如果(mSwiping){v.setTranslationX((x - mDownX));v.setAlpha(1 - deltaXAbs/v.getWidth());}}休息;案例 MotionEvent.ACTION_UP:{//用户放手 - 确定是将视图动画化还是放回原位如果(mSwiping){float x = event.getX() + v.getTranslationX();浮动 deltaX = x - mDownX;浮动 deltaXAbs = Math.abs(deltaX);浮动分数覆盖;浮动结束X;浮动 endAlpha;最终布尔值删除;如果 (deltaXAbs > v.getWidth()/4) {//大于四分之一的宽度 - 将其动画化分数覆盖 = deltaXAbs/v.getWidth();endX = deltaX <0 ?-v.getWidth() : v.getWidth();endAlpha = 0;删除 = 真;} 别的 {//不够远 - 将其动画回来分数覆盖 = 1 - (deltaXAbs/v.getWidth());结束X = 0;结束阿尔法 = 1;删除 = 假;}//动画位置和滑动项的 alpha//注意:这是滑动行为的简化版本,对于//这个演示的目的是关于动画的.真实版本应该使用//速度(通过 VelocityTracker 类)发送项目或//以适当的速度返回.long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);mListView.setEnabled(false);v.animate().setDuration(duration).alpha(endAlpha).translationX(endX).withEndAction(new Runnable() {@覆盖公共无效运行(){//恢复动画值v.setAlpha(1);v.setTranslationX(0);如果(删除){animateRemoval(mListView, v);} 别的 {mBackgroundContainer.hideBackground();mSwiping = 假;mListView.setEnabled(true);}}});}}mItemPressed = false;休息;默认:返回假;}返回真;}};/*** 此方法为 ListView 容器中的所有其他视图设置动画(不包括 ignoreView)* 进入他们的最终位置.它在 ignoreView 已从* 适配器,但在布局运行之前.这里的方法是弄清楚在哪里* 一切都是现在,然后允许布局运行,然后找出一切之后* 布局,然后在所有这些开始/结束位置之间运行动画.*/私有无效animateRemoval(最终ListView listview,查看viewToRemove){int firstVisiblePosition = listview.getFirstVisiblePosition();for (int i = 0; i < listview.getChildCount(); ++i) {查看孩子 = listview.getChildAt(i);如果(孩子!= viewToRemove){int position = firstVisiblePosition + i;long itemId = mAdapter.getItemId(position);mItemIdTopMap.put(itemId, child.getTop());}}//从适配器中删除项目int position = mListView.getPositionForView(viewToRemove);mAdapter.remove(mAdapter.getItem(position));最终的 ViewTreeObserver 观察者 = listview.getViewTreeObserver();观察者.addOnPreDrawListener(新的ViewTreeObserver.OnPreDrawListener(){公共布尔 onPreDraw() {观察者.removeOnPreDrawListener(this);boolean firstAnimation = true;int firstVisiblePosition = listview.getFirstVisiblePosition();for (int i = 0; i < listview.getChildCount(); ++i) {最终视图子 = listview.getChildAt(i);int position = firstVisiblePosition + i;long itemId = mAdapter.getItemId(position);整数 startTop = mItemIdTopMap.get(itemId);int top = child.getTop();如果(开始顶部!= null){如果(开始顶部!=顶部){int delta = startTop - 顶部;child.setTranslationY(delta);child.animate().setDuration(MOVE_DURATION).translationY(0);如果(第一个动画){child.animate().withEndAction(new Runnable() {公共无效运行(){mBackgroundContainer.hideBackground();mSwiping = 假;mListView.setEnabled(true);}});第一个动画 = 假;}}} 别的 {//动画新视图和其他视图.问题是他们没有//存在于起始状态,所以我们必须计算它们的起始位置//基于相邻视图.int childHeight = child.getHeight() + listview.getDividerHeight();startTop = top + (i > 0 ? childHeight : -childHeight);int delta = startTop - 顶部;child.setTranslationY(delta);child.animate().setDuration(MOVE_DURATION).translationY(0);如果(第一个动画){child.animate().withEndAction(new Runnable() {公共无效运行(){mBackgroundContainer.hideBackground();mSwiping = 假;mListView.setEnabled(true);}});第一个动画 = 假;}}}mItemIdTopMap.clear();返回真;}});}}

StableArrayAdapter.java

public class StableArrayAdapter extends ArrayAdapter;{HashMap<字符串,整数>mIdMap = new HashMap();View.OnTouchListener mTouchListener;公共稳定阵列适配器(上下文上下文,int textViewResourceId,列表<字符串>对象,View.OnTouchListener 侦听器){超级(上下文,textViewResourceId,对象);mTouchListener = 监听器;for (int i = 0; i 

Cheese.java

public class Cheeses {公共静态最终字符串[] sCheeseStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi","Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",Aisy Cendre"、Allgauer Emmentaler"、Alverca"、Ambert"、美国奶酪"、Ami du Chambertin"、Anejo Enchilado"、Anneau du Vic-Bilh"、Anthoriro"、Appenzell"、阿拉贡"、阿尔迪加斯纳"、阿德拉汉"、亚美尼亚弦乐"、Aromes au Gene de Marc"、Asadero"、Asiago"、Aubisque Pyrenees"、Autun"、Avaxtskyr"、Baby Swiss"、Babybel"、Baguette Laonnaise"、Bakers"、Baladi"、Balaton"、Bandal"、Banon"、巴里湾切达干酪"、贝辛"、篮子奶酪"、巴斯奶酪"、巴伐利亚伯格卡斯奶酪"、贝洛"、博福特"、波伏德"、Beenleigh Blue"、啤酒奶酪"、Bel Paese"、贝加德"、贝热布鲁"、伯克斯韦尔"、贝亚兹佩尼尔"、比尔卡斯"、肯尼迪主教"、"Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",七月蓝",蓝德斯卡斯",蓝色",蓝色城堡",蓝​​色拉斯戈尔",Blue Vein (Australian)"、Blue Vein Cheeses"、Bocconcini"、Bocconcini(澳大利亚)"、Boeren Leidenkaas"、Bonchester"、Bosworth"、Bougon"、Boule Du Roves"、Boulette d'Avesnes"、Boursault"、Boursin"、Bouyssou"、Bra"、Braudostur"、早餐奶酪"、Brebis du Lavort"、Brebis du Lochois"、Brebis du Puyfaucon"、"Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",布林"、布林达摩"、布林达摩"、布林扎 (Burduf Brinza)"、Briquette de Brebis"、Briquette du Forez"、Briquette"、Briquette Demi-Affine"、Brousse du Rove"、Bruder Basil"、Brusselae Kaas (Fromage de Bruxelles)"、Bryndza"、Buchette d'Anjou"、Buffalo"、Burgos"、Butte"、Butterkase"、Button (Innes)"、巴克斯顿蓝"、Cabecou"、Caboc"、Cabrales"、Cachaille"、Caciocavallo"、Caciotta"、《卡菲利》、《凯恩斯莫尔》、《卡伦扎纳》、《坎巴佐拉》、《诺曼底卡门贝尔》、加拿大切达干酪"、Canestrato"、Cantal"、Caprice des Dieux"、Capricorn Goat"、Capriole Banon"、Carre de l'Est"、Casciotta di Urbino"、Cashel Blue"、Castellano"、"Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",凯尔特的承诺"、Cendre d'Olivet"、Cerney"、Chabichou"、Chabichou du Poitou"、Chabis de Gatine"、Chaource"、Charolais"、Chaumes"、Cheddar"、Cheddar Clothbound"、Cheshire"、Chevres"、Chevrotin des Aravis"、Chontaleno"、Civray"、Coeur de Camembert au Calvados"、Coeur de Chevre"、Colby"、Cold Pack"、Comte"、Coolea"、Cooleney"、Coquetdale"、Corleggy"、Cornish Pepper"、"Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",Cougar Gold"、Coulommiers"、Coverdale"、Crayeux de Roncq"、Cream Cheese"、奶油哈瓦蒂"、阿格里亚奶油"、墨西哥奶油"、鲜奶油"、克雷森扎"、Croghan"、Crottin de Chavignol"、Crottin du Chavignol"、Crowdie"、Crowley"、"Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino","Cypress Grove Chevre", "Danablu (丹麦蓝)", "Danbo", "Danish Fontina",Daralagjazsky"、Dauphin"、Delice des Fiouves"、Denhany Dorset Drum"、Derby"、Dessertnyj Belyj"、Devon Blue"、Devon Garland"、Dolcelatte"、Doolin"、Doppelrhamstufel"、Dorset Blue Vinney"、Double Gloucester"、Double Worcester"、Dreux a la Feuille"、Dry Jack"、Duddleswell"、Dunbarra"、Dunlop"、Dunsyre Blue"、"Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",埃门特特级园"、埃姆莱特"、埃门塔尔"、勃艮第之泉"、埃斯巴雷奇"、Esrom"、Etorki"、Evansdale Farmhouse Brie"、Evora De L'Alentejo"、Exmoor Blue"、"Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",芬兰瑞士"、芬兰人"、菲奥雷萨尔多"、Fleur du Maquis"、Flor de Guia"、花玛丽"、折叠"、薄荷奶酪折叠"、Fondant de Brebis"、枫丹白露"、丰塔尔"、Fontina Val d'Aosta"、Formaggio di capra"、Fougerus"、四香草高达"、琥珀堡"、卢瓦尔河啤酒"、蒙布里松啤酒"、新鲜杰克"、新鲜马苏里拉奶酪"、新鲜意大利乳清干酪"、新鲜松露"、Fribourgeois"、Friesekaas"、Friesian"、Friesla"、Frinault"、Fromage a Raclette"、Fromage Corse"、Fromage de Montagne de Savoie"、Fromage Frais"、Fruit Cream Cheese"、煎奶酪"、Fynbo"、Gabriel"、Galette du Paludier"、Galette Lyonnaise"、加洛韦山羊奶宝石"、Gammelost"、Gaperon a l'Ail"、Garrotxa"、Gastanberra"、Geitost"、Gippsland Blue"、Gjetost"、Gloucester"、Golden Cross"、Gorgonzola"、"Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",格拉夫顿村切达干酪"、格拉纳"、格拉纳帕达诺"、大瓦泰尔"、Grataron d' Areches"、Gratte-Paille"、Graviera"、Greuilh"、Greve"、格里斯里尔"、格鲁耶尔"、古比恩"、古尔比尼"、哈洛米"、Halloumy (Australian)"、Haloumi-Style Cheese"、Harbourne Blue"、Havarti"、海蒂·格鲁耶尔"、赫里福德跳"、赫尔加德索斯特"、赫里奥特农舍"、赫维"、"Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster","Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",Jermi Tortes"、Jibneh Arabieh"、Jindi Brie"、Jubilee Blue"、Juustoleipa"、Kadchgall"、Kaseri"、Kashta"、Kefalotyri"、Kenafa"、Kernhem"、Kervella Affine"、Kikorangi"、King Island Cape Wickham Brie"、King River Gold"、Klosterkaese"、Knockalara"、Kugelkase"、L'Aveyronnais"、L'Ecir de l'Aubrac"、La Taupiniere"、La Vache Qui Rit"、Laguiole"、Lairobell"、Lajta"、Lanark Blue"、Lancashire"、Langres"、Lappi"、Laruns"、Lavistown"、Le Brin"、Le Fium Orbo"、Le Lacandou"、Le Roule"、Leafield"、Lebbene"、Leerdammer"、Leicester"、Leyden"、Limburger"、林肯郡偷猎者"、Lingot Saint Bousquet d'Orb"、Liptauer"、Little Rydings"、Livarot"、Llanboidy"、Llanglofan Farmhouse"、Loch Arthur Farmhouse"、Loddiswell Avondale"、Longhorn"、Lou Palou"、Lou Pevre"、Lyonnais"、Maasdam"、Macconais"、Mahoe Aged Gouda"、Mahon"、Malvern"、Mamirolle"、Manchego"、Manouri"、Manur"、大理石切达干酪"、大理石奶酪"、Maredsous"、Margotin"、"Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",Mascarpone Torta"、Matocq"、Maytag Blue"、Meira"、Menallack Farmhouse"、Menonita"、Meredith Blue"、Mesost"、Metton (Cancoillotte)"、Meyer Vintage Gouda"、Mihalic Peynir"、Milleens"、Mimolette"、Mine-Gabhar"、Mini Baby Bells"、Mixte"、Molbo"、修道院奶酪"、Mondseer"、Mont D'or Lyonnais"、Montasio"、蒙特雷杰克"、蒙特雷杰克干"、莫比尔"、莫比尔克鲁德蒙塔涅"、Mothais a la Feuille"、Mozzarella"、Mozzarella (Australian)"、"Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",Murol"、Mycella"、Myzithra"、Naboulsi"、Nantais"、Neufchatel"、Neufchatel(澳大利亚)"、Niolo"、Nokkelost"、Northumberland"、Oaxaca"、《旧约克》、《橄榄橄榄》、《橄榄蓝》、《橄榄山》、奥克尼超成熟切达干酪"、奥拉"、Oschtjepka"、Ossau Fermier"、Ossau-Iraty"、Oszczypek"、牛津蓝"、P'tit Berrichon"、Palet de Babligny"、Paneer"、Panela"、Pannerone"、Pant ys Gawn"、Parmesan (Parmigiano)"、Parmigiano Reggiano"、"Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",Patefine Fort"、Pave d'Affinois"、Pave d'Auge"、Pave de Chirac"、Pave du Berry"、山核桃"、胡桃叶山核桃"、山核桃"、Peekskill 金字塔"、Pelardon des Cevennes"、Pelardon des Corbieres"、Penamellera"、Penbryn"、Pencarreg"、Perail de Brebis"、Petit Morin"、Petit Pardou"、Petit-Suisse"、Picodon de Chevre"、Picos de Europa"、Piora"、Pithtviers au Foin"、Plateau de Herve"、Plymouth Cheese"、Podhalanski"、Poivre d'Ane"、Polkolbin"、Pont l'Eveque"、Port Nicholson"、Port-Salut"、Postel"、Pouligny-Saint-Pierre"、Pourly"、Prastost"、Pressato"、Prince-Jean"、加工切达干酪"、Provolone"、Provolone(澳大利亚)"、Pyengana Cheddar"、Pyramide"、Quark"、夸克(澳大利亚)"、Quartirolo Lombardo"、Quatre-Vents"、Quercy Petit"、Queso Blanco"、Queso Blanco con Frutas --Pina y Mango"、Queso de Murcia"、Queso del Montsec"、Queso del Tietar"、Queso Fresco"、Queso Fresco (Adobera)"、Queso Iberico"、Queso Jalapeno"、Queso Majorero"、Queso Media Luna"、Queso Para Frier"、Queso Quesadilla"、Rabacal"、Raclette"、Ragusano"、Raschera"、"Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou","Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",Rigotte"、Rocamadour"、Rollot"、Romano"、Romans Part Dieu"、Roncal"、Roquefort"、"Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",Saanenkaese"、Saga"、Sage Derby"、Sainte Maure"、Saint-Marcellin"、Saint-Nectaire"、Saint-Paulin"、Salers"、Samso"、San Simon"、Sancerre"、"Sap Sago", "Sardo", "Sardo Egypt", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",Selles sur Cher"、Selva"、Serat"、Seriously Strong Cheddar"、Serra da Estrela"、"Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",萨默塞特布里"、索诺玛杰克"、Sottocenare al Tartufo"、Soumaintrain"、Sourire Lozerien"、Spenwood"、Sraffordshire Organic"、St. Agur Blue Cheese"、斯蒂尔顿"、臭主教"、弦"、苏塞克斯滑溜溜"、斯韦西亚斯特"、斯瓦莱代尔"、"Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",塔斯马尼亚高地 Chevre Log"、Taupiniere"、Teifi"、Telemea"、Testouri"、"Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",提尔西特"、廷布布里干酪"、托马"、汤米布鲁利"、汤米 d'Abondance"、Tomme de Chevre"、Tomme de Romans"、Tomme de Savoie"、Tomme des Chouans"、Tommes"、Torta del Casar"、Toscanello"、Touree de L'Aubier"、Tourmalet"、Trappe (Veritable)"、Trois Cornes De Vendee"、Tronchon"、Trou du Cru"、Truffe"、Tupi"、Turunmaa"、Tymsboro"、Tyn Grug"、Tyning"、Ubriaco"、Ulloa"、Vacherin-Fribourgeois"、Valencay"、Vasterbottenost"、Venaco"、Vendomois"、Vieux Corse"、Vignotte"、Vulscombe"、Waimata Farmhouse Blue"、去皮奶酪(澳大利亚)"、滑铁卢"、Weichkaese"、惠灵顿"、温斯利代尔"、白色斯蒂尔顿"、怀特斯通农舍"、威格摩尔"、伍德赛德卡贝库"、Xanadu"、Xynotyro"、Yarg Cornish"、Yarra Valley Pyramid"、Yorkshire Blue"、萨莫拉诺"、萨内蒂·格拉纳·帕达诺"、萨内蒂·帕米吉亚诺·雷吉亚诺"};}

BackgroundContainer.java

public class BackgroundContainer extends FrameLayout {布尔 mShowing = false;可绘制的 mShadowedBackground;int mOpenAreaTop, mOpenAreaBottom, mOpenAreaHeight;布尔 mUpdateBounds = false;公共背景容器(上下文上下文){超级(上下文);在里面();}公共背景容器(上下文上下文,属性集属性){超级(上下文,属性);在里面();}公共背景容器(上下文上下文,AttributeSet attrs,int defStyle){超级(上下文,属性,defStyle);在里面();}私有无效初始化(){mShadowedBackground =getContext().getResources().getDrawable(R.drawable.shadowed_background);}public void showBackground(int top, int bottom) {setWillNotDraw(false);mOpenAreaTop = 顶部;mOpenAreaHeight = 底部;mShowing = true;mUpdateBounds = true;}公共无效隐藏背景(){setWillNotDraw(true);mShowing = false;}@覆盖protected void onDraw(Canvas canvas) {如果(mShowing){如果(mUpdateBounds){mShadowedBackground.setBounds(0, 0, getWidth(), mOpenAreaHeight);}画布.save();canvas.translate(0, mOpenAreaTop);mShadowedBackground.draw(画布);canvas.restore();}}}

布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"机器人:方向=垂直"android:layout_width="match_parent"android:layout_height="match_parent"工具:context=".ListViewAnimations" ><查看class="com.example.android.listviewremovalanimation.BackgroundContainer"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/listViewBackground"><列表视图android:id="@+id/listview"android:divider="@null"android:dividerHeight="0dp"android:layout_width="match_parent"android:layout_height="wrap_content"/></查看></LinearLayout>

opaque_text_view.xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@drawable/tv_background_with_divider"android:textAppearance="?android:attr/textAppearanceListItemSmall"机器人:重力=center_vertical"android:paddingStart="?android:attr/listPreferredItemPaddingStart"android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

What is the best way to animate a listview row?

I keep trying something like:

final Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.translate_up_fade_anim);
        animation.setAnimationListener(new AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                dataSource.remove(index);
                adapter.notifyDataSetChanged();
            }
        });
        view.startAnimation(animation);

but the animation isn't exactly what i want. I think i need to animate every other listview row except the deleted one, but i am open to suggestions.

解决方案

Here's the sourcee code (for android 4.1 i guess).

http://developer.android.com/shareables/devbytes/ListViewRemovalAnimation.zip

Here's the link to the video

http://www.youtube.com/watch?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0&v=YCHNAi9kJI4

Here's a blog Google Engineer

http://www.graphics-geek.blogspot.in.

You have the videos to get to know what's happening. You also get to know how to port back to previous versions.

Example:

ListViewRemovalAnimation.java

public class ListViewRemovalAnimation extends Activity {

    StableArrayAdapter mAdapter;
    ListView mListView;
    BackgroundContainer mBackgroundContainer;
    boolean mSwiping = false;
    boolean mItemPressed = false;
    HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();

    private static final int SWIPE_DURATION = 250;
    private static final int MOVE_DURATION = 150;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view_deletion);

        mBackgroundContainer = (BackgroundContainer) findViewById(R.id.listViewBackground);
        mListView = (ListView) findViewById(R.id.listview);
        android.util.Log.d("Debug", "d=" + mListView.getDivider());
        final ArrayList<String> cheeseList = new ArrayList<String>();
        for (int i = 0; i < Cheeses.sCheeseStrings.length; ++i) {
            cheeseList.add(Cheeses.sCheeseStrings[i]);
        }
        mAdapter = new StableArrayAdapter(this,R.layout.opaque_text_view, cheeseList,
                mTouchListener);
        mListView.setAdapter(mAdapter);
    }

    /**
     * Handle touch events to fade/move dragged items as they are swiped out
     */
    private View.OnTouchListener mTouchListener = new View.OnTouchListener() {

        float mDownX;
        private int mSwipeSlop = -1;

        @Override
        public boolean onTouch(final View v, MotionEvent event) {
            if (mSwipeSlop < 0) {
                mSwipeSlop = ViewConfiguration.get(ListViewRemovalAnimation.this).
                        getScaledTouchSlop();
            }
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mItemPressed) {
                    // Multi-item swipes not handled
                    return false;
                }
                mItemPressed = true;
                mDownX = event.getX();
                break;
            case MotionEvent.ACTION_CANCEL:
                v.setAlpha(1);
                v.setTranslationX(0);
                mItemPressed = false;
                break;
            case MotionEvent.ACTION_MOVE:
                {
                    float x = event.getX() + v.getTranslationX();
                    float deltaX = x - mDownX;
                    float deltaXAbs = Math.abs(deltaX);
                    if (!mSwiping) {
                        if (deltaXAbs > mSwipeSlop) {
                            mSwiping = true;
                            mListView.requestDisallowInterceptTouchEvent(true);
                            mBackgroundContainer.showBackground(v.getTop(), v.getHeight());
                        }
                    }
                    if (mSwiping) {
                        v.setTranslationX((x - mDownX));
                        v.setAlpha(1 - deltaXAbs / v.getWidth());
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                {
                    // User let go - figure out whether to animate the view out, or back into place
                    if (mSwiping) {
                        float x = event.getX() + v.getTranslationX();
                        float deltaX = x - mDownX;
                        float deltaXAbs = Math.abs(deltaX);
                        float fractionCovered;
                        float endX;
                        float endAlpha;
                        final boolean remove;
                        if (deltaXAbs > v.getWidth() / 4) {
                            // Greater than a quarter of the width - animate it out
                            fractionCovered = deltaXAbs / v.getWidth();
                            endX = deltaX < 0 ? -v.getWidth() : v.getWidth();
                            endAlpha = 0;
                            remove = true;
                        } else {
                            // Not far enough - animate it back
                            fractionCovered = 1 - (deltaXAbs / v.getWidth());
                            endX = 0;
                            endAlpha = 1;
                            remove = false;
                        }
                        // Animate position and alpha of swiped item
                        // NOTE: This is a simplified version of swipe behavior, for the
                        // purposes of this demo about animation. A real version should use
                        // velocity (via the VelocityTracker class) to send the item off or
                        // back at an appropriate speed.
                        long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);
                        mListView.setEnabled(false);
                        v.animate().setDuration(duration).
                                alpha(endAlpha).translationX(endX).
                                withEndAction(new Runnable() {
                                    @Override
                                    public void run() {
                                        // Restore animated values
                                        v.setAlpha(1);
                                        v.setTranslationX(0);
                                        if (remove) {
                                            animateRemoval(mListView, v);
                                        } else {
                                            mBackgroundContainer.hideBackground();
                                            mSwiping = false;
                                            mListView.setEnabled(true);
                                        }
                                    }
                                });
                    }
                }
                mItemPressed = false;
                break;
            default: 
                return false;
            }
            return true;
        }
    };

    /**
     * This method animates all other views in the ListView container (not including ignoreView)
     * into their final positions. It is called after ignoreView has been removed from the
     * adapter, but before layout has been run. The approach here is to figure out where
     * everything is now, then allow layout to run, then figure out where everything is after
     * layout, and then to run animations between all of those start/end positions.
     */
    private void animateRemoval(final ListView listview, View viewToRemove) {
        int firstVisiblePosition = listview.getFirstVisiblePosition();
        for (int i = 0; i < listview.getChildCount(); ++i) {
            View child = listview.getChildAt(i);
            if (child != viewToRemove) {
                int position = firstVisiblePosition + i;
                long itemId = mAdapter.getItemId(position);
                mItemIdTopMap.put(itemId, child.getTop());
            }
        }
        // Delete the item from the adapter
        int position = mListView.getPositionForView(viewToRemove);
        mAdapter.remove(mAdapter.getItem(position));

        final ViewTreeObserver observer = listview.getViewTreeObserver();
        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            public boolean onPreDraw() {
                observer.removeOnPreDrawListener(this);
                boolean firstAnimation = true;
                int firstVisiblePosition = listview.getFirstVisiblePosition();
                for (int i = 0; i < listview.getChildCount(); ++i) {
                    final View child = listview.getChildAt(i);
                    int position = firstVisiblePosition + i;
                    long itemId = mAdapter.getItemId(position);
                    Integer startTop = mItemIdTopMap.get(itemId);
                    int top = child.getTop();
                    if (startTop != null) {
                        if (startTop != top) {
                            int delta = startTop - top;
                            child.setTranslationY(delta);
                            child.animate().setDuration(MOVE_DURATION).translationY(0);
                            if (firstAnimation) {
                                child.animate().withEndAction(new Runnable() {
                                    public void run() {
                                        mBackgroundContainer.hideBackground();
                                        mSwiping = false;
                                        mListView.setEnabled(true);
                                    }
                                });
                                firstAnimation = false;
                            }
                        }
                    } else {
                        // Animate new views along with the others. The catch is that they did not
                        // exist in the start state, so we must calculate their starting position
                        // based on neighboring views.
                        int childHeight = child.getHeight() + listview.getDividerHeight();
                        startTop = top + (i > 0 ? childHeight : -childHeight);
                        int delta = startTop - top;
                        child.setTranslationY(delta);
                        child.animate().setDuration(MOVE_DURATION).translationY(0);
                        if (firstAnimation) {
                            child.animate().withEndAction(new Runnable() {
                                public void run() {
                                    mBackgroundContainer.hideBackground();
                                    mSwiping = false;
                                    mListView.setEnabled(true);
                                }
                            });
                            firstAnimation = false;
                        }
                    }
                }
                mItemIdTopMap.clear();
                return true;
            }
        });
    }

}

StableArrayAdapter.java

public class StableArrayAdapter extends ArrayAdapter<String> {

    HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
    View.OnTouchListener mTouchListener;

    public StableArrayAdapter(Context context, int textViewResourceId,
            List<String> objects, View.OnTouchListener listener) {
        super(context, textViewResourceId, objects);
        mTouchListener = listener;
        for (int i = 0; i < objects.size(); ++i) {
            mIdMap.put(objects.get(i), i);
        }
    }

    @Override
    public long getItemId(int position) {
        String item = getItem(position);
        return mIdMap.get(item);
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = super.getView(position, convertView, parent);
        if (view != convertView) {
            // Add touch listener to every new view to track swipe motion
            view.setOnTouchListener(mTouchListener);
        }
        return view;
    }

}

Cheese.java

public class Cheeses {

    public static final String[] sCheeseStrings = {
            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
            "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
            "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
            "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
            "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
            "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
            "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
            "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
            "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
            "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
            "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
            "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
            "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
            "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
            "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
            "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
            "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
            "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
            "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
            "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
            "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
            "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
            "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
            "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
            "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
            "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
            "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
            "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
            "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
            "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
            "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
            "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
            "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
            "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
            "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
            "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
            "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
            "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
            "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
            "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
            "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
            "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
            "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
            "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
            "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
            "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
            "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
            "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
            "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
            "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
            "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
            "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
            "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
            "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
            "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
            "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
            "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
            "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
            "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
            "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
            "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
            "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
            "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
            "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
            "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
            "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
            "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
            "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
            "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
            "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
            "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
            "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
            "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
            "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
            "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
            "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
            "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
            "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
            "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
            "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
            "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
            "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
            "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
            "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
            "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
            "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
            "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
            "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
            "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
            "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
            "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
            "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
            "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
            "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
            "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
            "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
            "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
            "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
            "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
            "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
            "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
            "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
            "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
            "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
            "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
            "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
            "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
            "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
            "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
            "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
            "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
            "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
            "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
            "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
            "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
            "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
            "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
            "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
            "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
            "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
            "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
            "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
            "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
            "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
            "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
            "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
            "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
            "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
            "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
    };

}

BackgroundContainer.java

public class BackgroundContainer extends FrameLayout {

    boolean mShowing = false;
    Drawable mShadowedBackground;
    int mOpenAreaTop, mOpenAreaBottom, mOpenAreaHeight;
    boolean mUpdateBounds = false;

    public BackgroundContainer(Context context) {
        super(context);
        init();
    }

    public BackgroundContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public BackgroundContainer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        mShadowedBackground =
                getContext().getResources().getDrawable(R.drawable.shadowed_background);
    }

    public void showBackground(int top, int bottom) {
        setWillNotDraw(false);
        mOpenAreaTop = top;
        mOpenAreaHeight = bottom;
        mShowing = true;
        mUpdateBounds = true;
    }

    public void hideBackground() {
        setWillNotDraw(true);
        mShowing = false;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mShowing) {
                if (mUpdateBounds) {
                mShadowedBackground.setBounds(0, 0, getWidth(), mOpenAreaHeight);
                }
            canvas.save();
            canvas.translate(0, mOpenAreaTop);
            mShadowedBackground.draw(canvas);
            canvas.restore();
        }
    }

}

Layout files

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ListViewAnimations" >

    <view
        class="com.example.android.listviewremovalanimation.BackgroundContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listViewBackground">

        <ListView
            android:id="@+id/listview"
            android:divider="@null"
            android:dividerHeight="0dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </view>

</LinearLayout>

opaque_text_view.xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/tv_background_with_divider"
    android:textAppearance="?android:attr/textAppearanceListItemSmall"
    android:gravity="center_vertical"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
/>

这篇关于Android listview 行删除动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆