Android notifyDataSetChanged()ListView项闪烁
我对ListView有问题。当应用程序启动时,它会从网络上加载10篇文章并显示出来。当您到达ListView的底部时,我开始加载下一个10个帖子,并将它们添加到列表的末尾。当我调用Android notifyDataSetChanged()ListView项闪烁,android,listview,Android,Listview,我对ListView有问题。当应用程序启动时,它会从网络上加载10篇文章并显示出来。当您到达ListView的底部时,我开始加载下一个10个帖子,并将它们添加到列表的末尾。当我调用notifyDataSetChanged()ListView时,它将重新绘制不变的元素。这会导致奇怪的图像闪烁 我读到hasStableIds()可能会有帮助,并尝试在两个值中设置它,但都没有帮助 以下是我的适配器代码: public class PostListAdapter extends BaseAdap
notifyDataSetChanged()
ListView时,它将重新绘制不变的元素。这会导致奇怪的图像闪烁
我读到hasStableIds()
可能会有帮助,并尝试在两个值中设置它,但都没有帮助
以下是我的适配器代码:
public class PostListAdapter extends BaseAdapter {
Context context;
ArrayList<Post> posts;
LayoutInflater inflater;
View.OnClickListener onClickListener;
public PostListAdapter(Context context, ArrayList<Post> posts, View.OnClickListener onClickListener){
this.posts = posts; this.context = context; inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.onClickListener = onClickListener;
}
@Override
public int getCount() {
return posts.size();
}
@Override
public Object getItem(int position) {
return posts.get(position);
}
@Override
public long getItemId(int position) {
return (long)posts.get(position).id;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("draw " + posts.get(position).id);
LinearLayout mainLayout;
if (convertView == null) {
mainLayout = (LinearLayout) inflater.inflate(R.layout.post, null, false);
}else {
mainLayout = (LinearLayout) convertView;
LinearLayout cont = (LinearLayout) mainLayout.findViewById(R.id.image_container);
while (cont.getChildCount() != 0){
cont.removeViewAt(0);
}
}
LinearLayout imageContainer = (LinearLayout) mainLayout.findViewById(R.id.image_container);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Post post = posts.get(position);
TextView textView = (TextView)mainLayout.findViewById(R.id.postLink);
textView.setText("http://joyreactor.cc/post/" + Integer.toString(post.id));
textView.setPaintFlags(textView.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
textView.setOnClickListener(onClickListener);
ArrayList<ImageView> images = new ArrayList<>();
for (int i=0; i<post.images.size(); i++){
ScaleImageView image = new ScaleImageView(context);
image.setPadding(0, 10, 0, 0);
image.setImageBitmap(Bitmap.createBitmap(post.sizes.get(i)[0], post.sizes.get(i)[1], Bitmap.Config.RGB_565));
//image.setImageResource(R.drawable.grill);
image.setLayoutParams(params);
ImageLoader loader = new ImageLoader(image, post, i, context);
loader.execute(post.images.get(i));
post.loaders.add(loader);
imageContainer.addView(image);
}
if (post.images.size() == 0){
ScaleImageView image = new ScaleImageView(context);
image.setLayoutParams(params);
image.setImageBitmap(MainActivity.bmpMissPicture);
imageContainer.addView(image);
}
mainLayout.setTag(post);
return mainLayout;
}
}
另外notifyDataSetChanged()
i调用MainActivity线程中的处理程序:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bmpMissPicture = Bitmap.createBitmap(500, 500, Bitmap.Config.RGB_565);
Canvas c = new Canvas(bmpMissPicture);
Paint p = new Paint();
ListView listView = (ListView) findViewById(R.id.postList);
View.OnClickListener onClickListener = new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(((TextView) v).getText().toString()));
startActivity(browserIntent);
}
};
adapter = new PostListAdapter(getApplicationContext(), posts, onClickListener);
listView.setAdapter(adapter);
p.setColor(getResources().getColor(R.color.missPicColor));
c.drawRect(0, 0, 500, 500, p);
handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
System.out.println("calling notifyDataSetChanged()");
if (msg.what == 0) {
((LinearLayout) findViewById(R.id.mainContainer)).removeViewAt(0);
findViewById(R.id.mainContainer).invalidate();
findViewById(R.id.postList).invalidate();
adapter.notifyDataSetChanged();
//System.out.println(posts.size());
final ListView listView = (ListView) findViewById(R.id.postList);
new Thread(new Runnable() {
@Override
public void run() {
while (!stopAskUpdate){
int pos = listView.getLastVisiblePosition();
//Log.d("Inf Scroll", "Pos: " + (pos+1) + " of " + posts.size());
if (pos >= posts.size()-1 && !loadNewPosts){
MainActivity.loadNewPosts = true;
new PageParser("http://joyreactor.cc/" + nextPage, posts, MainActivity.handler, false).start();
}
try{Thread.sleep(1500);}catch (InterruptedException e){}
}
}
}).start();
}else {
adapter.notifyDataSetChanged();
System.out.println("Size after load more posts " + posts.size());
}
}
};
}
那么,你能告诉我如何解决这个问题以及为什么它不起作用吗
提前感谢您的帮助。我想您面临的问题可能是因为您在“活动”类中反复初始化“适配器”。每次在“数组”中添加新项时,都可能是在初始化适配器,然后在设置适配器后调用“notifyDataSetChanged()”方法。请确保只初始化一次适配器
还有一个建议,请尝试使用“毕加索”库加载图像。我找到了方法。你应该做BasicAdapter应该做的工作,但由于某些原因,它做不到。在
getView()
中用新数据填充convertView
之前,您应该检查convertView
是否已经包含它。对于我的情况,解决方案如下所示:
@Override
public long getItemId(int position) {
return (long)posts.get(position).id;
}
public View getView(int position, View convertView, ViewGroup parent) {
boolean flag = false;
LinearLayout mainLayout;
if (convertView == null) {
flag = true;
mainLayout = (LinearLayout) inflater.inflate(R.layout.post, null, false);
}else {
mainLayout = (LinearLayout) convertView;
if (((Post)convertView.getTag()).id != getItemId(position)){ //View tag contain link to the post.
flag = true;
LinearLayout cont = (LinearLayout) mainLayout.findViewById(R.id.image_container);
while (cont.getChildCount() != 0){
cont.removeViewAt(0);
}
}
}
if (flag) {
//do some stuff with mainLayout....
mainLayout.setTag(posts.get(position));
}
return mainLayout;
因此,如果convertView
表示getView()
请求的post,我们只返回它而不做任何更改,否则我们会进行更改以表示新的post
由于方法hasStableIds()
的缘故,我认为适配器自己做这件事,并防止对getView()
的无用调用,但他没有
希望它能帮助他人,并感谢您的回答和评论。您可以在数组中添加和删除项,也可以调用notifyItemInserted或notifyitemRemoved。这将使您获得更好的性能。另外,使用毕加索将是一个不错的选择。不。我100%确信我只在
onCreate()中初始化适配器一次。
。关于毕加索:一开始我想自己动手,不想使用任何外部库。但感谢您的推荐。如果您想制作自己的自定义模块,请尝试修改您的模块,并通过删除不必要的内容来优化其性能,这很好。当你完成了所有的努力,你仍然认为你的代码仍然需要一些修改,然后找到一些图像加载库代码,如“毕加索”或“通用图像加载程序”并将您的缓存过程代码与库代码进行比较。您是否考虑过使用RecyclerView,它可以更细粒度地控制数据集中哪些项发生了更改?是的,但RecyclerView仅在Android Lolipop中添加。我想用ListView来做。它在android支持库中提供,所以你也可以用旧版本的android。真的吗?!我会尝试,但这个问题现在对我来说是一个挑战。开箱即用,我认为您无法缓解您在ListView中看到的问题。notifyDataSetChanged相当于说“我不知道数据集中发生了什么变化,所以ListView(因为您也不知道)会重新绘制所有可见项。”很抱歉,BaseAdapter
没有此消息。也许你说的是RecyclerView
和他的适配器?哦,是的!我记得。这将是伟大的,如果你可以切换到回收的看法,然后我看到它。而且我已经找到了答案。现在我将用listview完成我的应用程序,之后我将它改为RecyclerView并比较它们。这对我帮助很大!请在充气前检查convertview是否为空。再次非常感谢。你太棒了。
@Override
public long getItemId(int position) {
return (long)posts.get(position).id;
}
public View getView(int position, View convertView, ViewGroup parent) {
boolean flag = false;
LinearLayout mainLayout;
if (convertView == null) {
flag = true;
mainLayout = (LinearLayout) inflater.inflate(R.layout.post, null, false);
}else {
mainLayout = (LinearLayout) convertView;
if (((Post)convertView.getTag()).id != getItemId(position)){ //View tag contain link to the post.
flag = true;
LinearLayout cont = (LinearLayout) mainLayout.findViewById(R.id.image_container);
while (cont.getChildCount() != 0){
cont.removeViewAt(0);
}
}
}
if (flag) {
//do some stuff with mainLayout....
mainLayout.setTag(posts.get(position));
}
return mainLayout;