仿Expandablelistview效果的ListView(加入了子列表渐入渐出的动画)

  新来的项目要求第一眼一看就是用Expandablelistview。效果图如下:

           

  其实本来希望直接使用Expandablelistview的,但是需求Expandablelistview在展开一个group时有个动画效果——该group的child一个一个滑动出来并且把下面的group“挤”下去。本以为这个Expandablelistview组件肯定有相关方法的,但竟然没有!网上居然也查不到(有很多人问同样的问题,答案却都是:继承Expandablelistview然后自定义这个动画,然后没了。究竟怎样自定义动画啊有没有搞错!)只好找了下Expandablelistview的方法,有个expandGroup()方法:


    /**
* Expand a group in the grouped list view
*
* @param groupPos the group to be expanded
* @return True if the group was expanded, false otherwise (if the group
* was already expanded, this will return false)
*/
public boolean expandGroup(int groupPos) {
return expandGroup(groupPos, false);
}

  看到它其实是执行了expandGroup(groupPos, false)方法,鼠标挪到方法上一看

  心中一阵狂喜,第二个参数不是是否使用动画么?!赶紧点进去看,结果……


    /**
* Expand a group in the grouped list view
*
* @param groupPos the group to be expanded
* @param animate true if the expanding group should be animated in
* @return True if the group was expanded, false otherwise (if the group
* was already expanded, this will return false)
*/
public boolean expandGroup(int groupPos, boolean animate) {
ExpandableListPosition elGroupPos = ExpandableListPosition.obtain(
ExpandableListPosition.GROUP, groupPos, -1, -1);
PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos);
elGroupPos.recycle();
boolean retValue = mConnector.expandGroup(pm);

if (mOnGroupExpandListener != null) {
mOnGroupExpandListener.onGroupExpand(groupPos);
}

if (animate) {
final int groupFlatPos = pm.position.flatListPos;

final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos),
shiftedGroupPosition);
}
pm.recycle();

return retValue;
}

  看到if(animate)语句瞬间无语了,只是执行了smoothScrollToPosition()就是加了动画效果?太坑了!无奈只好另辟蹊径来实现。

  (废话多了些,现在进入正题。)

  先在网上搜索看到一篇博文:http://blog.csdn.net/qingye_love/article/details/8858147。

  正是我想要的动画效果,写得很详细,不过他是弹出一个很短的操作界面(只有3个button),我想干脆用listView嵌套listView,然后把它的效果拿来用好了。

  主布局文件list_list_layout.xml,很简单,就一个ListView,这个ListView的每个子项对应Expandablelistview的一个Group项:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false"
></ListView>

</RelativeLayout>

  然后是每个ListView子项布局list_item_layout.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="match_parent"
android:orientation="vertical" >

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>

<ImageView
android:id="@+id/listview_item_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="5dp"
android:layout_centerVertical="true"
/>

<TextView
android:id="@+id/listview_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="#000"
android:textSize="20dp"
android:layout_marginLeft="60dp"
android:layout_alignBaseline="@id/listview_item_icon"
/>
</RelativeLayout>

<RelativeLayout
android:id="@+id/listview_item_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:focusable="false"
>
<ListView
android:id="@+id/listview_item_lv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
></ListView>
</RelativeLayout>

</LinearLayout>

  每个Group项由一个ImageView和一个TextView组成,然后下面有个RelativeLayout,id为listview_item_footer,这个RelativeLayout里有个listView,这个就是每个Group下的子列表了。

  对应每个子ListView,也就是没一个Group,适配器写法与普通无异:


