Использование анимации в адаптере с шаблоном 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;
}

Ответ 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);

Довольно сумасшедшее решение, я знаю, но оно спасло мою задницу в нескольких подобных случаях.