无需重新填充即可更新GridView / ListView

时间:2014-08-19 00:22:32

标签: android android-listview android-gridview android-adapter

每当你更新GridView / ListView时,你都会在适配器上调用notifyDatasetChanged,它会使用新数据重新填充列表,删除当前列表中的所有内容。

现在从L Preview设计指南

中获取这个小video

是否有可能实现同样的效果,你看到的唯一变化是没有任何" flicker"什么时候适配器重新加载或者这是什么东西现在只能在L中完成?

添加新笔记时,Google Keep应用程序中也可以找到此效果

3 个答案:

答案 0 :(得分:8)

我害怕它的所有视觉技巧 - 也就是动画。

  

...删除当前列表中的所有内容。

实际上,没有。

notifyDataSetChanged只告诉基础观察者数据已发生变化。而已。作为回应,getView(...)被称为每个可见视图 - ListView#getChildCount()可访问的数字。由于任一索引已更改(添加/删除项目),或者这些索引中的对象中保存的数据已更改(^)(一个或多个项已更改),因此后续调用{{1}可以在视觉上刷新数据(^) }}

“Android开发者”有一个有趣的视频,解释了如何制作您所追求的效果:DevBytes: ListView Cell Insertion Animation

视频/示例仅讨论单个项目插入。但它应该可以扩展到具有一些时间和数学技能的所有数据操作。

我能够扩展示例代码以插入多个项目。我将动画更改为更简单的alpha-f-in。单击ListView#getView(...)按钮会将一个随机数(1到3之间)添加到列表视图中。这是它的样子:

enter image description here

伪工作流程:

  • 在将新项目传递给适配器之前,循环遍历列表视图中的所有可见项目并保存其边界(在Add row中)和快照(在Rect中)

    < / LI>
  • 将项目传递给适配器

  • 向ListView的ViewTreeObserver添加BitmapDrawables

  • 调用OnPreDrawListener时,可以绘制新项目。我们可以使用onPreDraw(...)使用相应的索引访问他们的views

  • 所有新项目都设置为不可见,并且分配了alpha动画师

  • 为所有旧项目分配了translation_y animators

  • 无法再通过ListView#getChildAt(...)访问的项目 - 由于添加了新项目 - 被分配了translation_y动画师 - 将它们从屏幕上移出(或超出列表视图范围)

  • translation_y动画师被解雇了。当这些动画师完成运行时,将启动alpha动画师。

请注意,动画的类型(alpha,翻译,缩放或这些动画的任意组合)相对无关紧要。我认为如果getChildAt(..)GridView(保持),这将更加困难 - 只是因为数学将涉及StaggeredGridView {{ 1}}。工作流程应该保持不变。

(^) - 语法错误,但是自然的IMO。

答案 1 :(得分:1)

这可以通过抓取视图并更改其中的数据来实现。我已经包含了一个示例,在长按下您可以更改数据而不刷新可见项而不调用notifyDataSetChanged()。

此问题已在Google I / O 2010上提出,您可以在此处观看:

The world of ListView, time 52:30

代码改编自Vogella

package com.example.stableids;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import android.os.Build;
import android.os.Bundle;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.database.DataSetObserver;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
@SuppressLint("NewApi")
public class MainActivity extends Activity {

      @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @SuppressLint("NewApi")
    @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ListView listview = (ListView) findViewById(R.id.listview);
        String[] values = new String[] { "Android", "iPhone", "WindowsMobile",
            "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X",
            "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux",
            "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
            "Android", "iPhone", "WindowsMobile" };

        final ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < values.length; ++i) {
          list.add(values[i]);
        }
        final StableArrayAdapter adapter = new StableArrayAdapter(this,
            android.R.layout.simple_list_item_1, list);
        listview.setAdapter(adapter);

        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

          @SuppressLint("NewApi")
        @Override
          public void onItemClick(AdapterView<?> parent, final View view,
              final int position, long id) {
            final String item = (String) parent.getItemAtPosition(position);
            view.animate().setDuration(2000).alpha(0)
                .withEndAction(new Runnable() {
                  @Override
                  public void run() {
                    int i = list.indexOf(item);

                    list.remove(item);
                    list.add(i, "MODIFIED");
                    adapter.mIdMap.put("MODIFIED", position);
                    TextView tv = (TextView)view.findViewById(android.R.id.text1);
                    tv.setText("MODIFIED");
                    //adapter.notifyDataSetChanged();
                    view.setAlpha(1);
                  }
                });
          }

        });
      }

      private class StableArrayAdapter extends ArrayAdapter<String> {

        HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();

        public StableArrayAdapter(Context context, int textViewResourceId,
            List<String> objects) {
          super(context, textViewResourceId, objects);
          for (int i = 0; i < objects.size(); ++i) {
            mIdMap.put(objects.get(i), i);
          }
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            Log.d("STABLE ID", "getView called for " + position + " position");
            return super.getView(position, convertView, parent);

        }

        @Override
        public long getItemId(int position) {
          String item = getItem(position);
          return mIdMap.get(item);
        }

        @Override
        public boolean hasStableIds() {
          return true;
        }

      }

}

答案 2 :(得分:0)

更新GridView / ListView而不重新填充

如果每次更新和删除时都使用相同的数组List而不创建新的ArrayList,那么你的adapter.notifyDatasetChanged()方法就可以了。 请您每次都使用相同的arrayList,并且不要创建适配器类的新实例