FliCard 和 ListView Android 中一个奇怪的可见性错误
Posted
技术标签:
【中文标题】FliCard 和 ListView Android 中一个奇怪的可见性错误【英文标题】:FliCard and ListView A strange visibility bug in 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);
好的,我深入研究了那个错误,但我仍然不明白(我有很多日志)所以 我的问题就在这里,视图告诉我,它是可见的,
但它没有显示
重现问题的简单方法,进入横向模式,翻转前两项,滚动到列表末尾。
非常感谢那些试图回答的人。 马蒂亚斯
【问题讨论】:
【参考方案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);
【讨论】:
以上是关于FliCard 和 ListView Android 中一个奇怪的可见性错误的主要内容,如果未能解决你的问题,请参考以下文章