每当你更新GridView / ListView时,你都会在适配器上调用notifyDatasetChanged
,它会使用新数据重新填充列表,删除当前列表中的所有内容。
现在从L Preview
设计指南
是否有可能实现同样的效果,你看到的唯一变化是没有任何" flicker"什么时候适配器重新加载或者这是什么东西现在只能在L中完成?
添加新笔记时,Google Keep
应用程序中也可以找到此效果
答案 0 :(得分:8)
我害怕它的所有视觉技巧 - 也就是动画。
...删除当前列表中的所有内容。
实际上,没有。
notifyDataSetChanged
只告诉基础观察者数据已发生变化。而已。作为回应,getView(...)
被称为每个可见视图 - ListView#getChildCount()
可访问的数字。由于任一索引已更改(添加/删除项目),或者这些索引中的对象中保存的数据已更改(^)(一个或多个项已更改),因此后续调用{{1}可以在视觉上刷新数据(^) }}
“Android开发者”有一个有趣的视频,解释了如何制作您所追求的效果:DevBytes: ListView Cell Insertion Animation。
视频/示例仅讨论单个项目插入。但它应该可以扩展到具有一些时间和数学技能的所有数据操作。
我能够扩展示例代码以插入多个项目。我将动画更改为更简单的alpha-f-in。单击ListView#getView(...)
按钮会将一个随机数(1到3之间)添加到列表视图中。这是它的样子:
伪工作流程:
在将新项目传递给适配器之前,循环遍历列表视图中的所有可见项目并保存其边界(在Add row
中)和快照(在Rect
中)
将项目传递给适配器
向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,并且不要创建适配器类的新实例