import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ItemAdapter extends BaseAdapter{

private List<SeletorDataInfo> devList;
private LayoutInflater mInflater;

public ItemAdapter(Context mContext, List<SeletorDataInfo> devList){
this.devList = devList;
mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public int getCount() {
// TODO Auto-generated method stub
if(null == devList)
return 0;
else {
return devList.size();
}
}

@Override
public SeletorDataInfo getItem(int position) {
// TODO Auto-generated method stub
if(null == devList)
return null;
else {
return devList.get(position);
}
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ItemHolder itemHolder = null;
if (null == convertView) {
itemHolder = new ItemHolder();
convertView = mInflater.inflate(
R.layout.item_item_layout, null);

itemHolder.name = (TextView) convertView
.findViewById(R.id.item_item_name);
itemHolder.icon = (ImageView) convertView
.findViewById(R.id.item_item_icon);

convertView.setTag(itemHolder);
} else {
itemHolder = (ItemHolder) convertView.getTag();
}

SeletorDataInfo mSelfData = getItem(position);
if (null != mSelfData) {
itemHolder.name.setText(mSelfData.getName());
itemHolder.icon.setBackground(mSelfData.getIcon());
}
return convertView;
}

private class ItemHolder {
ImageView icon;
TextView name;
}

}

  其中SeletorDataInfo是我自己定义的数据类。然后是所有Group的适配器:


import java.util.List;

import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class ListViewAdapter extends BaseAdapter{

private Context mContext;
private List<SeletorDataInfo> roomList;
private List<List<SeletorDataInfo>> allList;
private LayoutInflater mInflater;
private int mLcdWidth = 0;
private float mDensity = 0;
private final int itemWidth;

public ListViewAdapter(Context mContext, List<SeletorDataInfo> roomList, List<List<SeletorDataInfo>> allList){
this.mContext = mContext;
this.roomList = roomList;
this.allList = allList;
mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
mLcdWidth = dm.widthPixels;
mDensity = dm.density;
//这里我每个列表项高度是59dp。
itemWidth = (int) (59 * mDensity);
}

@Override
public int getCount() {
// TODO Auto-generated method stub
if(null == roomList)
return 0;
else {
return roomList.size();
}
}

@Override
public SeletorDataInfo getItem(int position) {
// TODO Auto-generated method stub
if(null == roomList)
return null;
else {
return roomList.get(position);
}
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewHolder = null;
if (null == convertView) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(
R.layout.list_item_layout, null);

viewHolder.name = (TextView) convertView
.findViewById(R.id.listview_item_name);
viewHolder.icon = (ImageView) convertView
.findViewById(R.id.listview_item_icon);
viewHolder.lv = (ListView) convertView
.findViewById(R.id.listview_item_lv);

convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

SeletorDataInfo mSelfData = roomList.get(position);
if (null != mSelfData) {
viewHolder.name.setText(mSelfData.getName());
viewHolder.icon.setBackground(mSelfData.getIcon());
viewHolder.lv.setAdapter(new ItemAdapter(mContext, allList.get(position)));
}

//**********************************************************************************************************
RelativeLayout footer = (RelativeLayout) convertView.findViewById(R.id.listview_item_footer);
//不明白为什么宽度被设成:屏宽减去10dp(mLcdWidth - 10 * mDensity),不过不去深究这个,因为我们关心的是高度。
int widthSpec = MeasureSpec.makeMeasureSpec((int) (mLcdWidth - 10 * mDensity), MeasureSpec.EXACTLY);
//然后,调用measure()方法,宽度被设成上面的widthSpec,而高度传了个0,不过没有关系因为高度下面才会设置
footer.measure(widthSpec, 0);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) footer.getLayoutParams();
//在此设置高度为:该组(Group)的项目数 * 每一项的高度。
//本来我参看的那篇博文用的是params.bottomMargin = -footer.getMeasuredHeight();
//但我使用时取footer.getMeasuredHeight(); 总出问题,第一次取只有listView一项的高度,后面高度也不匹配
//不知道是listView缓存机制带来的问题还是什么,这里如果知道没一个列表项的高度,照现在的写法也没有问题。
params.height = (allList.get(position).size() * itemWidth);
if(roomList.get(position).state == 0) {
params.bottomMargin = - params.height;
footer.setVisibility(View.GONE);
} else {
params.bottomMargin = 0;
footer.setVisibility(View.VISIBLE);
}
     //**********************************************************************************************************
return convertView;
}

private class ViewHolder {
ImageView icon;
TextView name;
ListView lv;
}

}

  与之前的adapter不同的地方主要在星号之间的代码,原理其实很简单,先测出你子ListView(比如名为mListView)所占的高度(比如高度为mHeight),然后把这个mListView的LayoutParams.bottomMargin
=
-mHeight;这样,其实mListView正好在其父布局的外面(其父布局正是footer)。然后下面的动画类中,不断设置这个LayoutParams.bottomMargin的值,让它从-mHeight逐渐变为0。那么,这个mListView就好像从两个Group项中“挤出来”的感觉一样。

  然后是自定义动画:


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

public class ViewExpandAnimation extends Animation {

private View mAnimationView = null;
private LayoutParams mViewLayoutParams = null;
private int mStart = 0;
private int mEnd = 0;

public ViewExpandAnimation(View view){
animationSettings(view, 500);
}

public ViewExpandAnimation(View view, int duration){
animationSettings(view, duration);
}

private void animationSettings(View view, int duration){
setDuration(duration);
mAnimationView = view;
mViewLayoutParams = (LayoutParams) view.getLayoutParams();
mStart = mViewLayoutParams.bottomMargin;
mEnd = (mStart == 0 ? (0 - view.getHeight()) : 0);
view.setVisibility(View.VISIBLE);
}

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);

if(interpolatedTime < 1.0f){
mViewLayoutParams.bottomMargin = mStart + (int) ((mEnd - mStart) * interpolatedTime);
// invalidate
mAnimationView.requestLayout();
}else{
mViewLayoutParams.bottomMargin = mEnd;
mAnimationView.requestLayout();
if(mEnd != 0){
mAnimationView.setVisibility(View.GONE);
}
}
}
}

  activity中加入如下片段即可:


        mListViewAdapter = new ListViewAdapter(this, roomList, allList);
