Improve Large ListView Adapter smooth scroll, sometimes jerky

fra*_*ess 13 java performance android listview android-layout

I'm trying to see what is making my listview jerk sometimes when scroll, at times it's bad especially when the application first launches.

All the conditions I have are necessary, unless there is something I don't know(highly likely). I'm not running certain tasks on a seperate thread because they are dependent on the data I receive from the backend(I'm coding both, so backend suggestions are welcome as well). Product is in beta but really need to make this a slightly bit smoother. I'm compressing the images, and they are a bit long but it's not the problem because when I upload the images from the device, I also include the width and height of the image and send that along to the backend. These dimensions come back when loading the list.

One thing I wonder is if calculating/converting the dimensions for the specific device's screen is causing the slight lag. Not sure how resource intensive that task is, but without it(without knowing the dimensions, each row would start out flat and then expand to the actual picture size which would cause the list to jump, so I can't run that calculation on the background either.)

Basically the scrolling isn't bad, but I need to improve this somehow.

Here is my Adapter:

public class VListAdapter extends BaseAdapter {

    ViewHolder viewHolder;
    private boolean isItFromProfile;

    /**
     * fields For number formating, ex. 1000
     * would return 1k in the format method
     */
    private static final NavigableMap<Long, String> suffixes = new TreeMap<>();

    static {
        suffixes.put(1_000L, "k");
        suffixes.put(1_000_000L, "M");
        suffixes.put(1_000_000_000L, "G");
        suffixes.put(1_000_000_000_000L, "T");
        suffixes.put(1_000_000_000_000_000L, "P");
        suffixes.put(1_000_000_000_000_000_000L, "E");
    }

    private Context mContext;
    private LayoutInflater mInflater;
    private ArrayList<Post> mDataSource;
    private static double lat;
    private static double lon;

    public VListAdapter(Context context, ArrayList<Post> items) {
        mContext = context;
        mDataSource = items;
        //mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        isItFromProfile = false;
        mInflater = LayoutInflater.from(context);
    }

    public VListAdapter() {

    }

    public VListAdapter(Context baseContext, ArrayList<Post> posts, boolean b) {
        mContext = baseContext;
        mDataSource = posts;
        //mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        isItFromProfile = b;
        mInflater = LayoutInflater.from(baseContext);
    }


    public void addElement(Post post) {
        mDataSource.add(0, post);
        this.notifyDataSetChanged();
    }

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

    @Override
    public Object getItem(int position) {
        return mDataSource.get(position);
    }

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


    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        int limit = Math.min(position + 4, getCount());
        for (int i = position; i < limit; i++) {
            Glide.with(mContext).load(((Post) getItem(i)).getFilename().toString()).preload();
        }


//        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads()
//                .detectDiskWrites().detectNetwork()
//                .penaltyLog().build());



        View rowView = convertView;

        if (rowView == null) {
            viewHolder = new ViewHolder();
            rowView = mInflater.inflate(R.layout.mylist, parent, false);

            viewHolder.titleTextView = (TextView) rowView.findViewById(R.id.usernameinlist);
            viewHolder.timeago = (TextView) rowView.findViewById(R.id.timeago);
            //viewHolder.sharebutton = (ImageView) rowView.findViewById(R.id.sharebutton);
            viewHolder.likesTextView = (TextView) rowView.findViewById(R.id.likestext);
            viewHolder.viewcount = (TextView) rowView.findViewById(R.id.viewcount);
            viewHolder.distance = (TextView) rowView.findViewById(R.id.distance);
            viewHolder.footprints = (TextView) rowView.findViewById(R.id.footprintcount);
            viewHolder.postText = (TextView) rowView.findViewById(R.id.posttext);
            viewHolder.profilePic = (ImageView) rowView.findViewById(R.id.profilethumb);
            viewHolder.caption = (TextView) rowView.findViewById(R.id.captiontext);
            viewHolder.moremenu = (ImageView) rowView.findViewById(R.id.dots);
            viewHolder.likesPic = (ImageView) rowView.findViewById(R.id.likeimage);
            viewHolder.mapitPic = (ImageView) rowView.findViewById(R.id.mapimage);
            viewHolder.playbutton = (ImageView) rowView.findViewById(R.id.playbutton);
            viewHolder.videoThumb = (ImageView) rowView.findViewById(R.id.videothumb);
            viewHolder.listphoto = (ImageView) rowView.findViewById(R.id.listphoto);
            viewHolder.rainbow = (ImageView) rowView.findViewById(R.id.rainbow);
            rowView.setTag(viewHolder);

        } else {
            viewHolder = (ViewHolder) rowView.getTag();
        }


