Android 实现ListView的展开式动画ExpandAnimation

注意:本次修改,修补了之前的部分bug


android中展开动画主要有2中,Translate和Scale,但运行效果并不像javascript中jQuery(id).slideDown()那么完美,在这里,我们借助动画机制,实现ListView的展开Expand Open,收缩 Expand Close动画。


这些效果在《去哪儿旅行app》和《酷狗音乐app》被广泛使用,我们这里模仿ListView的展开收缩式动画。

先来看图说话,点击item,被点击的Item以下部分会慢慢下滑。


Android 实现ListView的展开式动画ExpandAnimation


好了,上代码

dimens.xml,这里是重点,因为下滑的前提是,尺寸必须是已知的,否则无法正常下滑

<dimen name="bottom_item_height">50dip</dimen>
<!--防止布局bug,所以需要设置一个数值区间大于bottom_item_height的复数-->
<dimen name="_bottom_item_height">-50.5dip</dimen>

主布局文件listview_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:listSelector="@android:color/transparent"
    android:dividerHeight="0.7dip"
    android:choiceMode="none"
    android:divider="#d7d7d7"
    android:orientation="vertical" >
    
</ListView>

listview_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dip" 
        android:orientation="horizontal"
        android:paddingLeft="15dip"
        android:paddingRight="15dip"
        >

        <TextView
            android:id="@+id/song_id_title_tv"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_weight="1"
            android:text="一片艳阳天"
            android:textColor="#333333"
            android:textSize="18sp" />

        <ImageView
            android:id="@+id/song_id_switcher_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:src="@drawable/kg_item_ic_btn_toggle_menu_default" />
   
    </LinearLayout>
   
 <LinearLayout
     android:id="@+id/song_id_panel_li"
     android:layout_width="match_parent"
     android:layout_height="@@dimen/bottom_item_height"
     android:layout_marginBottom="@dimen/_bottom_item_height"
     android:background="#333333"
     android:gravity="center_vertical"
     android:orientation="horizontal" >

     <ImageView
         android:layout_weight="1"
         android:layout_width="0dip"
         android:id="@+id/imageView1"
         android:layout_height="match_parent"
         android:scaleType="center"
         android:src="@drawable/kg_ic_player_menu_download" />

     <ImageView
         android:id="@+id/imageView2"
        android:layout_weight="1"
         android:layout_width="0dip"
         android:layout_height="match_parent"
         android:scaleType="center"
         android:src="@drawable/audio_identify_add_press" />

     <ImageView
         android:id="@+id/imageView3"
         android:layout_weight="1"
         android:layout_width="0dip"
         android:layout_height="match_parent"
         android:scaleType="center"
         android:src="@drawable/audio_identify_share_press" />

     <ImageView
         android:id="@+id/imageView4"
        android:layout_weight="1"
         android:layout_width="0dip"
         android:layout_height="match_parent"
         android:scaleType="center"
         android:src="@drawable/fm_distinguish_favorite" />
    
  </LinearLayout>
</LinearLayout>

自定义动画

package com.example.explistview;

import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.LinearLayout.LayoutParams;

import com.example.actiontabbar.R;

public class ExpandAnimation extends Animation {
	private View mTargetView;
	private boolean isExpandDown;
	public ExpandAnimation(View mTargetView, boolean isExpandDown,int defaultHeight) {
		super();
		this.mTargetView = mTargetView;
		this.isExpandDown = isExpandDown;
		setDuration(500L);
		setInterpolator(new AccelerateDecelerateInterpolator());
		setFillAfter(true);
		resetViewHeight(mTargetView, defaultHeight);
	}
	/**
	 * 动画开始前,务必保证targetView的高度是存在的
	 * @param v
	 * @param defaultHeight
	 */
	private void resetViewHeight(View v,int defaultHeight) 
	{
		LayoutParams lp = (LayoutParams) v.getLayoutParams();
		lp.height = v.getContext().getResources().getDimensionPixelSize(R.dimen.bottom_item_height);
		v.setLayoutParams(lp);
	}
	
	@Override
	protected void applyTransformation(float interpolatedTime, Transformation t) {
		super.applyTransformation(interpolatedTime, t);
		
		int height = mTargetView.getHeight();
		 /**
		  * 注意 这里的 LayoutParams 类型应该和mTargetView的parentView相关,
		  * 比如LinearLayout,这里的类型应该是android.widget.LinearLayout.LayoutParams
		  * 不推荐使用 ViewGroup.LayoutParams,因为不能改变外边距
		  */
		LayoutParams layoutParams = (LayoutParams) mTargetView.getLayoutParams();
		if(isExpandDown)
		{
			layoutParams.bottomMargin = -height  + (int) (height*interpolatedTime);
		}else{
			
			layoutParams.bottomMargin = -(int) (height*interpolatedTime);
		}
		mTargetView.setLayoutParams(layoutParams);
		mTargetView.getParent().requestLayout();
	}
}	

Activity文件

