GridView上的动画在滚动时搞砸了

时间:2015-04-01 20:26:57

标签: android gridview

我要做的是点击时View 并再次点击时停止摇晃,我做了,但是当我滚动GridView时,一切都搞砸了向上,以及未点击的另一个View上的摇动动画。

以下是一个更清晰的解释:http://i.imgur.com/IDv7vJ6.gif

我不知道为什么其他人的观点在震动。

这是我的代码:

public class PeopleFragmentGridAdapter extends BaseAdapter {

private List<Person> people = new ArrayList<>();
private Context context;
private Map<Integer, Boolean> animating;
private Map<Integer, ObjectAnimator> animators;

public PeopleFragmentGridAdapter(Context context, List<Person> people) {
    this.context = context;
    this.people.addAll(people);
    this.animating = new HashMap<>();
    this.animators = new HashMap<>();
}

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

@Override
public Person getItem(int position) {
    return people.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    ViewHolder ViewHolder;
    final Person p = people.get(position);

    if (convertView == null) {
        convertView = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
            .inflate(R.layout.fragment_people_grid_peoples_item, null);
        ViewHolder = new ViewHolder();
        ViewHolder.peopleGridItem = (PeopleGridItem) convertView.findViewById(R.id.fragment_people_grid_peoples_item_item);
        convertView.setTag(ViewHolder);
    } else {
        ViewHolder = (ViewHolder) convertView.getTag();
    }


    convertView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            p.setSelected(!p.isSelected());
            notifyDataSetChanged();
        }
    });

    if (animators.get(p.getId()) == null)
        animators.put(p.getId(), ObjectAnimator.ofFloat(convertView, "rotation", 0, -2, 0, 2, 0));
    if (animating.get(p.getId()) == null)
        animating.put(p.getId(), false);

    if (p.isSelected()) {
        if (!animating.get(p.getId())) {
            animating.put(p.getId(), true);
            animators.get(p.getId()).setTarget(convertView);
            animators.get(p.getId()).setRepeatCount(ObjectAnimator.INFINITE);
            animators.get(p.getId()).setDuration(300);
            animators.get(p.getId()).start();
        }
    } else {
        animators.get(p.getId()).setTarget(convertView);
        animating.put(p.getId(), false);
        animators.get(p.getId()).end();
        animators.get(p.getId()).cancel();
    }

    return convertView;
}
}

有什么不对吗?

2 个答案:

答案 0 :(得分:2)

其他观点动摇的原因是Android实现GridView的方式。为了保留内存,它会回收视图,以便只有那些在屏幕上显示(或即将显示)的视图才会保存在内存中。任何被回收的视图都会放入未使用的视图池中,以后可以重用。这是参数“convertView”获取其视图的地方。

这意味着在屏幕外屏幕上保持对象的状态是你的工作。

if (convertView == null) {
    convertView = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE))
        .inflate(R.layout.fragment_people_grid_peoples_item, null);
    ViewHolder = new ViewHolder();
    ViewHolder.peopleGridItem = (PeopleGridItem) convertView.findViewById(R.id.fragment_people_grid_peoples_item_item);
    convertView.setTag(ViewHolder);
} else {
    ViewHolder = (ViewHolder) convertView.getTag();
}

输入else语句 - 其中convertView!= null - 表示您收到了回收的视图,而不是给新的视图充气。在这里,你应该防止再循环的观点继续震动。

编辑:我更仔细地查看了您的代码,我认为您实际上是在考虑我之前提到的内容(但是没有成功),但我会留下它帮助别人。在你的情况下,我认为问题是你没有阻止你的ObjectAnimator。

if (animators.get(p.getId()) == null)
    animators.put(p.getId(), ObjectAnimator.ofFloat(convertView, "rotation", 0, -2, 0, 2, 0));

这里,如果当前Person的动画制作者为空。你创建一个新的。假设你点击了person1。 ObjectAnimator1开始为person1设置动画。向下滚动,此视图将重复用于person13。创建ObjectAnimator13,然后告诉它结束并取消。无论ObjectAnimator13说什么,ObjectAnimator1仍然可以激活重复使用的视图。

要解决此问题,您可能需要在ViewHolder中存储Person的当前ID,以便您可以做的第一件事是结束以前的任何现有动画。可能更好的方法是将ObjectAnimator存储在ViewHolder中,然后不需要在HashMap中搜索它。

答案 1 :(得分:1)

您可以使用标记比使用哈希映射更容易地执行此操作...在每个getView调用中,验证是否选择了此人 - 如果没有,则取消动画。并且因为convertviews被回收,你必须重置他们的状态每个 getView调用(在需要的地方开始动画,在不需要的地方取消)。

public void getView(final int position, View convertView, ViewGroup parent) {

  // inflate convertviews if necessary

  Person person = people.get(position);

  ObjectAnimator anim = (ObjectAnimator) convertView.getTag(R.id.tag_row_animation);
  if (person.isSelected()) {
    if (anim == null) {
      anim = getAnimator(convertView);
      anim.setTarget(convertView);
      anim.start();
      convertView.setTag(R.id.tag_row_animation, anim);
    }
  } else {
    if (anim != null) anim.cancel();
    convertView.setTag(R.id.tag_row_animation, null);
  }

  convertView.setTag(R.id.tag_list_person, person);  
  convertView.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
      Person person = (Person) v.getTag(R.id.tag_list_person);
      person.toggleSelected();
      notifyDataSetChanged();
    }
  });

  return convertView;
}

public ObjectAnimator getAnimator(View v) {
  ObjectAnimator a = ObjectAnimator.ofFloat(v, "rotation", 0, -2, 0, 2, 0);
  a.setDuration(300);
  a.setRepeatCount(ObjectAnimator.INFINITE);
  return a;
}

至于你的解决方案不起作用的原因:因为你的hashmap正在保存对转换视图的引用,并且转换视图每n个位置被回收(n =屏幕上的位置数,给予或取1或2),他们将为每个位置摇动%n == 0,因为你设置为震动的前一个转换视图现在将为转换视图被回收的n个位置的网格单元摇动。