        final Post post = (Post) getItem(position);
        int color = Color.parseColor("#dddddd");
        viewHolder.likesPic.setColorFilter(color);
        viewHolder.mapitPic.setColorFilter(color);
        viewHolder.moremenu.setColorFilter(color);


        if (Hawk.count() == 0)
            initHawkWithDataFromServer();

        if (isItFromProfile) {
            viewHolder.profilePic.setVisibility(View.GONE);
            viewHolder.titleTextView.setVisibility(View.GONE);
            viewHolder.distance.setVisibility(View.GONE);
        }

        viewHolder.titleTextView.setText(post.getUsername());
        PrettyTime prettyTime = new PrettyTime();
        DateTime dateTime = new DateTime(post.getUploadDate().get$date());
        viewHolder.timeago.setText(prettyTime.format(dateTime.toDate()));
        viewHolder.likesTextView.setText(String.valueOf(format(post.getLikes())));
        viewHolder.footprints.setText(String.valueOf(format(post.getLocation().size() - 1)));

        //don't display 0 if there are no likes, just show heart icon
        if (viewHolder.likesTextView.getText().equals("0"))
            viewHolder.likesTextView.setVisibility(View.GONE);
        else
            viewHolder.likesTextView.setVisibility(View.VISIBLE);



        //don't display 0 if there are no footprints
        if (viewHolder.footprints.getText().equals("0"))
            viewHolder.footprints.setVisibility(View.GONE);
        else
            viewHolder.footprints.setVisibility(View.VISIBLE);

        double[] loc = post.getLocation().get(0);
        viewHolder.distance.setText("~" + PostListFragment.distance(loc[0], loc[1], 'M') + " Miles");
        if (post.getViews() != null)
            viewHolder.viewcount.setText(format(post.getViews()) + (post.getViews() == 1 ? " View" : " Views"));

        String profilePictureS3Url = "https://s3-us-west-2.amazonaws.com/moleheadphotos/" + post.getUsername()
                + ".jpg";

        String filename = post.getS3link();
        final String videoThumbURL = "https://s3-us-west-2.amazonaws.com/moleheadphotos/" + filename;


        Glide.with(mContext).load(profilePictureS3Url).asBitmap().centerCrop().into(new BitmapImageViewTarget(viewHolder.profilePic) {
            @Override
            protected void setResource(Bitmap resource) {
                RoundedBitmapDrawable circularBitmapDrawable =
                        RoundedBitmapDrawableFactory.create(mContext.getResources(), resource);
                circularBitmapDrawable.setCircular(true);
                viewHolder.profilePic.setImageDrawable(circularBitmapDrawable);
            }
        });

        int height = ((Post) getItem(position)).getHeight();
        int width = ((Post) getItem(position)).getWidth();

        if (height != 0 && width != 0) {
            ViewGroup.LayoutParams params = viewHolder.listphoto.getLayoutParams();
            Resources r = mContext.getResources();
            height = (int) getHeight(height, width);
            params.height = height;
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            viewHolder.listphoto.setLayoutParams(params);

        } else {
            ViewGroup.LayoutParams params = viewHolder.listphoto.getLayoutParams();
            params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
            viewHolder.listphoto.setLayoutParams(params);
        }

        if (post.getType() == null) {
            Glide.clear(viewHolder.listphoto);
            viewHolder.listphoto.setVisibility(View.GONE);
            //Glide.clear(viewHolder.listphoto);
            viewHolder.videoThumb.setVisibility(View.VISIBLE);
            viewHolder.rainbow.setVisibility(View.VISIBLE);
            Glide.with(mContext).load(videoThumbURL).fitCenter()
                    .diskCacheStrategy(DiskCacheStrategy.ALL).dontAnimate().into(viewHolder.videoThumb);
            viewHolder.playbutton.setVisibility(View.VISIBLE);

        }