package com.example.explistview;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.actiontabbar.R;
import com.nineoldandroids.view.ViewHelper;
import com.nineoldandroids.view.ViewPropertyAnimator;

 public class AnimExpandActivity extends ActionBarActivity {

	private ListView mListView;

	private final List<Song> songList = new ArrayList<AnimExpandActivity.Song>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.listview_layout);
		mListView = (ListView) findViewById(R.id.main_listview);
		loadTestData();
		mListView.setAdapter(new ListSongItemAdapter(this, songList));
		
	}

	private void loadTestData() {
		
		songList.add(new Song("苏有朋-珍惜", false));
		songList.add(new Song("崔子格-卜卦", false));
		songList.add(new Song("吴奇隆-祝你一路顺风", false));
		songList.add(new Song("马天宇-该死的温柔", false));
		songList.add(new Song("陈奕迅-梦想天空分外蓝", false));
		songList.add(new Song("陈奕迅-爱情转移", false));
		songList.add(new Song("陈奕迅-十年", false));
		songList.add(new Song("张宇-雨一直下", false));
		songList.add(new Song("王筝-我们都是好孩子", false));
		songList.add(new Song("筷子兄弟-小苹果", false));
		songList.add(new Song("筷子兄弟-老男孩", false));
		songList.add(new Song("成龙-神话", false));
		songList.add(new Song("金莎-星月神话", false));
		songList.add(new Song("金莎-相思垢", false));
		songList.add(new Song("许嵩-断桥残雪", false));
		songList.add(new Song("许嵩-半城烟沙", false));
		songList.add(new Song("许嵩-灰色头像", false));
		songList.add(new Song("许嵩-庐州月", false));
		songList.add(new Song("陈坤-好久没回家", false));
	}

	private class ListSongItemAdapter extends BaseAdapter {

		private final List<Song> dataSource = new ArrayList<AnimExpandActivity.Song>();

		private LayoutInflater mLayoutInflater = null;

		private int dimensionPixelSize = 0;

		public ListSongItemAdapter(Context cxt, List<Song> dataSource) {
			this.dataSource.addAll(dataSource);
			mLayoutInflater = LayoutInflater.from(cxt);
			dimensionPixelSize  = cxt.getResources().getDimensionPixelSize(R.dimen.bottom_item_height);

		}
		@Override
		public int getCount() {

			return dataSource.size();
		}
		
		@Override
		public Object getItem(int position) {
			return dataSource.get(position);
		}

		@Override
		public long getItemId(int position) {

			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			SongViewHolder songViewHolder = null;

			if (convertView == null
					|| !SongViewHolder.class.isInstance(convertView.getTag())) {
				convertView = mLayoutInflater.inflate(R.layout.listview_item,
						null);
				songViewHolder = new SongViewHolder(convertView);
				convertView.setTag(songViewHolder);
			} else {
				songViewHolder = (SongViewHolder) convertView.getTag();
			}
			Song song = dataSource.get(position);
			LayoutParams lp = (LayoutParams) songViewHolder.targetPanel.getLayoutParams();
			if(song.isOpen)
			{
				ViewHelper.setRotation(songViewHolder.switcherIv, 180f);
				lp.bottomMargin = 0;
				lp.height =  dimensionPixelSize;
				
			}else{
				ViewHelper.setRotation(songViewHolder.switcherIv, 0f);
				lp.bottomMargin = - dimensionPixelSize;
				//高度是0,lp.bottomMargin值无效,这里之所以设置高度为0,因为为了防止布局耗时导致绘制不及时,从而出现错误显示
				lp.height = 0;
			}
			songViewHolder.targetPanel.setLayoutParams(lp);
			songViewHolder.switcherIv.setOnClickListener(switcherClickListener);
			songViewHolder.switcherIv.setTag(position);
			songViewHolder.titleTv.setText(position + " " + song.name);
			
			return convertView;
		}

	}

	private class Song implements Serializable {
		private String name;

		private boolean isOpen;

		public Song(String name, boolean isOpen) {
			super();
			this.name = name;
			this.isOpen = isOpen;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public void setOpen(boolean isOpen) {
			this.isOpen = isOpen;
		}

		public boolean isOpen() {
			return isOpen;
		}

		@Override
		public String toString() {
			return "Song [name=" + name + ", isOpen=" + isOpen + "]";
		}
		
		
	}

	private class SongViewHolder implements Serializable {

		private View contentView;

		private TextView titleTv;

		private ImageView switcherIv;

		private View targetPanel;

		public SongViewHolder(View contentView) {
			this.contentView = contentView;

			titleTv = (TextView) contentView
					.findViewById(R.id.song_id_title_tv);
			switcherIv = (ImageView) contentView
					.findViewById(R.id.song_id_switcher_btn);
			targetPanel = contentView.findViewById(R.id.song_id_panel_li);
		}
	}

	private View.OnClickListener switcherClickListener = new View.OnClickListener() {

		@Override
		public void onClick(View v) {

			int position = (Integer) v.getTag();

			final BaseAdapter adapter = (BaseAdapter) mListView.getAdapter();
			Song item = (Song) adapter.getItem(position);
			item.setOpen(!item.isOpen());

			ViewGroup itemView = (ViewGroup) v.getParent().getParent();
			SongViewHolder songViewHolder = (SongViewHolder) itemView.getTag();

			Toast.makeText(AnimExpandActivity.this,"position=" + position + ",title=" + item.getName(),Toast.LENGTH_SHORT).show();
			
			int defaultHeight = getResources().getDimensionPixelSize(R.dimen.bottom_item_height);
			itemView.startAnimation(new ExpandAnimation(songViewHolder.targetPanel, item.isOpen(),defaultHeight));

			rotateSwitcherIcon(v, item);
			
		}
		

		
	};
	
	/**
	 * 旋转切换图标
	 * @param v
	 * @param item
	 */
	private void rotateSwitcherIcon(View v, Song item)
	{
		float degree = item.isOpen() ? 180 : 0;
		 ViewPropertyAnimator.animate(v)
		.setDuration(500)
		.setInterpolator(new AccelerateDecelerateInterpolator())
		.rotation(degree)
		.start();
	}
	

}


----------------------------------------------------------

这里使用了较为原始的自定义动画的方式,推荐读者能够使用 属性动画自定义这种实现

可参考链接 http://blog.csdn.net/lingling1420q/article/details/38678493

目前为止,这个例子还需要进行优化,等时间再说吧。


try doing it!


你可能感兴趣的:(Android自定义动画,ExpandAnimation)