【问题标题】:FliCard and ListView A strange visibility bug in AndroidFliCard 和 ListView Android 中一个奇怪的可见性错误
【发布时间】:2015-08-28 08:38:39
【问题描述】:

我正在尝试在 ListView 中为我的项目实现 FlipCard 行为,错误是我的 convertView 不会根据我在 getView 方法中设置的可见性更新其可见性状态。就像没有人关心我的能见度变化一样。
重现问题:单击项目图片(太阳、云...),它将翻转项目并呈现其背面。然后向上或向下滚动,直到翻转的convertView被未翻转的View重用。未翻转的视图将不再显示其内容。
第一项应该显示其内容,但它什么也不显示,因为使用的 convertView(由 getView 参数给出的)在上次使用时将其可见性设置为 GONE。

你可以在这里找到完整的项目:
https://github.com/MathiasSeguy-Android2EE/ForecastYahooRest 你必须查看分支“flipcard”

所以 ArrayAdapter 涉及到:

package com.android2ee.formation.restservice.sax.forecastyahoo.view.forecast.arrayadpater;

import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.android2ee.formation.restservice.sax.forecastyahoo.R;
import com.android2ee.formation.restservice.sax.forecastyahoo.transverse.model.YahooForcast;

import java.util.List;

/**
 * @author Mathias Seguy (Android2EE)
 * @goals
 *        This class aims to display the forecast in the listView
 */
public class ForecastArrayAdapter extends ArrayAdapter<YahooForcast> {

    /**
     * Handler to launch the animation runnable
     */
    Handler handlerForAnimation;
    /**
     * To know when the item is flipped or not
     * When flipped it show us its back side else its front side
     */
    SparseBooleanArray isFlipped;

    /**
     * To detect the first launch
     */
    int notifyDataSetChangedCallsNumber = 0;
    /**
     * The layout inflater
     */
    LayoutInflater inflater;
    /**
     * The Context
     */
    Context ctx;
    /**
     * To know if the device is postJellyBean or not
     */
    boolean postJB;
    /**
     * To know if the device is postHoneyComb or not
     */
    boolean postHC;
    /**
     * Drawable used for the backside of the item
     */
    Drawable[] drawableBackground;


    /**
     *
     * @param context
     * @param forecast
     */
    public ForecastArrayAdapter(Context context, List<YahooForcast> forecast) {
        super(context, R.layout.item_forecast, forecast);
        inflater = LayoutInflater.from(context);
        ctx = context;
        postJB = context.getResources().getBoolean(R.bool.postJB);
        postHC = context.getResources().getBoolean(R.bool.postHC);
        //instantiate the handler
        handlerForAnimation = new Handler();
        isFlipped=new SparseBooleanArray();
        drawableBackground=new Drawable[5];
        drawableBackground[0]=context.getResources().getDrawable(R.drawable.back1);
        drawableBackground[1]=context.getResources().getDrawable(R.drawable.back2);
        drawableBackground[2]=context.getResources().getDrawable(R.drawable.back3);
        drawableBackground[3]=context.getResources().getDrawable(R.drawable.back4);
        drawableBackground[4]=context.getResources().getDrawable(R.drawable.back5);
    }

    /**
     * Private static better than temp
     */
    private static View rowView;

    /**
     * Private static better than temp
     */
    private static YahooForcast forcast;
    /**
     * Private static better than temp
     */
    private static ViewHolder viewHolder;

    /*
     * (non-Javadoc)
     * 
     * @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
     */
    @SuppressLint("NewApi")
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.e("ForecastArrayAdapter","getView "+position);
        rowView = convertView;
        forcast = getItem(position);
        if (rowView == null) {
            // always add the layout, the parent and false
            rowView = inflater.inflate(R.layout.item_forecast, null, false);
            ViewHolder vh = new ViewHolder(rowView,position);
            rowView.setTag(vh);
        }
        viewHolder = (ViewHolder) rowView.getTag();
        //used for animation
        viewHolder.currentPosition=position;
        if (postJB) {
            viewHolder.getImvIcon().setBackground(forcast.getImage());
            viewHolder.getImvBack().setBackground(drawableBackground[position%5]);
        } else {
            viewHolder.getImvIcon().setBackgroundDrawable(forcast.getImage());
            viewHolder.getImvBack().setBackgroundDrawable(drawableBackground[position % 5]);
        }
        if (forcast.getDate() != null) {
            viewHolder.getTxvDate().setText(DateFormat.format("E dd MMM", forcast.getDate()));
        } else {
            viewHolder.getTxvDate().setText("unknown");
        }