        if (post.getType() != null) {
            if (post.getType().equals("video")) {
                viewHolder.playbutton.setVisibility(View.VISIBLE);
                Glide.clear(viewHolder.listphoto);
                viewHolder.listphoto.setVisibility(View.GONE);
                Glide.clear(viewHolder.postText);
                viewHolder.postText.setVisibility(View.GONE);
                viewHolder.videoThumb.setVisibility(View.VISIBLE);
                viewHolder.rainbow.setVisibility(View.VISIBLE);

                Glide.with(mContext).load(videoThumbURL).fitCenter()
                        .diskCacheStrategy(DiskCacheStrategy.ALL).dontAnimate().into(viewHolder.videoThumb);

            }

            if (post.getType().equals("image")) {
                Glide.clear(viewHolder.videoThumb);
                viewHolder.videoThumb.setVisibility(View.GONE);
                viewHolder.rainbow.setVisibility(View.GONE);
                Glide.clear(viewHolder.playbutton);
                viewHolder.playbutton.setVisibility(View.GONE);
                Glide.clear(viewHolder.postText);
                viewHolder.postText.setVisibility(View.GONE);
                viewHolder.listphoto.setVisibility(View.VISIBLE);
                viewHolder.listphoto.setBottom(0);

                Glide.with(mContext).load(post.getFilename().toString())
                        .diskCacheStrategy(DiskCacheStrategy.ALL).dontAnimate()
                        .into(viewHolder.listphoto);
            }

            if (post.getType().equals("text")) {
                Glide.clear(viewHolder.videoThumb);
                viewHolder.videoThumb.setVisibility(View.GONE);
                viewHolder.rainbow.setVisibility(View.GONE);
                Glide.clear(viewHolder.playbutton);
                viewHolder.playbutton.setVisibility(View.GONE);
                Glide.clear(viewHolder.listphoto);
                viewHolder.listphoto.setVisibility(View.GONE);
                viewHolder.postText.setVisibility(View.VISIBLE);
                viewHolder.postText.setText(post.getText());
            }

        }

        if (Hawk.contains("liked" + post.getId().get$oid())) {
            viewHolder.likesPic.clearColorFilter();
            Glide.with(mContext).load(R.drawable.heartroundorange).into(viewHolder.likesPic);
            ((ImageView) viewHolder.likesPic).setColorFilter(Color.parseColor("#ff3a6f"));
        } else {

            Glide.with(mContext).load(R.drawable.heartroundgray).diskCacheStrategy(DiskCacheStrategy.ALL)
                    .into(viewHolder.likesPic);
        }


        if (Hawk.contains("mapped" + post.getId().get$oid())) {
            viewHolder.mapitPic.clearColorFilter();
            ((ImageView) viewHolder.mapitPic).setImageResource(R.drawable.dropmaincolororange);
            ((ImageView) viewHolder.mapitPic).setColorFilter(Color.parseColor("#444444"));
        } else {
            Glide.with(mContext).load(R.drawable.dropdarkgray).diskCacheStrategy(DiskCacheStrategy.ALL)
                    .into(viewHolder.mapitPic);
        }


