zoukankan      html  css  js  c++  java
  • 仿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;
                    }
                }  
            });
  • 相关阅读:
    【Java TCP/IP Socket】应用程序协议中消息的成帧与解析(含代码)
    【Java TCP/IP Socket】UDP Socket(含代码)
    【Java TCP/IP Socket】TCP Socket(含代码)
    Java NIO —— Buffer(缓冲区)
    java.nio.ByteBuffer 以及flip,clear及rewind区别
    Java文件IO操作应该抛弃File拥抱Paths和Files
    Java网络编程之InetAddress和URL
    Java enum的用法详解
    PAT Basic 1083 是否存在相等的差 (20 分)
    PAT Basic 1082 射击比赛 (20 分)
  • 原文地址:https://www.cnblogs.com/Couch-potato/p/3756716.html
Copyright © 2011-2022 走看看