mListView.setAdapter(mListViewAdapter);
mListView.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> arg0, View v, int pos,
long arg3) {
View footer = v.findViewById(R.id.listview_item_footer);
footer.startAnimation(new ViewExpandAnimation(footer));
if(roomList.get(pos).state == 0) {
roomList.get(pos).state = 1;
} else {
roomList.get(pos).state = 0;
}
}
});

仿Expandablelistview效果的ListView(加入了子列表渐入渐出的动画),布布扣,bubuko.com

时间: 2024-10-25 12:55:51

仿Expandablelistview效果的ListView(加入了子列表渐入渐出的动画)的相关文章

Unity实现场景加载渐入渐出效果

项目中要用到加载场景的时候有个渐入渐出的效果,做了一下,跟大家分享 首先,创建两个场景Main和Game场景: 其次,在Main场景中创建FandeScene.cs脚本,创建Fade空对象,挂载,给一张黑色的图片,拖成预设体,同样也拖到Game场景中. using UnityEngine; using System.Collections; public class FadeScene : MonoBehaviour {     public Texture blackTexture;     

转 android 从底部弹出一个popuwindow,渐入渐出效果。我这里是用在购物车需要选择购买选项的操作。

最近要改客户端,需要实现一个从底部弹出的popuwindow,像我这种渣渣android技术,能整出popuwindow但是整不出动画,百度之,记录一下. 从下面这个地址转的 http://blog.csdn.net/yxhuang2008/article/details/42617805 最近因为要用到PopupWindow,所以,就在网上搜索了一下,发现挺多关于这样的文章,现在我把它们整理了一下. 1.Android PopupWindow 的使用技巧,http://www.cnblogs.

Javascript渐隐渐入渐出效果 fadeOut fadeIn

//fadeOut //function start function fadeOut(ele,speed){ var ele=document.getElementById(ele); var opacitynum=ele.style.opacity||1; var speed=(speed/100)||10; function opacityOff(){ if(opacitynum>0){ ele.style.opacity=opacitynum=(opacitynum-0.01).toFi

为ListView的子列表添加不同的响应事件

如何实现当点击不同的listView中的每个子列表出发不同的事件? 当在布局中,设置对应的ListView主布局及对应子布局时,在Activity中向ListView中添加子列表,利用每个子列表的Position,进行查找Id,使每个子列表相应不同的事件. 如下: 1 SettingItem.setOnItemClickListener(new OnItemClickListener(){ 2 public void onItemClick(AdapterView<?> adpterView,

Android Animation动画实战(一): 从布局动画引入ListView滑动时,每一Item项的显示动画

前言: 之前,我已经写了两篇博文,给大家介绍了Android的基础动画是如何实现的,如果还不清楚的,可以点击查看:Android Animation动画详解(一): 补间动画 及 Android Animation动画详解(二): 组合动画特效 . 已经熟悉了基础动画的实现后,便可以试着去实现常见APP中出现过的那些精美的动画.今天我主要给大家引入一个APP的ListView的动画效果: 当展示ListView时,Listview的每一个列表项都按照规定的动画显示出来. 说起来比较抽象,先给大家

Android开发之自定义HorizontalScrollView视图实现仿ViewPager效果

开发过程中,需要达到 HorizontalScrollView和ViewPager的效果,于是直接重写了HorizontalScrollView来达到实现ViewPager的效果. 实际效果图如下: (1)自定义HorizontalScrollView类:AppHorizontalScrollView实现: package com.czm.ui.view; import java.util.ArrayList; import android.content.Context; import and

Atitit 图像处理之仿油画效果&#160;Oilpaint油画滤镜 水彩画 漫画滤镜&#160;v2

Atitit 图像处理之仿油画效果 Oilpaint油画滤镜 水彩画 漫画滤镜 v2 1.1. 具体源码参考1 2. ,油画 水彩画具有几个比较明显的特点如下:1 2.1. 明暗层次(灰度)较少  也就5级别够用了1 2.2. 颜色泛用   使用的颜色比较单一,一般不会超过7种颜色,不像真实照片那样,具有丰富的颜色种类:2 3. 水彩画滤镜算法如下:2 3.1. 这个其实就是灰度层次降低维度的过程.3 3.2. 模板半径Radius用来调节水彩画的水彩程度.即是颜色的降低维度的过程3 3.3.

仿微信效果,主要是actionbar的一些知识

1.新建actionBar的menu <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="com.pangzaifei.weixin.Demo" > <item android:id="@+id/action_searc

【Android自定义控件】圆圈交替,仿progress效果

还是我们自定View的那几个步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 3.重写onMesure (不是必须) 4.重写onDraw 自定义View的属性 <?xml version="1.0" encoding="utf-8"?> <resources> <attr name="firstColor" format="color" /> <att