        if (!Hawk.contains("mapped" + post.getId().get$oid())) {
            viewHolder.mapitPic.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Hawk.put("mapped" + post.getId().get$oid(), 1);
                    ((ImageView) viewHolder.mapitPic).setImageResource(R.drawable.dropmaincolororange);
                    viewHolder.footprints.setText(String.valueOf(post.getLocation().size() + 1));
                    post.getLocation().add(new double[]{PostListFragment.lon, PostListFragment.lat});
                    notifyDataSetChanged();
                    Thread t = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            postMappedToServer(post.getId().get$oid());
                        }
                    });
                    t.start();
                    TastyToast.makeText(mContext, "Post dropped off here.", TastyToast.LENGTH_SHORT, TastyToast.CONFUSING);
                }
            });
        } else {
            viewHolder.mapitPic.setClickable(false);
        }

        if (!Hawk.contains("liked" + post.getId().get$oid())) {
            viewHolder.likesPic.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    Hawk.put("liked" + post.getId().get$oid(), 1);
                    viewHolder.likesPic.setClickable(false);
                    ((ImageView) viewHolder.likesPic).setImageResource(R.drawable.heartroundorange);
                    viewHolder.likesTextView.setText(String.valueOf(post.getLikes() + 1));
                    post.setLikes(post.getLikes() + 1);
                    notifyDataSetChanged();
                    Thread t = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            postLikeToServer(post);
                        }
                    });
                    t.start();
                }
            });
        } else {
            viewHolder.likesPic.setClickable(false);
        }


        if (post.getType() == null || post.getType().equals("video"))
            viewHolder.videoThumb.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    if (VListAdapter.this.mContext instanceof ProfileFeed) {
                        ((ProfileFeed) VListAdapter.this.mContext).closeActivity();
                    }

                    Intent broadcast = new Intent();
                    broadcast.setAction("com.molehead.openout.POST");

                    broadcast.putExtra("postId", post.getFilename().toString());
                    broadcast.putExtra("hawkId", post.getId().get$oid());
                    broadcast.putExtra("s3link", post.getS3link());
                    broadcast.putExtra("username", post.getUsername());

                    if (Hawk.contains("liked" + post.getId().get$oid()))
                        broadcast.putExtra("liked", "yes");
                    else
                        broadcast.putExtra("liked", "no");

                    broadcast.putExtra("likecount", post.getLikes().toString());

                    App.post = post;
                    LocalBroadcastManager.getInstance(mContext.getApplicationContext()).sendBroadcast(broadcast);
                }
            });

        viewHolder.moremenu.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                PopupMenu popup = new PopupMenu(mContext.getApplicationContext(), viewHolder.moremenu, Gravity.CENTER);
                //Inflating the Popup using xml file
                popup.getMenuInflater().inflate(R.menu.menu_main, popup.getMenu());

                //registering popup with OnMenuItemClickListener
                popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                    public boolean onMenuItemClick(MenuItem item) {
                        switch (item.getItemId()) {
                            case R.id.action_share:
                                String postId = post.getId().get$oid();
                                Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
                                sharingIntent.setType("text/plain");
                                String shareBody = postId + ".jpg"; //https://openout.herokuapp.com/posts/" + postId;
                                String shareSub = "Shared via Molehead";
                                sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, shareSub);
                                sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);
                                sharingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                Intent new_intent = Intent.createChooser(sharingIntent, "Share");
                                new_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                mContext.getApplicationContext().startActivity(new_intent);
                                break;
                        }
                        return true;
                    }
                });

                popup.show();
            }
        });
        return rowView;
    }


    private void initHawkWithDataFromServer() {
        SharedPreferences settings = mContext.getApplicationContext().getSharedPreferences("userinfo", 0);
        String username = settings.getString("username", "ok");
        String password = settings.getString("password", "ok");


        LoginService loginService =
                ServiceGenerator.createService(LoginService.class, username, password);
        final Call<List<Post>> call = loginService.getLikes(username);
        Log.i("lonlat", String.valueOf(lon) + " and  " + String.valueOf(lat));


        call.enqueue(new Callback<List<Post>>() {
            @Override
            public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {

                ArrayList<Post> posts = new ArrayList<>();
                posts = (ArrayList<Post>) response.body();
                if (!posts.isEmpty())
                    for (Post p : posts) {
                        Hawk.put("liked" + p.getId().get$oid(), 1);
                    }
            }

            @Override
            public void onFailure(Call<List<Post>> call, Throwable t) {
            }
        });
    }


    private void postMappedToServer(String oid) {
        SharedPreferences settings = mContext.getSharedPreferences("userinfo", 0);
        String username = settings.getString("username", "ok");
        String password = settings.getString("password", "ok");
        LoginService loginService =
                ServiceGenerator.createService(LoginService.class, username, password);

        Log.i("postlistfraglat", String.valueOf(PostListFragment.lat));
        Call<ResponseBody> call = loginService.addLocation(oid, PostListFragment.lon, PostListFragment.lat);

        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if (response.isSuccessful())
                    Log.i("mapped", "success");
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });
    }


    public void postLikeToServer(Post post) {

        SharedPreferences settings = mContext.getSharedPreferences("userinfo", 0);
        String username = settings.getString("username", "ok");
        String password = settings.getString("password", "ok");

        LoginService loginService =
                ServiceGenerator.createService(LoginService.class, username, password);

        Call<ResponseBody> call = loginService.like(post, 1, username);


        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                if (response.isSuccessful()) {
                    try {
                        Log.i("call", response.body().string());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.i("MFEED", "like request failed");
            }
        });
    }


    public static String format(long value) {
        //Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
        if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1);
        if (value < 0) return "-" + format(-value);
        if (value < 1000) return Long.toString(value); //deal with easy case

        Map.Entry<Long, String> e = suffixes.floorEntry(value);
        Long divideBy = e.getKey();
        String suffix = e.getValue();

        long truncated = value / (divideBy / 10); //the number part of the output times 10
        boolean hasDecimal = truncated < 100 && (truncated / 10d) != (truncated / 10);

        return hasDecimal ? (truncated / 10d) + suffix : (truncated / 10) + suffix;
    }


    static class ViewHolder {
        private TextView titleTextView;
        private TextView timeago;
        private TextView likesTextView;
        private TextView viewcount;
        private TextView distance;
        private TextView footprints;
        private ImageView profilePic;
        private ImageView moremenu;
        private ImageView likesPic;
        private ImageView mapitPic;
        private ImageView rainbow;
        //private ImageView sharebutton;
        private TextView caption;
        private ImageView listphoto;
        private ImageView videoThumb;
        private ImageView playbutton;
        private TextView postText;
        private Post post;

    }


    private float getHeight(float height, float width) {
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        return (height * size.x / width);

    }
}
Run Code Online (Sandbox Code Playgroud)