        viewHolder.getTxvTendance().setText(forcast.getTendance());
        if (forcast.getTempMax() != -1000) {
            viewHolder.getTxvMax().setVisibility(View.VISIBLE);
            viewHolder.getTxvMin().setVisibility(View.VISIBLE);
            viewHolder.getTxvMax().setText(ctx.getString(R.string.max, forcast.getTempMax()));
            viewHolder.getTxvMin().setText(ctx.getString(R.string.min, forcast.getTempMin()));
        } else {
            viewHolder.getTxvMax().setVisibility(View.GONE);
            viewHolder.getTxvMin().setVisibility(View.GONE);
        }
        if (forcast.getTemp() != -1000) {
            viewHolder.getTxvCurrent().setVisibility(View.VISIBLE);
            viewHolder.getTxvCurrent().setText(ctx.getString(R.string.temp, forcast.getTemp()));
        } else {
            viewHolder.getTxvCurrent().setVisibility(View.GONE);
        }
        // launch animations to show the update to the user (not the first time but only when refreshing)
        //because the first time is not an update, it's just loading data from db
        if (notifyDataSetChangedCallsNumber >=2) {
            viewHolder.launchUpdateAnimation(notifyDataSetChangedCallsNumber);
        }
        //and finally manage the visibility of the side : front or back side is visible
        manageSideVisibility(position);
        return rowView;
    }

    /* (non-Javadoc)
     * @see android.widget.ArrayAdapter#notifyDataSetChanged()
     */
    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
        notifyDataSetChangedCallsNumber++;
    }
    /**************************************************
     * Flipping Animation tricks
     * **************************************************
     */

    /**
     * If the element has been flipped, flip it else set it has not flipped
     * @param position
     */
    private void manageSideVisibility(int position){
        if(isFlipped.get(position)){
            //the backside is visible
            viewHolder.getImvBack().setVisibility(View.VISIBLE);
            viewHolder.getLinRoot().setVisibility(View.GONE);
        }else{
            //the ffront is visible
            viewHolder.getImvBack().setVisibility(View.GONE);
            viewHolder.getLinRoot().setVisibility(View.VISIBLE);
        }
    }
    /******************************************************************************************/
    /** Runnable for animation **************************************************************************/
    /******************************************************************************************/
    public class MyRunnable implements Runnable {
        /**
         * The viewHolder that contains the view to animate
         */
        private ViewHolder vh;

        public MyRunnable(ViewHolder vh) {
            this.vh=vh;
        }

        public void run() {
            vh.animateUpdate();
        }
    }

    /******************************************************************************************/
    /** The ViewHolder pattern **************************************************************************/
    /******************************************************************************************/

    private class ViewHolder {
        View view;
        LinearLayout linRoot;
        TextView txvDate;
        TextView txvTendance;
        ImageView imvIcon;
        TextView txvCurrent;
        TextView txvMin;
        TextView txvMax;
        TextView txvUpdating;
        //For Update animation
        Animation updateAnimation;
        MyRunnable animationRunnable;
        int dataTimeStamp=0;
        //For animatibbbbbbon
        ImageView imvBack;
        int currentPosition;
        //PostHoneyComb
        Animator flipAnimatorIn;
        Animator flipAnimatorOut;
        Animator reverseFlipAnimatorIn;
        Animator reverseFlipAnimatorOut;
        AnimatorSet setFlip;
        AnimatorSet setReverse;
        //PreHoneyComb
        Animation animInLegacy;
        Animation animOutLegacy;
        int id;
        /**
         * @param rowview
         */
        private ViewHolder(View rowview,int position) {
            super();
            this.view = rowview;
            animationRunnable=new MyRunnable(this);
            id=position;
        }

        /**
         * @return the txvDate
         */
        public final TextView getTxvDate() {
            if (null == txvDate) {
                txvDate = (TextView) view.findViewById(R.id.date);
            }
            return txvDate;
        }

        /**
         * @return the txvTendance
         */
        public final TextView getTxvTendance() {
            if (null == txvTendance) {
                txvTendance = (TextView) view.findViewById(R.id.txv_tendance);
            }
            return txvTendance;
        }
        /**
         * @return the imvIcon
         */
        public final ImageView getImvIcon() {
            if (null == imvIcon) {
                imvIcon = (ImageView) view.findViewById(R.id.icon);
                imvIcon.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if(postHC){
                            animateItem();
                        }else{
                            flipItemLegacy();
                        }
                    }
                });
            }
            return imvIcon;
        }
        /**
         * @return the imvBack
         */
        public final ImageView getImvBack() {
            if (null == imvBack) {
                imvBack = (ImageView) view.findViewById(R.id.imvBack);
                imvBack.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if(postHC){
                            reverseAnimateItem();
                        }else{
                            reverseItemLegacy();
                        }
                    }
                });
            }
            return imvBack;
        }
        /**
         * @return the txvTendance
         */
        public final TextView getTxvUpdating() {
            if (null == txvUpdating) {
                txvUpdating = (TextView) view.findViewById(R.id.txv_updating);
            }
            return txvUpdating;
        }
        /**
         * @return the txvCurrent
         */
        public final TextView getTxvCurrent() {
            if (null == txvCurrent) {
                txvCurrent = (TextView) view.findViewById(R.id.txv_current);
                txvCurrent.setText("Toto");
            }
            return txvCurrent;
        }

        /**
         * @return the txvMin
         */
        public final TextView getTxvMin() {
            if (null == txvMin) {
                txvMin = (TextView) view.findViewById(R.id.txv_min);
            }
            return txvMin;
        }

        /**
         * @return the txvMax
         */
        public final TextView getTxvMax() {
            if (null == txvMax) {
                txvMax = (TextView) view.findViewById(R.id.txv_max);
            }
            return txvMax;
        }

        /**
         * @return the linRoot
         */
        public final LinearLayout getLinRoot() {
            if (null == linRoot) {
                linRoot = (LinearLayout) view.findViewById(R.id.lay_item);
            }
            return linRoot;
        }
        /**************************************************
         * Animation tricks
         * All Version
         * The UpdateAnimation
         * **************************************************
         */
        /**
         * Launch the Update Animation
         */
        public void animateUpdate() {
            if (updateAnimation==null) {
                updateAnimation=AnimationUtils.loadAnimation(getContext(), R.anim.anim_item_updated);
                updateAnimation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        getTxvUpdating().setVisibility(View.VISIBLE);}
                    @Override
                    public void onAnimationEnd(Animation animation) {
                            getTxvUpdating().setVisibility(View.GONE);
                    }
                    @Override
                    public void onAnimationRepeat(Animation animation) {}
                });
            }

            if (isFlipped.get(currentPosition)) {
                getImvBack().startAnimation(updateAnimation);
            } else {
                //run it
                getLinRoot().startAnimation(updateAnimation);
            }
        }
        /**
         * Launch the Update Animation
         */
        public void launchUpdateAnimation(int ndscCallsNumber){
            if(dataTimeStamp!=ndscCallsNumber) {
                //it means an already runnable is associated with this item
                //we need to remove it (else it gonna run the animation twice
                //and it's strange for the user)
                handlerForAnimation.removeCallbacks(animationRunnable);
                //then launched it in few seconds
                handlerForAnimation.postDelayed(animationRunnable, 300 * currentPosition);
                Log.e("tag", "launchUpdateAnimation in " + 300 * currentPosition + " for item " + currentPosition);
                dataTimeStamp=ndscCallsNumber;
            }
        }

        /**************************************************
         * Animation tricks
         * preHoneyComb : 4 Gingerbread in fact
         * **************************************************
         */
        private void flipItemLegacy(){
            if(animInLegacy==null){
                animInLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_in);
            }
            if(animOutLegacy==null){
                animOutLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_out);
            }
            animOutLegacy.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {}
                public void onAnimationEnd(Animation animation) {
                    Log.e("ForecastArrayAdapter","flipItemLegacy onAnimationEnd called ");
                    getImvBack().setVisibility(View.VISIBLE);
                    getImvBack().startAnimation(animInLegacy);
                    getLinRoot().setVisibility(View.GONE);
                }
                public void onAnimationRepeat(Animation animation) {}
            });
            getLinRoot().startAnimation(animOutLegacy);

            isFlipped.put(currentPosition,true);

        }
        private void reverseItemLegacy(){
            if(animInLegacy==null){
                animInLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_in);
            }
            if(animOutLegacy==null){
                animOutLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_out);
            }
            animInLegacy.setAnimationListener(new Animation.AnimationListener() {
                public void onAnimationStart(Animation animation) {}
                public void onAnimationEnd(Animation animation) {
                    getLinRoot().setVisibility(View.VISIBLE);
                    getLinRoot().startAnimation(animInLegacy);
                    getImvBack().setVisibility(View.GONE);
                }
                public void onAnimationRepeat(Animation animation) {}
            });
            getImvBack().startAnimation(animOutLegacy);

            isFlipped.put(currentPosition,false);

        }

        /**************************************************
         * Animation tricks
         * postHoneyComb
         * **************************************************
         */

        @SuppressLint("NewApi")
        private void animateItem(){
            initialiseFlipAnimator();
            setFlip.start();
            isFlipped.put(currentPosition,true);
        }
        @SuppressLint("NewApi")
        private void reverseAnimateItem(){
            initialiseReverseFlipAnimator();
            setReverse.start();
            isFlipped.put(currentPosition,false);
        }
        @SuppressLint("NewApi")
        private void initialiseReverseFlipAnimator() {
            if(reverseFlipAnimatorIn==null){
                reverseFlipAnimatorIn= AnimatorInflater.loadAnimator(getContext(), R.animator.flip_in);
                reverseFlipAnimatorIn.addListener(new SimpleAnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        getLinRoot().setVisibility(View.VISIBLE);

                    }

                    @Override
                    public void onAnimationEnd(Animator animation) {
                        getImvBack().setVisibility(View.GONE);
                    }
                });
                reverseFlipAnimatorIn.setTarget(getLinRoot());
                reverseFlipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
                reverseFlipAnimatorOut.setTarget(imvBack);
                setReverse=new AnimatorSet();
                setReverse.playTogether(reverseFlipAnimatorIn,reverseFlipAnimatorOut);
            }
        }

        @SuppressLint("NewApi")
        private void initialiseFlipAnimator(){
            Log.e("ForecastArrayAdapter","initialiseFlipAnimator");
            if(flipAnimatorIn==null){
                flipAnimatorIn= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_in);
                flipAnimatorIn.setTarget(getImvBack());
                flipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
                flipAnimatorOut.setTarget(getLinRoot());
                flipAnimatorIn.addListener(new SimpleAnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        Log.e("tag","anim onAnimationStart");
                        getImvBack().setVisibility(View.VISIBLE);
                    }
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        Log.e("tag","anim onAnimationEnd");
                        getLinRoot().setVisibility(View.GONE);
                    }
                });
                setFlip=new AnimatorSet();
                setFlip.playTogether(flipAnimatorIn, flipAnimatorOut);
            }
        }
    }

    @SuppressLint("NewApi")
    public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
        /**
         * <p>Notifies the start of the animation.</p>
         *
         * @param animation The started animation.
         */
        public abstract void onAnimationStart(Animator animation);

        /**
         * <p>Notifies the end of the animation. This callback is not invoked
         * for animations with repeat count set to INFINITE.</p>
         *
         * @param animation The animation which reached its end.
         */
        public abstract void onAnimationEnd(Animator animation) ;

        /**
         * <p>Notifies the cancellation of the animation. This callback is not invoked
         * for animations with repeat count set to INFINITE.</p>
         *
         * @param animation The animation which was canceled.
         */
        @Override
        public void onAnimationCancel(Animator animation) {
            onAnimationEnd(animation);
        }

        /**
         * <p>Notifies the repetition of the animation.</p>
         *
         * @param animation The animation which was repeated.
         */
        @Override
        public void onAnimationRepeat(Animator animation) {
            onAnimationStart(animation);
        }
    }
}

