Android:使用动画折叠多个ListView项目

时间:2014-08-03 06:17:36

标签: android listview android-listview

我如何进行动画折叠和隐藏ListView中的某些项目?

 public class Post {
    public String title;
    public boolean isVisible;
 }

目前,我正在设置帖子' isVisible标记为false,调用适配器notifyDataSetChanged(),并设置每个View的可见性以匹配帖子' isVisible中的getView(int position, View convertView, ViewGroup parent)标记。

例如,如果我有帖子:

一个

ç
d
ë

我想要隐藏B和C,我如何动画D滚动到A并隐藏B和C?

1 个答案:

答案 0 :(得分:0)

对于每个项目,您不需要单独的Animator,您可以使用单个ValueAnimator执行此操作。以下代码适用于SDK> = 12。

main.xml中

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

    <FrameLayout
        android:id="@+id/contentFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

outer.xml

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

    <TextView
        android:id="@+id/textAboveList"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click section headers to collapse / expand children" />

    <FrameLayout
        android:id="@+id/listContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

header.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#2F2F2F"
    android:gravity="center_vertical" >

    <TextView
        android:id="@+id/headerText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="8dp"
        android:text=""
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="#FFF"
        android:textStyle="bold"
        android:visibility="visible" />

</LinearLayout>

item.xml

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

    <TextView
        android:id="@+id/itemText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="#FFF"
        android:gravity="center_vertical"
        android:padding="5dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="#FF000000" />

</LinearLayout>

Inner.java

package com.example.sectionheaders;

import java.util.ArrayList;
import java.util.TreeMap;