Vas*_*liy 6

指向特定问题是不可能的,因为适配器中有太多代码.但有一件事是肯定的 - RecyclerView在这种情况下切换到不会帮助你.

适配器不应包含业务逻辑 - 它们只应将输入对象"调整"到基础视图.在您的情况下,似乎适配器执行计算,生成新线程,执行网络请求等.

您需要重构代码,以使适配器与此类似:

public class PostsListAdapter extends ArrayAdapter<Post> {


    private Context mContext;

    public PostsListAdapter(Context context, int resource) {
        super(context, resource);
        mContext = context;
    }

    public void bindPosts(List<Post> posts) {
        clear();
        addAll(posts);
        notifyDataSetChanged();
    }


    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            // assign new View to convertView
            // create new ViewHolder
            // set ViewHolder as tag of convertView
            // set listeners
        } else {
            // get a reference to existing ViewHolder
        }

        // populate ViewHolder's elements with data from getItem(position)
        // kick off asynchronous loading of images
        // NOTE: no calculations allowed here - just simple bidding of data to Views

        return convertView;
    }

}
Run Code Online (Sandbox Code Playgroud)

您的代码需要以这样的方式构建,即在绑定新数据之前涉及计算和转换数据的业务逻辑ListView,以及Post传递给bindPosts()方法的对象已经包含上述计算和转换的结果.

适配器刚刚从"适应"的最终数据PostsViews仅此而已- .

如果你现在时间很短,只需要"让它工作",那么我将首先删除产生新线程并发出网络请求的逻辑.看看这是否会提高性能.