好的,我深入研究了那个错误,但我仍然不明白(我有很多日志)所以 我的问题就在这里,视图告诉我,它是可见的,

但它没有显示

重现问题的简单方法,进入横向模式,翻转前两项,滚动到列表末尾。

非常感谢那些试图回答的人。
马蒂亚斯

【问题讨论】:

    标签: android listview animation android-listview visibility


    【解决方案1】:

    优皮,我找到了!!
    总结
    好的,对我来说,这是一个错误或过度优化的行为。 因此,当我的视图翻转时,我隐藏前面以显示后面,而未翻转时,我隐藏后面以显示前面。 问题是如果我在与项目 n1 关联的视图 v1 中隐藏前面。我滚动。然后这个视图在getView 方法中作为convertView 被重用为n2 项。但是对于项目 n2,我们显示前面...
    并且出现了错误:前视图已几乎被删除/垃圾收集或其他任何东西,但不再存在。它响应函数的调用,但它的内部状态是错误的。所以它告诉你,我是可见的,但它不是,它是一个幽灵。

    我的理解:
    我的想法(这是一个假设) 所以这里的重点是对 ListView 的过度优化: 当视图进入转换视图池时,系统会销毁处于“可见性消失”状态的资源。我的意思是具有 Visibility=Gone 的 ViewGroup 根的视图。

    我的理解是错误的 所以 Romain Guy 告诉我,“ListView 不会破坏 GONE 视图。特别是因为它不查看回收视图的子视图。如果你可以在 View 上调用一个方法,它显然没有被 GC。它可能是UI Toolkit 绘图或适配器中的错误。"
    .... 好的,我将继续深入研究我的问题以了解这一点。

    解决方案
    所以解决方案很明显,我需要两个转换视图池,一个默认为正面可见,另一个默认为背面可见。所以我创建了两个布局,一个前面可见,另一个后面可见,我使用 getViewTypeCount() 和 getItemViewType(int position) 方法,3分钟后它开始工作了。

    结论
    总而言之,当您在 ListView 中隐藏和显示项目中的元素时,您需要定义与 convertViews 池一样多的配置。
    :( 我很难过理解这一点,如果没有错误来证明这一点,我永远不会相信。

    项目 你可以在这里找到完整的项目: https://github.com/MathiasSeguy-Android2EE/ForecastYahooRest 并且您必须检查分支“翻转卡”是否已更新。

    守则:

    package com.android2ee.formation.restservice.sax.forecastyahoo.view.forecast.arrayadpater;
    
    import android.animation.Animator;
    import android.animation.AnimatorInflater;
    import android.animation.AnimatorSet;
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.os.Handler;
    import android.text.format.DateFormat;
    import android.util.Log;
    import android.util.SparseBooleanArray;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.animation.Animation;
    import android.view.animation.AnimationUtils;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    import com.android2ee.formation.restservice.sax.forecastyahoo.R;
    import com.android2ee.formation.restservice.sax.forecastyahoo.transverse.model.YahooForcast;
    
    import java.util.List;
    
    /**
     * @author Mathias Seguy (Android2EE)
     * @goals This class aims to display the forecast in the listView
     */
    public class ForecastArrayAdapter extends ArrayAdapter<YahooForcast> {
    
        /**
         * Handler to launch the animation runnable
         */
        Handler handlerForAnimation;
        /**
         * To know when the item is flipped or not
         * When flipped it show us its back side else its front side
         */
        SparseBooleanArray isFlipped;
    
        /**
         * To detect the first launch
         */
        int notifyDataSetChangedCallsNumber = 0;
        /**
         * The layout inflater
         */
        LayoutInflater inflater;
        /**
         * The Context
         */
        Context ctx;
        /**
         * To know if the device is postJellyBean or not
         */
        boolean postJB;
        /**
         * To know if the device is postHoneyComb or not
         */
        boolean postHC;
        /**
         * Drawable used for the backside of the item
         */
        Drawable[] drawableBackground;
        int[] drawableRes;
    
    
        /**
         * @param context
         * @param forecast
         */
        public ForecastArrayAdapter(Context context, List<YahooForcast> forecast) {
            super(context, R.layout.item_forecast, forecast);
            inflater = LayoutInflater.from(context);
            ctx = context;
            postJB = context.getResources().getBoolean(R.bool.postJB);
            postHC = context.getResources().getBoolean(R.bool.postHC);
            //instantiate the handler
            handlerForAnimation = new Handler();
            isFlipped = new SparseBooleanArray(5);
            drawableRes = new int[5];
            drawableRes[0] = R.drawable.back1;
            drawableRes[1] = R.drawable.back2;
            drawableRes[2] = R.drawable.back3;
            drawableRes[3] = R.drawable.back4;
            drawableRes[4] = R.drawable.back5;
            drawableBackground = new Drawable[5];
            drawableBackground[0] = context.getResources().getDrawable(R.drawable.back1);
            drawableBackground[1] = context.getResources().getDrawable(R.drawable.back2);
            drawableBackground[2] = context.getResources().getDrawable(R.drawable.back3);
            drawableBackground[3] = context.getResources().getDrawable(R.drawable.back4);
            drawableBackground[4] = context.getResources().getDrawable(R.drawable.back5);
        }
    
        /**
         * Private static better than temp
         */
        private static View rowView;
    
        /**
         * Private static better than temp
         */
        private static YahooForcast forcast;
        /**
         * Private static better than temp
         */
        private static ViewHolder viewHolder;
    
        /*
         * (non-Javadoc)
         * 
         * @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup)
         */
        @SuppressLint("NewApi")
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            rowView = convertView;
            forcast = getItem(position);
            if (rowView == null) {
                if(getItemViewType(position)==0){
                    //then used the layout of flipped view
                    // always add the layout, the parent and false
                    rowView = inflater.inflate(R.layout.item_forecast, null, false);
                }else{
                    //then used the layout for not flipped view
                    // always add the layout, the parent and false
                    rowView = inflater.inflate(R.layout.item_forecast, null, false);
                }
    
                ViewHolder vh = new ViewHolder(rowView, position);
                rowView.setTag(vh);
            }
            viewHolder = (ViewHolder) rowView.getTag();
            //used for animation
            viewHolder.setCurrentPosition(position);
            if (postJB) {
                viewHolder.getImvIcon().setBackground(forcast.getImage());
                viewHolder.getImvBack().setBackground(drawableBackground[position % 5]);
            } else {
                viewHolder.getImvIcon().setBackgroundDrawable(forcast.getImage());
                //if you don't use setBackgroundResource nothing happens on Gingerbread (deep sadness sometimes)
                viewHolder.getImvBack().setBackgroundResource(drawableRes[position % 5]);
            }
            if (forcast.getDate() != null) {
                viewHolder.getTxvDate().setText(DateFormat.format("E dd MMM", forcast.getDate()));
            } else {
                viewHolder.getTxvDate().setText("unknown");
            }
    
            viewHolder.getTxvTendance().setText(forcast.getTendance());
            if (forcast.getTempMax() != -1000) {
                viewHolder.getTxvMax().setVisibility(View.VISIBLE);
                viewHolder.getTxvMin().setVisibility(View.VISIBLE);
                viewHolder.getTxvMax().setText(ctx.getString(R.string.max, forcast.getTempMax()));
                viewHolder.getTxvMin().setText(ctx.getString(R.string.min, forcast.getTempMin()));
            } else {
                viewHolder.getTxvMax().setVisibility(View.GONE);
                viewHolder.getTxvMin().setVisibility(View.GONE);
            }
            if (forcast.getTemp() != -1000) {
                viewHolder.getTxvCurrent().setVisibility(View.VISIBLE);
                viewHolder.getTxvCurrent().setText(ctx.getString(R.string.temp, forcast.getTemp()));
            } else {
                viewHolder.getTxvCurrent().setVisibility(View.GONE);
            }
            // launch animations to show the update to the user (not the first time but only when refreshing)
            //because the first time is not an update, it's just loading data from db
            if (notifyDataSetChangedCallsNumber >= 2) {
                viewHolder.launchUpdateAnimation(notifyDataSetChangedCallsNumber);
            }
            //and finally manage the visibility of the side : front or back side is visible
            return rowView;
        }
    
        /* (non-Javadoc)
         * @see android.widget.ArrayAdapter#notifyDataSetChanged()
         */
        @Override
        public void notifyDataSetChanged() {
            super.notifyDataSetChanged();
            notifyDataSetChangedCallsNumber++;
        }
        /***********************************************************
         *  Trying to fix the bug of the visible view not displayed
         *  by managing 2 pools of views
         **********************************************************/
        @Override
        public int getViewTypeCount() {
            //Two pools: the one for flipped views, the other not flipped views
            return 2;
        }
    
        @Override
        public int getItemViewType(int position) {
            //If the View is flipped then pick in the pool 0
            //else pick in the pool 1
            return isFlipped.get(position)?0:1;
        }
    
        /**************************************************
         * Flipping Animation tricks
         * **************************************************
         */
    
        /**
         * If the element has been flipped, flip it else set it has not flipped
         *
         * @param position
         */
        private void manageSideVisibility(int position) {
            if (isFlipped.get(position)) {
                Log.e("ForecastArrayAdapter","ImvBack Visible"+position);
                //the backside is visible
                viewHolder.getImvBack().setVisibility(View.VISIBLE);
                viewHolder.getLinRoot().setVisibility(View.GONE);
                viewHolder.getImvBack().invalidate();
            } else {
                Log.e("ForecastArrayAdapter","ImvBack GONE"+position);
                //the ffront is visible
                viewHolder.getImvBack().setVisibility(View.GONE);
                viewHolder.getLinRoot().setVisibility(View.VISIBLE);
                viewHolder.getLinRoot().invalidate();
            }
            printView("ImvBack",viewHolder.getImvBack(),position);
            printView("LinRoot",viewHolder.getLinRoot(),position);
        }
        public void printView(String viewName,View view,int position){
            Log.e("ForecastArrayAdapter","("+viewName+","+position+") getWidth()="+view.getWidth());
            Log.e("ForecastArrayAdapter","("+viewName+","+position+") getHeight()="+view.getHeight());
            Log.e("ForecastArrayAdapter", "(" + viewName + "," + position + ") getHeight()=" + view.getBackground());
            Log.e("ForecastArrayAdapter", "(" + viewName + "," + position + ") getVisibility()=" + getVisibility(view));
        }
    
        public String getVisibility(View view){
            switch (view.getVisibility()){
                case View.GONE:
                    return "GONE";
                case View.VISIBLE:
                    return "VISIBLE";
                case View.INVISIBLE:
                    return "INVISIBLE";
            }
            return "Unknow";
        }
        /******************************************************************************************/
        /** Runnable for animation **************************************************************************/
        /**
         * **************************************************************************************
         */
        public class MyRunnable implements Runnable {
            /**
             * The viewHolder that contains the view to animate
             */
            private ViewHolder vh;
    
            public MyRunnable(ViewHolder vh) {
                this.vh = vh;
            }
    
            public void run() {
                vh.animateUpdate();
            }
        }
    
        public void printisFlipp(String methodName) {
            for (int i = 0;i < 5; i++){
                Log.e("ForecastArrayAdapter", "in("+methodName+") isFlipped[" + i + "]=" + isFlipped.get(i));
            }
        }
        /******************************************************************************************/
        /** The ViewHolder pattern **************************************************************************/
        /******************************************************************************************/
    
        private class ViewHolder {
            View view;
            LinearLayout linRoot;
            TextView txvDate;
            TextView txvTendance;
            ImageView imvIcon;
            TextView txvCurrent;
            TextView txvMin;
            TextView txvMax;
            TextView txvUpdating;
            //For Update animation
            Animation updateAnimation;
            MyRunnable animationRunnable;
            int dataTimeStamp=0;
            //For animation
            ImageView imvBack;
            int currentPosition;
    
            public int getCurrentPosition() {
                return currentPosition;
            }
    
            public void setCurrentPosition(int currentPosition) {
                this.currentPosition = currentPosition;
            }
    
            //PostHoneyComb
            Animator flipAnimatorIn;
            Animator flipAnimatorOut;
            Animator reverseFlipAnimatorIn;
            Animator reverseFlipAnimatorOut;
            AnimatorSet setFlip;
            AnimatorSet setReverse;
            //PreHoneyComb
            Animation animInLegacy;
            Animation animOutLegacy;
            int id;
            /**
             * @param rowview
             */
            private ViewHolder(View rowview,int position) {
                super();
                this.view = rowview;
                animationRunnable=new MyRunnable(this);
                id=position;
            }
    
            /**
             * @return the txvDate
             */
            public final TextView getTxvDate() {
                if (null == txvDate) {
                    txvDate = (TextView) view.findViewById(R.id.date);
                }
                return txvDate;
            }
    
            /**
             * @return the txvTendance
             */
            public final TextView getTxvTendance() {
                if (null == txvTendance) {
                    txvTendance = (TextView) view.findViewById(R.id.txv_tendance);
                }
                return txvTendance;
            }
            /**
             * @return the imvIcon
             */
            public final ImageView getImvIcon() {
                if (null == imvIcon) {
                    imvIcon = (ImageView) view.findViewById(R.id.icon);
                    imvIcon.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if(postHC){
                                animateItem();
                            }else{
                                flipItemLegacy();
                            }
                        }
                    });
                }
                return imvIcon;
            }
            /**
             * @return the imvBack
             */
            public final ImageView getImvBack() {
                if (null == imvBack) {
                    imvBack = (ImageView) view.findViewById(R.id.imvBack);
                    imvBack.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if(postHC){
                                reverseAnimateItem();
                            }else{
                                reverseItemLegacy();
                            }
                        }
                    });
                }
                return imvBack;
            }
            /**
             * @return the txvTendance
             */
            public final TextView getTxvUpdating() {
                if (null == txvUpdating) {
                    txvUpdating = (TextView) view.findViewById(R.id.txv_updating);
                }
                return txvUpdating;
            }
            /**
             * @return the txvCurrent
             */
            public final TextView getTxvCurrent() {
                if (null == txvCurrent) {
                    txvCurrent = (TextView) view.findViewById(R.id.txv_current);
                    txvCurrent.setText("Toto");
                }
                return txvCurrent;
            }
    
            /**
             * @return the txvMin
             */
            public final TextView getTxvMin() {
                if (null == txvMin) {
                    txvMin = (TextView) view.findViewById(R.id.txv_min);
                }
                return txvMin;
            }
    
            /**
             * @return the txvMax
             */
            public final TextView getTxvMax() {
                if (null == txvMax) {
                    txvMax = (TextView) view.findViewById(R.id.txv_max);
                }
                return txvMax;
            }
    
            /**
             * @return the linRoot
             */
            public final LinearLayout getLinRoot() {
                if (null == linRoot) {
                    linRoot = (LinearLayout) view.findViewById(R.id.lay_item);
                }
                return linRoot;
            }
            /**************************************************
             * Animation tricks
             * All Version
             * The UpdateAnimation
             * **************************************************
             */
            /**
             * Launch the Update Animation
             */
            public void animateUpdate() {
                if (updateAnimation==null) {
                    updateAnimation=AnimationUtils.loadAnimation(getContext(), R.anim.anim_item_updated);
                    updateAnimation.setAnimationListener(new Animation.AnimationListener() {
                        @Override
                        public void onAnimationStart(Animation animation) {
                            getTxvUpdating().setVisibility(View.VISIBLE);}
                        @Override
                        public void onAnimationEnd(Animation animation) {
                                getTxvUpdating().setVisibility(View.GONE);
                        }
                        @Override
                        public void onAnimationRepeat(Animation animation) {}
                    });
                }
    
                if (isFlipped.get(currentPosition)) {
                    getImvBack().startAnimation(updateAnimation);
                } else {
                    //run it
                    getLinRoot().startAnimation(updateAnimation);
                }
            }
            /**
             * Launch the Update Animation
             */
            public void launchUpdateAnimation(int ndscCallsNumber){
                if(dataTimeStamp!=ndscCallsNumber) {
                    //it means an already runnable is associated with this item
                    //we need to remove it (else it gonna run the animation twice
                    //and it's strange for the user)
                    handlerForAnimation.removeCallbacks(animationRunnable);
                    //then launched it in few seconds
                    handlerForAnimation.postDelayed(animationRunnable, 300 * currentPosition);
                    dataTimeStamp=ndscCallsNumber;
                }
            }
    
            /**************************************************
             * Animation tricks
             * preHoneyComb : 4 Gingerbread in fact
             * **************************************************
             */
            private void flipItemLegacy(){
                initLegacyAnimation();
                getLinRoot().startAnimation(animOutLegacy);
                isFlipped.put(getCurrentPosition(), true);
            }
    
    
            private void reverseItemLegacy(){
                initLegacyAnimation();
                getImvBack().startAnimation(animInLegacy);
                isFlipped.put(getCurrentPosition(),false);
            }
            private void initLegacyAnimation() {
                if(animInLegacy==null){
                    animInLegacy= AnimationUtils.loadAnimation(getContext(), R.anim.forecast_item_in);
                    animInLegacy.setAnimationListener(new Animation.AnimationListener() {
                        public void onAnimationStart(Animation animation) {}
                        public void onAnimationEnd(Animation animation) {
                            getLinRoot().setVisibility(View.VISIBLE);
                            getImvBack().setVisibility(View.GONE);
                        }
                        public void onAnimationRepeat(Animation animation) {}
                    });
                }
                if(animOutLegacy==null){
                    animOutLegacy= AnimationUtils.loadAnimation(getContext(),R.anim.forecast_item_out);
                    animOutLegacy.setAnimationListener(new Animation.AnimationListener() {
                        public void onAnimationStart(Animation animation) {
                        }
    
                        public void onAnimationEnd(Animation animation) {
                            getImvBack().setVisibility(View.VISIBLE);
                            getLinRoot().setVisibility(View.GONE);
                        }
    
                        public void onAnimationRepeat(Animation animation) {
                        }
                    });
                }
            }
    
            /**************************************************
             * Animation tricks
             * postHoneyComb
             * **************************************************
             */
    
            @SuppressLint("NewApi")
            private void animateItem(){
                initialiseFlipAnimator();
                setFlip.start();
                isFlipped.put(getCurrentPosition(), true);
                printisFlipp("animateItem");
            }
            @SuppressLint("NewApi")
            private void reverseAnimateItem(){
                initialiseReverseFlipAnimator();
                setReverse.start();
                isFlipped.put(getCurrentPosition(), false);
                printisFlipp("animateItem");
            }
            @SuppressLint("NewApi")
            private void initialiseReverseFlipAnimator() {
                if(reverseFlipAnimatorIn==null){
                    reverseFlipAnimatorIn= AnimatorInflater.loadAnimator(getContext(), R.animator.flip_in);
                    reverseFlipAnimatorIn.addListener(new SimpleAnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            getLinRoot().setVisibility(View.VISIBLE);
    
                        }
    
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            getImvBack().setVisibility(View.GONE);
                        }
                    });
                    reverseFlipAnimatorIn.setTarget(getLinRoot());
                    reverseFlipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
                    reverseFlipAnimatorOut.setTarget(imvBack);
                    setReverse=new AnimatorSet();
                    setReverse.playTogether(reverseFlipAnimatorIn,reverseFlipAnimatorOut);
                }
            }
    
            @SuppressLint("NewApi")
            private void initialiseFlipAnimator(){
                if(flipAnimatorIn==null){
                    flipAnimatorIn= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_in);
                    flipAnimatorIn.setTarget(getImvBack());
                    flipAnimatorOut= AnimatorInflater.loadAnimator(getContext(),R.animator.flip_out);
                    flipAnimatorOut.setTarget(getLinRoot());
                    flipAnimatorIn.addListener(new SimpleAnimatorListener() {
                        @Override
                        public void onAnimationStart(Animator animation) {
                            getImvBack().setVisibility(View.VISIBLE);
                        }
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            getLinRoot().setVisibility(View.GONE);
                        }
                    });
                    setFlip=new AnimatorSet();
                    setFlip.playTogether(flipAnimatorIn, flipAnimatorOut);
                }
            }
        }
    
        @SuppressLint("NewApi")
        public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
            /**
             * <p>Notifies the start of the animation.</p>
             *
             * @param animation The started animation.
             */
            public abstract void onAnimationStart(Animator animation);
    
            /**
             * <p>Notifies the end of the animation. This callback is not invoked
             * for animations with repeat count set to INFINITE.</p>
             *
             * @param animation The animation which reached its end.
             */
            public abstract void onAnimationEnd(Animator animation) ;
    
            /**
             * <p>Notifies the cancellation of the animation. This callback is not invoked
             * for animations with repeat count set to INFINITE.</p>
             *
             * @param animation The animation which was canceled.
             */
            @Override
            public void onAnimationCancel(Animator animation) {
                onAnimationEnd(animation);
            }
    
            /**
             * <p>Notifies the repetition of the animation.</p>
             *
             * @param animation The animation which was repeated.
             */
            @Override
            public void onAnimationRepeat(Animator animation) {
                onAnimationStart(animation);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-12
      • 2016-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-29
      • 1970-01-01
      • 2010-12-25
      • 1970-01-01
      相关资源
      最近更新 更多