Использование анимации в адаптере с шаблоном ViewHolder
У меня проблема при использовании анимации в моем адаптере.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(resource, parent, false);
holder = new ViewHolder();
holder.newRoomView = (TextView) convertView.findViewById(R.id.newRoom);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Room item = items.get(position);
// animate new rooms
if (item.isNewRoom()) {
AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);
alphaAnim.setDuration(1500);
alphaAnim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
holder.newRoomView.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});
holder.newRoomView.startAnimation(alphaAnim);
}
// ...
return convertView;
}
При добавлении новой комнаты за пределы адаптера и при вызове notifyDataSetChanged
новая комната будет правильно анимирована, но когда вызывается onAnimationEnd
, скрывается другая (не новая комната).
Есть ли способ скрыть нужную комнату?
Ответы
Ответ 1
Поскольку вы не объявили переменную holder
в методе getView()
, я могу только предположить, что вы объявили ее как переменную экземпляра в своем классе. Это твоя проблема. К моменту завершения анимации переменная holder
содержит ссылку на совершенно другой элемент.
Вам нужно использовать локальную переменную, объявленную как final
внутри метода getView()
. Я не знаю, нужна ли вам эта переменная holder
вне метода getView()
или нет, но если вы это сделаете, вы можете сделать это:
// animate new rooms
if (item.isNewRoom()) {
final ViewHolder holderCopy = holder; // make a copy
AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);
alphaAnim.setDuration(1500);
alphaAnim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
holderCopy.newRoomView.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});
holder.newRoomView.startAnimation(alphaAnim);
}
Это, конечно, не будет работать, если анимация занимает так много времени, что представление было переработано тем временем.
Ответ 2
if (item.isNewRoom()) {
// store view reference first
final View newRoomView = holder.newRoomView;
...
alphaAnim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
// hide exactly this view when animation ends
newRoomView.setVisibility(View.INVISIBLE);
}
...
}
Ответ 3
Вероятно, это связано с механизмом рециркуляции ListView. Попробуйте пометить анимированный вид и использовать findViewByTag
для его получения onAnimationEnd
. Например.
public View getView(final int position, final View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(resource, parent, false);
holder = new ViewHolder();
holder.newRoomView = (TextView) convertView.findViewById(R.id.newRoom);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Room item = items.get(position);
// animate new rooms
if (item.isNewRoom()) {
AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);
alphaAnim.setDuration(1500);
alphaAnim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
View view = convertView.findViewWithTag(position);
if (view != null) {
view.setVisibility(View.INVISIBLE);
}
}
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});
holder.newRoomView.startAnimation(alphaAnim);
holder.newRoomView.setTag(position);
}
// ...
return convertView;
}
Ответ 4
Если вы хотите перейти на новый RecyclerView, вы сможете использовать эту великолепную анимационную библиотеку, проверьте ее: https://github.com/wasabeef/recyclerview-animators
Ответ 5
Имейте в виду, что вам нужно всегда иметь инструкцию else для таких вещей.
В противном случае все остальное, используя один и тот же держатель, также будет невидимым.
if (item.isNewRoom()) {
AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0.0f);
alphaAnim.setDuration(1500);
alphaAnim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation) {
holder.newRoomView.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationRepeat(Animation animation) {}
});
holder.newRoomView.startAnimation(alphaAnim);
}else{
holder.newRoomView.setVisibility(View.VISIBLE);
}
Ответ 6
OK. Раньше у меня была эта проблема, и это, вероятно, ошибка Android:
Вы используете внутренние объекты для установки прослушивателя анимации:
alphaAnim.setAnimationListener(new AnimationListener(){/*bla bla bla*/});
Измените приведенный выше код на что-то вроде этого:
AnimationListener myListener = new AnimationListener(){/*bla bla bla*/};
alphaAnim.setAnimationListener(myListener);
Довольно сумасшедшее решение, я знаю, но оно спасло мою задницу в нескольких подобных случаях.