import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ListFragment;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class Inner extends ListFragment {

    private static final String TAG = "Inner";
    private static final int shrinkExpandAnimationDuration = 500;

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        CustomAdapter adap = (CustomAdapter)getListAdapter();
        String text =(String) adap.getItem(position);
        int itemType = adap.getItemViewType(position);
        if (itemType == CustomAdapter.ITEM){
            Log.i(TAG, "you clicked item: " + text);
        } else {
            int vis = adap.getSectionVisibility(position);
            if (vis == CustomAdapter.SHRINKING || vis == CustomAdapter.EXPANDING){
                Log.i(TAG, "animation already running, ignoring click");
            } else {
                if (vis == CustomAdapter.VISIBLE){
                    Log.i(TAG, "hiding section " + text);
                    adap.setSectionShrinking(position);
                    makeItemHeightShrinker(adap, position, shrinkExpandAnimationDuration).start();
                } else {
                    if (vis == CustomAdapter.INVISIBLE){
                        Log.i(TAG, "showing section " + text);
                        adap.setSectionExpanding(position);
                        makeItemHeightExpander(adap, position, shrinkExpandAnimationDuration).start();
                    }
                }
            }
        }
    }
    ValueAnimator makeItemHeightShrinker(final CustomAdapter adap, final int position, int durationMillis){
        ValueAnimator result = ValueAnimator.ofFloat(0, 1);
        result.setDuration(durationMillis);
        AnimatorUpdateListener shrink = new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator parameterSpawner){
                double lambda = parameterSpawner.getAnimatedFraction();
                double relHeight = 1 - lambda;
                // loop through elements to be hidden, and for each of them, set height to "natural height" * (1-lambda)
                Log.i(TAG, "----- shrinking ------- animated fraction: " + lambda);
                adap.setKidsRelHeightUnderSection(position, relHeight);
                if (lambda == 1){
                    adap.setSectionInvisible(position);
                }
            }
        };
        result.addUpdateListener(shrink);
        return result;
    };
    ValueAnimator makeItemHeightExpander(final CustomAdapter adap, final int position, int durationMillis){
        ValueAnimator result = ValueAnimator.ofFloat(0, 1);
        result.setDuration(durationMillis);
        AnimatorUpdateListener knirhs = new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator parameterSpawner){
                double lambda = parameterSpawner.getAnimatedFraction();
                // loop through elements to be hidden, and for each of them, set height to "natural height" * lambda
                Log.i(TAG, "----- expanding ------- animated fraction: " + lambda);
                adap.setKidsRelHeightUnderSection(position, lambda);
                if (lambda == 1){
                    adap.setSectionVisible(position);
                }
            }
        };
        result.addUpdateListener(knirhs);
        return result;
    };

    public static class CustomAdapter extends BaseAdapter {

        private static final int ITEM = 0;
        private static final int HEADER = 1;

        private static final int VISIBLE   = 0;
        private static final int SHRINKING = 1;
        private static final int INVISIBLE = 2;
        private static final int EXPANDING = 3;

        private static final int ITEM_HEIGHT = 85;

        private ArrayList<String> mData = new ArrayList<String>();
        private TreeMap<Integer, Integer> sectionVisibility = new TreeMap<Integer, Integer>();
        // sectionVisibility.get(i) = VISIBLE iff ith item is a header of a visible section
        // sectionVisibility.get(i) = SHRINKING iff ith item is a header of a section which is in the process of being shrunken by animation
        // sectionVisibility.get(i) = INVISIBLE iff ith item is a header of an invisible section
        // sectionVisibility.get(i) = EXPANDING iff ith item is a header of a section which is in the process of being expanded by animation
        // !sectionVisibility.containsKey(i) iff ith item is not a section header, but regular item
        private TreeMap<Integer, Integer> parentItem = new TreeMap<Integer, Integer>();
        // parentItem(i) = j, iff jth item is the section header above the ith item
        private TreeMap<Integer, Double> kidsRelHeightUnderSection = new TreeMap<Integer, Double>();

        private LayoutInflater mInflater;

        public CustomAdapter(Context context) {
            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
        public int addSection(final String sectionHeader, final ArrayList<String> kids){
            int headerIndex = mData.size();
            sectionVisibility.put(headerIndex, VISIBLE);
            kidsRelHeightUnderSection.put(headerIndex, 1.0);
            mData.add(sectionHeader);
            for (int i=0; i<kids.size(); i++){
                int kidIndex = mData.size();
                String kid = kids.get(i);
                mData.add(kid);
                parentItem.put(kidIndex, headerIndex);
            }
            notifyDataSetChanged();
            return headerIndex;
        }
        public void setKidsRelHeightUnderSection(final int sectionIndex, double relativeHeight){
            kidsRelHeightUnderSection.put(sectionIndex, relativeHeight);
            notifyDataSetChanged();
        }
        public void setSectionVisible(int sectionIndex){
            if (!sectionVisibility.containsKey(sectionIndex)){
                Log.wtf(TAG, "bad programmer");
                return;
            }
            sectionVisibility.put(sectionIndex, VISIBLE);
            notifyDataSetChanged();
        }
        public void setSectionInvisible(int sectionIndex){
            if (!sectionVisibility.containsKey(sectionIndex)){
                Log.wtf(TAG, "bad programmer");
                return;
            }
            sectionVisibility.put(sectionIndex, INVISIBLE);
            notifyDataSetChanged();
        }
        public void setSectionShrinking(int sectionIndex){
            if (!sectionVisibility.containsKey(sectionIndex)){
                Log.wtf(TAG, "bad programmer");
                return;
            }
            sectionVisibility.put(sectionIndex, SHRINKING);
        }
        public void setSectionExpanding(int sectionIndex){
            if (!sectionVisibility.containsKey(sectionIndex)){
                Log.wtf(TAG, "bad programmer");
                return;
            }
            sectionVisibility.put(sectionIndex, EXPANDING);
        }
        public int getSectionVisibility(final int sectionIndex){
            return sectionVisibility.get(sectionIndex);
        }

        @Override
        public int getItemViewType(int position) {
            return sectionVisibility.containsKey(position) ? HEADER : ITEM;
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public int getCount() {
            return mData.size();
        }

        @Override
        public String getItem(int position) {
            return mData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
        private void adjustTextView(TextView tv, int state, int height, int position){
            if (state == INVISIBLE){
                tv.setVisibility(View.GONE);
                return;
            }
            tv.setVisibility(View.VISIBLE);
            LayoutParams lp = tv.getLayoutParams();
            if (lp == null){
                lp = new LayoutParams(LayoutParams.MATCH_PARENT, height);
            } else {
                lp.height = height;
            }
            tv.setLayoutParams(lp);
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            int rowType = getItemViewType(position);
            if (convertView == null) {
                holder = new ViewHolder();
                switch (rowType) {
                case ITEM:
                    convertView = mInflater.inflate(R.layout.item, parent, false);
                    holder.textView = (TextView) convertView.findViewById(R.id.itemText);
                    break;
                case HEADER:
                    convertView = mInflater.inflate(R.layout.header, parent, false);
                    holder.textView = (TextView) convertView.findViewById(R.id.headerText);
                    break;
                }
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            holder.textView.setText(mData.get(position));
            if (rowType == ITEM){
                Integer myParentItem = parentItem.get(position);
                int itemVisibility = sectionVisibility.get(myParentItem);
                //View parentView = (View)holder.textView.getParent();
                int newHeight = LayoutParams.WRAP_CONTENT;
                if (itemVisibility == SHRINKING || itemVisibility == EXPANDING){
                    newHeight = (int)Math.round(kidsRelHeightUnderSection.get(myParentItem) * ITEM_HEIGHT);
                }
                adjustTextView(holder.textView, itemVisibility, newHeight, position);
            }
            return convertView;
        }

        public static class ViewHolder {
            public TextView textView;
        }

    }

}

Outer.java

package com.example.sectionheaders;

import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Outer extends Fragment {

    public Inner inner;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.outer, container, false);
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.listContainer, inner, "inner").commit();
        return v;
    }

}

