一.Android官网上training的这个实例项目 com.example.android.animationsdemo.MainActivity
里面有5种4.0以上的实例效果
1.Simple Crossfade
这是一个文字从无到有逐渐显现的效果,没什么说的,就是一个函数
- privatevoid showContentOrLoadingIndicator(boolean contentLoaded) {
- // Decide which view to hide and which to show.
- final View showView = contentLoaded ? mContentView : mLoadingView;
- final View hideView = contentLoaded ? mLoadingView : mContentView;
- // Set the "show" view to 0% opacity but visible, so that it is visible
- // (but fully transparent) during the animation.
- showView.setAlpha(0f);
- showView.setVisibility(View.VISIBLE);
- // Animate the "show" view to 100% opacity, and clear any animation listener set on
- // the view. Remember that listeners are not limited to the specific animation
- // describes in the chained method calls. Listeners are set on the
- // ViewPropertyAnimator object for the view, which persists across several
- // animations.
- showView.animate()
- .alpha(1f)
- .setDuration(mShortAnimationDuration)
- .setListener(null);
- // Animate the "hide" view to 0% opacity. After the animation ends, set its visibility
- // to GONE as an optimization step (it won't participate in layout passes, etc.)
- hideView.animate()
- .alpha(0f)
- .setDuration(mShortAnimationDuration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- publicvoid onAnimationEnd(Animator animation) {
- hideView.setVisibility(View.GONE);
- }
- });
- }
private void showContentOrLoadingIndicator(boolean contentLoaded) {
// Decide which view to hide and which to show.
final View showView = contentLoaded ? mContentView : mLoadingView;
final View hideView = contentLoaded ? mLoadingView : mContentView;
// Set the "show" view to 0% opacity but visible, so that it is visible
// (but fully transparent) during the animation.
showView.setAlpha(0f);
showView.setVisibility(View.VISIBLE);
// Animate the "show" view to 100% opacity, and clear any animation listener set on
// the view. Remember that listeners are not limited to the specific animation
// describes in the chained method calls. Listeners are set on the
// ViewPropertyAnimator object for the view, which persists across several
// animations.
showView.animate()
.alpha(1f)
.setDuration(mShortAnimationDuration)
.setListener(null);
// Animate the "hide" view to 0% opacity. After the animation ends, set its visibility
// to GONE as an optimization step (it won't participate in layout passes, etc.)
hideView.animate()
.alpha(0f)
.setDuration(mShortAnimationDuration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
hideView.setVisibility(View.GONE);
}
});
}
2.Card Flip
point 2.1
在android 3.0以上,你必须调用invalidateOptionsMenu() 当你要update你的menu时,因为 action bar是一直出现的。
point 2.2
public abstract FragmentTransaction setCustomAnimations(int enter, int exit, int popEnter, int popExit)
Set specific animation resources to run for the fragments that are entering and exiting in this transaction. The popEnter
and popExit
animations will be played for enter/exit operations specifically when popping the back stack.
这是一个卡片翻转正背面切换的效果,做的其实一般。
- if (savedInstanceState == null) {
- // If there is no saved instance state, add a fragment representing the
- // front of the card to this activity. If there is saved instance state,
- // this fragment will have already been added to the activity.
- getFragmentManager()
- .beginTransaction()
- .add(R.id.container, new CardFrontFragment())
- .commit();
- } else {
- mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
- }
- // Monitor back stack changes to ensure the action bar shows the appropriate
- // button (either "photo" or "info").
- getFragmentManager().addOnBackStackChangedListener(this);
if (savedInstanceState == null) {
// If there is no saved instance state, add a fragment representing the
// front of the card to this activity. If there is saved instance state,
// this fragment will have already been added to the activity.
getFragmentManager()
.beginTransaction()
.add(R.id.container, new CardFrontFragment())
.commit();
} else {
mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
}
// Monitor back stack changes to ensure the action bar shows the appropriate
// button (either "photo" or "info").
getFragmentManager().addOnBackStackChangedListener(this);
主要还是这个函数,它和onBackStackChanged(), popBackStack()配合,这一点值得学习。
- privatevoid flipCard() {
- if (mShowingBack) {
- getFragmentManager().popBackStack();
- return;
- }
- // Flip to the back.
- mShowingBack = true;
- // Create and commit a new fragment transaction that adds the fragment for the back of
- // the card, uses custom animations, and is part of the fragment manager's back stack.
- getFragmentManager()
- .beginTransaction()
- .setCustomAnimations(
- R.animator.card_flip_right_in, R.animator.card_flip_right_out,
- R.animator.card_flip_left_in, R.animator.card_flip_left_out)
- // Replace any fragments currently in the container view with a fragment
- .replace(R.id.container, new CardBackFragment())
- // Add this transaction to the back stack, allowing users to press Back
- // to get to the front of the card.
- .addToBackStack(null)
- // Commit the transaction.
- .commit();
- // Defer an invalidation of the options menu (on modern devices, the action bar). This
- // can't be done immediately because the transaction may not yet be committed. Commits
- // are asynchronous in that they are posted to the main thread's message loop.
- mHandler.post(new Runnable() {
- @Override
- publicvoid run() {
- invalidateOptionsMenu();
- }
- });
- }
private void flipCard() {
if (mShowingBack) {
getFragmentManager().popBackStack();
return;
}
// Flip to the back.
mShowingBack = true;
// Create and commit a new fragment transaction that adds the fragment for the back of
// the card, uses custom animations, and is part of the fragment manager's back stack.
getFragmentManager()
.beginTransaction()
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
// Replace any fragments currently in the container view with a fragment
.replace(R.id.container, new CardBackFragment())
// Add this transaction to the back stack, allowing users to press Back
// to get to the front of the card.
.addToBackStack(null)
// Commit the transaction.
.commit();
// Defer an invalidation of the options menu (on modern devices, the action bar). This
// can't be done immediately because the transaction may not yet be committed. Commits
// are asynchronous in that they are posted to the main thread's message loop.
mHandler.post(new Runnable() {
@Override
public void run() {
invalidateOptionsMenu();
}
});
}
- @Override
- publicvoid onBackStackChanged() {
- mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
- // When the back stack changes, invalidate the options menu (action bar).
- invalidateOptionsMenu();
- }
@Override
public void onBackStackChanged() {
mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0);
// When the back stack changes, invalidate the options menu (action bar).
invalidateOptionsMenu();
}
3.Screen Slide
这个我用过很多次了,复习一遍。
<android.support.v4.view.ViewPager />
private ViewPager mPager;
private PagerAdapter mPagerAdapter;
- // Instantiate a ViewPager and a PagerAdapter.
- mPager = (ViewPager) findViewById(R.id.pager);
- mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
- mPager.setAdapter(mPagerAdapter);
- mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
- @Override
- publicvoid onPageSelected(int position) {
- // 更新actionbar的menu,可以借鉴
- invalidateOptionsMenu();
- }
- });
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// 更新actionbar的menu,可以借鉴
invalidateOptionsMenu();
}
});
- @Override
- publicboolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.activity_screen_slide, menu);
- menu.findItem(R.id.action_previous).setEnabled(mPager.getCurrentItem() > 0);
- // 这个很好,我一般就只用上面的,没想到这里还可以这样写
- MenuItem item = menu.add(Menu.NONE, R.id.action_next, Menu.NONE,
- (mPager.getCurrentItem() == mPagerAdapter.getCount() - 1)
- ? R.string.action_finish
- : R.string.action_next);
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- returntrue;
- }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.activity_screen_slide, menu);
menu.findItem(R.id.action_previous).setEnabled(mPager.getCurrentItem() > 0);
// 这个很好,我一般就只用上面的,没想到这里还可以这样写
MenuItem item = menu.add(Menu.NONE, R.id.action_next, Menu.NONE,
(mPager.getCurrentItem() == mPagerAdapter.getCount() - 1)
? R.string.action_finish
: R.string.action_next);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
return true;
}
顺便把这个也贴上吧,要不感觉不完整
- @Override
- publicboolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // Navigate "up" the demo structure to the launchpad activity.
- // See http://developer.android.com/design/patterns/navigation.html for more.
- NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class));
- returntrue;
- case R.id.action_previous:
- // Go to the previous step in the wizard. If there is no previous step,
- // setCurrentItem will do nothing.
- mPager.setCurrentItem(mPager.getCurrentItem() - 1);
- returntrue;
- case R.id.action_next:
- // Advance to the next step in the wizard. If there is no next step, setCurrentItem
- // will do nothing.
- mPager.setCurrentItem(mPager.getCurrentItem() + 1);
- returntrue;
- }
- returnsuper.onOptionsItemSelected(item);
- }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Navigate "up" the demo structure to the launchpad activity.
// See http://developer.android.com/design/patterns/navigation.html for more.
NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class));
return true;
case R.id.action_previous:
// Go to the previous step in the wizard. If there is no previous step,
// setCurrentItem will do nothing.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
return true;
case R.id.action_next:
// Advance to the next step in the wizard. If there is no next step, setCurrentItem
// will do nothing.
mPager.setCurrentItem(mPager.getCurrentItem() + 1);
return true;
}
return super.onOptionsItemSelected(item);
}
最后,在加上继承的FragmentStatePagerAdapter 就好了,里面判断并创建显示的 Fragment。
这个create()方法比较有趣,一般都命名为getInstance(),可以看到 fragment传参的是 setArguments() 和 getArguments();
- publicstatic ScreenSlidePageFragment create(int pageNumber) {
- ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_PAGE, pageNumber);
- fragment.setArguments(args);
- return fragment;
- }
- @Override
- publicvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mPageNumber = getArguments().getInt(ARG_PAGE);
- }
public static ScreenSlidePageFragment create(int pageNumber) {
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(ARG_PAGE);
}
另外,再加一点,在Strings.xml中,
- <resourcesxmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <stringname="title_template_step">Step <xliff:gid="step_number">%1$d</xliff:g>: Lorem Ipsum</string>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="title_template_step">Step <xliff:g id="step_number">%1$d</xliff:g>: Lorem Ipsum</string>
- // Set the title view to show the page number.
- ((TextView) rootView.findViewById(android.R.id.text1)).setText(
- getString(R.string.title_template_step, mPageNumber + 1))
// Set the title view to show the page number.
((TextView) rootView.findViewById(android.R.id.text1)).setText(
getString(R.string.title_template_step, mPageNumber + 1))
1、xliff:g标签介绍:
xliff:g标签是用于在动态的设置某些值时,需要进行字符串连接,但又不改变在其中的静态的字符常量的值,我们就需要使用此标签。
2、属性介绍
属性id可以随便命名
属性example表示举例说明,可以省略
%n$ms:代表输出的是字符串,n代表是第几个参数,设置m的值可以在输出之前放置空格
%n$md:代表输出的是整数,n代表是第几个参数,设置m的值可以在输出之前放置空格,也可以设为0m,在输出之前放置m个0
%n$mf:代表输出的是浮点数,n代表是第几个参数,设置m的值可以控制小数位数,如m=2.2时,输出格式为00.00
- <stringname="time">当前时间:<xliff:gid="prefix">%1$s</xliff:g>时 <xliff:gid="time">%2$s</xliff:g>分</string>
- //然后通过程序,context.getString(R.string.time,"10","05");
<string name="time">当前时间:<xliff:g id="prefix">%1$s</xliff:g>时 <xliff:g id="time">%2$s</xliff:g>分</string>
//然后通过程序,context.getString(R.string.time,"10","05");
将会输出——当前时间:10时05分
4.Zoom
其中包含了很多数学,Yay, Math.
其主要就是一个ImageView到另一个ImageView的比例,距离计算,和动画的设计。
- AnimatorSet set = new AnimatorSet();
- set
- .play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left,
- finalBounds.left))
- .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top,
- finalBounds.top))
- .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f))
- .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f));
- set.setDuration(mShortAnimationDuration);
- set.setInterpolator(new DecelerateInterpolator());
- set.addListener(new AnimatorListenerAdapter() {
- @Override
- publicvoid onAnimationEnd(Animator animation) {
- mCurrentAnimator = null;
- }
- @Override
- publicvoid onAnimationCancel(Animator animation) {
- mCurrentAnimator = null;
- }
- });
- set.start();
- mCurrentAnimator = set;
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left,
finalBounds.left))
.with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top,
finalBounds.top))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f));
set.setDuration(mShortAnimationDuration);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
5.Layout Changes
一行行许多view的添加和删除的动画效果
用到的是ViewGroup, 但确实没用动画效果
private ViewGroup mContainerView =(ViewGroup) findViewById(R.id.container);
这个R.id.container在布局文件中是一个LinearLayout
- privatevoid addItem() {
- // Instantiate a new "row" view.
- final ViewGroup newView = (ViewGroup) LayoutInflater.from(this).inflate(
- R.layout.list_item_example, mContainerView, false);
- // Set the text in the new row to a random country.
- ((TextView) newView.findViewById(android.R.id.text1)).setText(
- COUNTRIES[(int) (Math.random() * COUNTRIES.length)]);
- // Set a click listener for the "X" button in the row that will remove the row.
- newView.findViewById(R.id.delete_button).setOnClickListener(new View.OnClickListener() {
- @Override
- publicvoid onClick(View view) {
- // Remove the row from its parent (the container view).
- // Because mContainerView has android:animateLayoutChanges set to true,
- // this removal is automatically animated.
- mContainerView.removeView(newView);
- // If there are no rows remaining, show the empty view.
- if (mContainerView.getChildCount() == 0) {
- findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
- }
- }
- });
- // Because mContainerView has android:animateLayoutChanges set to true,
- // adding this view is automatically animated.
- mContainerView.addView(newView, 0);
- }
private void addItem() {
// Instantiate a new "row" view.
final ViewGroup newView = (ViewGroup) LayoutInflater.from(this).inflate(
R.layout.list_item_example, mContainerView, false);
// Set the text in the new row to a random country.
((TextView) newView.findViewById(android.R.id.text1)).setText(
COUNTRIES[(int) (Math.random() * COUNTRIES.length)]);
// Set a click listener for the "X" button in the row that will remove the row.
newView.findViewById(R.id.delete_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Remove the row from its parent (the container view).
// Because mContainerView has android:animateLayoutChanges set to true,
// this removal is automatically animated.
mContainerView.removeView(newView);
// If there are no rows remaining, show the empty view.
if (mContainerView.getChildCount() == 0) {
findViewById(android.R.id.empty).setVisibility(View.VISIBLE);
}
}
});
// Because mContainerView has android:animateLayoutChanges set to true,
// adding this view is automatically animated.
mContainerView.addView(newView, 0);
}
只是用ViewGroup的addView()和removeView(),是自带透明度动画效果,不算难。
二.ListViewAnimations 由三部分构成 Google cards example, Animation in adapter, Item manipulation
1.Google card example 这个效果真的很棒
point 1 LruCache
这里面不得不提的就是一个 最核心的类是LruCache (此类在android-support-v4的包中提供)
import android.support.v4.util.LruCache;
首先要初始化:
private LruCache<String, Bitmap> mMemoryCache;
- // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
- // LruCache通过构造函数传入缓存值,以KB为单位。
- int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
- // 使用最大可用内存值的1/8作为缓存的大小。
- int cacheSize = maxMemory / 8;
- mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
- @Override
- protectedint sizeOf(String key, Bitmap bitmap) {
- // 重写此方法来衡量每张图片的大小,默认返回图片数量。
- return bitmap.getByteCount() / 1024;
- }
- };
// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
// LruCache通过构造函数传入缓存值,以KB为单位。
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 使用最大可用内存值的1/8作为缓存的大小。
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重写此方法来衡量每张图片的大小,默认返回图片数量。
return bitmap.getByteCount() / 1024;
}
};
还要写两个方法:
- publicvoid addBitmapToMemoryCache(String key, Bitmap bitmap) {
- if (getBitmapFromMemCache(key) == null) {
- mMemoryCache.put(key, bitmap);
- }
- }
- public Bitmap getBitmapFromMemCache(String key) {
- return mMemoryCache.get(key);
- }
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
当向 ImageView 中加载一张图片时,首先会在 LruCache 的缓存中进行检查。如果找到了相应的键值,则会立刻更新ImageView ,否则开启一个后台线程来加载这张图片。
- <span style="font-size: 14px;">publicvoid loadBitmap(int resId, ImageView imageView) {
- final String imageKey = String.valueOf(resId);
- final Bitmap bitmap = getBitmapFromMemCache(imageKey);
- if (bitmap != null) {
- imageView.setImageBitmap(bitmap);
- } else {
- imageView.setImageResource(R.drawable.image_placeholder);
- BitmapWorkerTask task = new BitmapWorkerTask(imageView);
- task.execute(resId);
- }
- } </span>
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
}
BitmapWorkerTask 还要把新加载的图片的键值对放到缓存中。
- <span style="font-size: 14px;">class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
- // 在后台加载图片。
- @Override
- protected Bitmap doInBackground(Integer... params) {
- final Bitmap bitmap = decodeSampledBitmapFromResource(
- getResources(), params[0], 100, 100);
- addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
- return bitmap;
- }
- }</span>
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
// 在后台加载图片。
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
}
好的,我们现在回到 Google card example
用到库里的很多东西 ,SwingBottomInAnimationAdapter 从下方进入的效果,里面就是用到了使 Y 位移变化的动画
SwipeDismissAdapter 横滑消除的Adapter,其中含有 OnDissmissCallback 接口 ,还有 SwipeDismissListViewTouchListener
OnDismissCallback 是横滑删除的接口类 ; 在 GoogleCardAdapter中包含对LruCache的使用。
- ListView listView = (ListView) findViewById(R.id.activity_googlecards_listview);
- mGoogleCardsAdapter = new GoogleCardsAdapter(this);
- SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(
- new SwipeDismissAdapter(mGoogleCardsAdapter, this));
- swingBottomInAnimationAdapter.setListView(listView);
- listView.setAdapter(swingBottomInAnimationAdapter);
- mGoogleCardsAdapter.addAll(getItems());
ListView listView = (ListView) findViewById(R.id.activity_googlecards_listview);
mGoogleCardsAdapter = new GoogleCardsAdapter(this);
SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(
new SwipeDismissAdapter(mGoogleCardsAdapter, this));
swingBottomInAnimationAdapter.setListView(listView);
listView.setAdapter(swingBottomInAnimationAdapter);
mGoogleCardsAdapter.addAll(getItems());
2.AnimationInExamples
是item view 出现的例子,有 SwingBottomIn,SwingRightIn,SwingLeftIn,SwingBottomRightIn,ScaleIn
SwingBottomIn 就是ListView+ Adapter, 只是这个Adapter被专门的效果Adapter所包裹。
- BaseAdapter mAdapter = createListAdapter();
- SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(
- mAdapter);
- swingBottomInAnimationAdapter.setListView(getListView());
- getListView().setAdapter(swingBottomInAnimationAdapter);
BaseAdapter mAdapter = createListAdapter();
SwingBottomInAnimationAdapter swingBottomInAnimationAdapter = new SwingBottomInAnimationAdapter(
mAdapter);
swingBottomInAnimationAdapter.setListView(getListView());
getListView().setAdapter(swingBottomInAnimationAdapter);
三, ExpandingCell
附:ListView添加不同布局的item
http://www.eoeandroid.com/thread-72369-1-1.html
http://blog.csdn.net/lihonghao1017/article/details/10172493
很好的文章,记录开源项目的;
http://blog.tisa7.com/android_open_source_projects