Anu*_*ngh 4

  • 将您的实施更改为RecyclerView在废弃视图或回收方面更有效的实施方式。
  • 如果项目是静态的并且不会改变以显着平滑滚动,我们还可以启用优化:

    recyclerView.setHasFixedSize(true);
    
    Run Code Online (Sandbox Code Playgroud)
  • 创建一个Intent服务,注册BroadcastReceiver为api请求、业务规则、数据修改完成时的数据返回回调或错误回调。使用同步调用initHawkWithDataFromServer()提前执行,从api获取结果后继续修改或应用业务逻辑。之后创建新的适配器或更新现有的适配器数据集。

  • 将以下所有数据计算或数据值格式化逻辑从适配器getView()移至上述意图服务。

    您可以向现有的 Post pojo 添加更多 getter 和 setter。

    DateTime dateTime = new DateTime(post.getUploadDate().get$date()); 
    viewHolder.timeago.setText(prettyTime.format(dateTime.toDate()));
    viewHolder.likesTextView.setText(String.valueOf(format(post.getLikes())));
    viewHolder.footprints.setText(String.valueOf(format(post.getLocation().size)) - 1)));
    
    Post{
        //Your existing property
        @Expose(serialize = false, deserialize = false)
        //equals neither serialize nor deserialize or
        private DateTime uploadedDateTime;
        //etc. prettyTime.format, String.valueOf
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 删除不必要的反射:

    GsonBuilder builder = new GsonBuilder();
    builder.excludeFieldsWithoutExposeAnnotation();
    Gson gson = builder.create();
    new  Retrofit.Builder().addConverterFactory(GsonConverterFactory.create(gson)).build();
    
    Run Code Online (Sandbox Code Playgroud)

    并将其添加到您的改造服务创建类中。您还可以使用瞬态(private transient DateTime uploadedDateTime;

  • 删除 public void addElement(Post post) { mDataSource.add(0, post); this.notifyDataSetChanged();} ,并且每当您需要通知单个或多个项目是否插入、删除等时。请使用以下命令:

    notifyItemChanged(int)
    notifyItemInserted(int)
    notifyItemRemoved(int)
    notifyItemRangeChanged(int, int)
    notifyItemRangeInserted(int, int)
    notifyItemRangeRemoved(int, int)
    
    Run Code Online (Sandbox Code Playgroud)

我们可以从活动或片段中使用它们:

//Add a new contact
items.add(0, new Post("Barney"));
//Notify the adapter that an item was inserted at position 0
adapter.notifyItemInserted(0);
Run Code Online (Sandbox Code Playgroud)

以上方法效率较高。每次我们想要从 中添加或删除项目时RecyclerView,我们都需要显式通知适配器该事件。与ListView适配器不同,RecyclerView适配器不应该依赖notifyDataSetChanged(),因为应该使用更细粒度的操作。请参阅 API 文档了解更多详细信息

另外,如果您打算更新现有列表,请确保在进行任何更改之前获取当前的项目数。例如,getItemCount()应该调用适配器上的 a 来记录将更改的第一个索引。

// record this value before making any changes to the existing list
int curSize = adapter.getItemCount(); // replace this line with wherever you get new records
ArrayList<Post> newItems = Post.createPostsList(20);
// update the existing list
items.addAll(newItems);
// curSize should represent the first element that got added
// newItems.size() represents the itemCount
adapter.notifyItemRangeInserted(curSize, newItems.size());
Run Code Online (Sandbox Code Playgroud)

区分较大的变化

支持库 v24.2.0 中添加了一个新DiffUtil类,以帮助计算新旧列表之间的差异。细节

  • 如果您的图像尺寸不同,请勿通过 glide 预加载图像。尝试创建您自己的. 也尝试看看
  • 作为班级成员创建颜色

    int color = Color.parseColor("#dddddd");
    
    Run Code Online (Sandbox Code Playgroud)
  • 在 Post pojo 本身中写入View.GONEView.VISIBLE,如果 IntentService 会在 Retrofit 的后台线程中执行。尝试 api 在 json 中返回布尔值,而不是“0”作为字符串。

  • 将下面全部移至IntentService //如果没有点赞则不显示0,只显示心形图标 if (viewHolder.likesTextView.getText().equals("0")) viewHolder.likesTextView.setVisibility(View.GONE); 否则 viewHolder.likesTextView.setVisibility(View.VISIBLE);

    //don't display 0 if there are no footprints
    if (viewHolder.footprints.getText().equals("0"))
        viewHolder.footprints.setVisibility(View.GONE);
    else
        viewHolder.footprints.setVisibility(View.VISIBLE);
    
    double[] loc = post.getLocation().get(0);
    viewHolder.distance.setText("~" + PostListFragment.distance(loc[0], loc[1], 'M') + " Miles");
    
    Run Code Online (Sandbox Code Playgroud)
  • 所有字符串连接也在 Post 或 IntnetService 中,例如:

    String profilePictureS3Url = "https://s3-us-west-2.amazonaws.com/moleheadphotos/" + post.getUsername() + ".jpg";
    
    Run Code Online (Sandbox Code Playgroud)

您也可以提前创建滤色器,并且只能创建一次。从列表视图中删除滚动条,因为它计算高度以显示滚动条。