MainActivity.java

package com.example.sectionheaders;

import java.util.ArrayList;
import java.util.Arrays;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

    Fragment content;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        FragmentManager fm = getFragmentManager();
        content = fm.findFragmentById(R.id.contentFragment);
        if (content == null){
            Log.i("Main", "content is null");
            Outer outer = new Outer();
            outer.inner = new Inner();
            FragmentTransaction ft = fm.beginTransaction();
            ft.add(R.id.contentFragment, outer, "blah").commit();
            content = outer;
            Inner.CustomAdapter adap = new Inner.CustomAdapter(this);
            adap.addSection("A", new ArrayList<String>(Arrays.asList(new String[]{"A1", "A2", "A3"})));
            adap.addSection("B", new ArrayList<String>(Arrays.asList(new String[]{"B1", "B2", "B3"})));
            adap.addSection("C", new ArrayList<String>(Arrays.asList(new String[]{"C1", "C2", "C3"})));
            adap.addSection("D", new ArrayList<String>(Arrays.asList(new String[]{"D1", "D2", "D3"})));
            adap.addSection("E", new ArrayList<String>(Arrays.asList(new String[]{"E1", "E2", "E3"})));
            adap.addSection("F", new ArrayList<String>(Arrays.asList(new String[]{"F1", "F2", "F3"})));
            adap.addSection("G", new ArrayList<String>(Arrays.asList(new String[]{"G1", "G2", "G3"})));
            adap.addSection("H", new ArrayList<String>(Arrays.asList(new String[]{"H1", "H2", "H3"})));
            adap.addSection("I", new ArrayList<String>(Arrays.asList(new String[]{"I1", "I2", "I3"})));
            adap.addSection("J", new ArrayList<String>(Arrays.asList(new String[]{"J1", "J2", "J3"})));
            adap.addSection("K", new ArrayList<String>(Arrays.asList(new String[]{"K1", "K2", "K3"})));
            adap.addSection("L", new ArrayList<String>(Arrays.asList(new String[]{"L1", "L2", "L3"})));
            adap.addSection("M", new ArrayList<String>(Arrays.asList(new String[]{"M1", "M2", "M3"})));
            outer.inner.setListAdapter(adap);
        } else {
            Log.i("Main", "content not null");
        }
    }
}