在listview中,毕加索不断地刷新图片,慢慢地加载

时间:2022-08-24 16:45:22

I have been searching SO threads for answers but couldn't figure out my issue from previous discussion. I have a listview which loads about 50 images (it used to be about 100 but this was barely loading any images at all). After grabbing my JSON content (including image URL) from an api endpoint, through an adapter, my code puts it inside the listview.

我一直在寻找答案,但无法从之前的讨论中找出我的问题。我有一个listview,它可以加载大约50张图片(以前大概是100张,但现在几乎不加载任何图片)。在通过一个适配器从api端点获取JSON内容(包括图像URL)之后,我的代码将其放在listview中。

Currently, with 50 images, picasso will load one image at a time as I scroll down on the feed. I feel as if keeping the scroll fixed on one item in the listview will make that image load faster. As I scroll up, however, it puts the placeholder back in and reloads the image again. Is there a way to solve this issue?

目前,有50张图片,毕加索将在我向下滚动食物时加载一个图像。我觉得把滚动条固定在listview中的一个条目上会使图像加载速度更快。然而,当我向上滚动时,它将占位符放回并重新加载图像。有办法解决这个问题吗?

public class MainActivity extends Activity {
    private List<Post> myPosts = new ArrayList<Post>();
    protected String[] mBlogPostTitles;
    public static final String TAG = MainActivity.class.getSimpleName();//prints name of class without package name

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if(isNetworkAvailable()) {
            GetBlogPostsTask getBlogPostsTask = new GetBlogPostsTask(); // new thread
            getBlogPostsTask.execute();// don't call do in background directly
        }else{
            Toast.makeText(this, "Network is unavailable", Toast.LENGTH_LONG).show();
        }
    }
    public boolean isNetworkAvailable() {
        ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();

        boolean isAvailable = false;

        if(networkInfo != null && networkInfo.isConnected()){
            isAvailable = true;
        }

        return isAvailable;
    }
    private void populateListView() {
        ArrayAdapter<Post> adapter = new MyListAdapter();
        ListView list = (ListView) findViewById(R.id.postsListView);
        list.setAdapter(adapter);
    }

    private class MyListAdapter extends ArrayAdapter<Post>{
        public MyListAdapter() {
            super(MainActivity.this, R.layout.item_view, myPosts);
        }

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

            // make sure we have a view to work with
            View itemView = convertView;
            if (itemView == null) {
                itemView = getLayoutInflater().inflate(R.layout.item_view, parent,false);
            }
            //find the post to work with
            Post currentPost = myPosts.get(position);
            Context context = itemView.getContext();

            String imageURL = currentPost.getImage();
            if(imageURL == null || imageURL.isEmpty()){
                ImageView imageView = (ImageView) itemView.findViewById(R.id.item_image);
                imageView.setVisibility(View.GONE);
            }else{
                ImageView imageView = (ImageView) itemView.findViewById(R.id.item_image);
                Picasso.with(context)
                        .load(imageURL)
                        .tag(context)
                        .placeholder(R.drawable.kanye8080s)
                        .error(R.drawable.stadiumarcadium)
                        .into(imageView);
                imageView.setVisibility(View.VISIBLE);
            }

            //Username
            TextView userText = (TextView) itemView.findViewById(R.id.item_txtUser);
            userText.setText(currentPost.getUser());

            //Time of post
            TextView timeText = (TextView) itemView.findViewById(R.id.item_txtTime);
            timeText.setText("" + currentPost.getTime());

            //The actual post
            TextView postText = (TextView) itemView.findViewById(R.id.item_txtPost);
            postText.setText("" + currentPost.getPost());

            //The actual post
            TextView likesText = (TextView) itemView.findViewById(R.id.item_txtLikes);
            likesText.setText("" + currentPost.getLikes());

            return itemView;
        }
    }

    private class GetBlogPostsTask extends AsyncTask<Object, Void, List> {

        @Override
        protected List doInBackground(Object[] params) {

            int responseCode = -1;//need to have this variable outside scope of try/catch block
            JSONObject jsonResponse = null;
            StringBuilder builder = new StringBuilder();
            HttpClient client = new DefaultHttpClient();
            HttpGet httpget = new HttpGet(""); /// api endpoint redacted

            try {

                HttpResponse response = client.execute(httpget);
                StatusLine statusLine = response.getStatusLine();
                responseCode = statusLine.getStatusCode();

                if(responseCode == HttpURLConnection.HTTP_OK){ //could have used just 200 value
                    HttpEntity entity = response.getEntity();
                    InputStream content = entity.getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                    String line;
                    while((line = reader.readLine()) != null){
                        builder.append(line);
                    }

                    jsonResponse = new JSONObject(builder.toString());

                    JSONArray jsonPosts = jsonResponse.getJSONArray("posts");
                    for(int i=0; i < jsonPosts.length(); i++ ){
                        JSONObject jsonPost = jsonPosts.getJSONObject(i);

                        int post_id = Integer.parseInt(jsonPost.getString("id"));
                        String post_user = jsonPost.getString("user");
                        String post_account = jsonPost.getString("account");
                        int post_time = Integer.parseInt(jsonPost.getString("time"));
                        String post_post = jsonPost.getString("post");
                        String post_image = jsonPost.getString("image");
                        int post_likes = Integer.parseInt(jsonPost.getString("likes"));

                        myPosts.add(new Post(post_id, post_user, post_account, post_time, post_post, post_image, "profile picture here", post_likes));
                    }
                }else{
                    Log.i(TAG, "Unsuccessful HTTP Response Code: " + responseCode);
                }
            }
            catch (MalformedURLException e){
                Log.e(TAG, "Exception caught");
            }
            catch (IOException e){
                Log.e(TAG, "Exception caught");
            }
            catch (Exception e){//must be in this order, this is the last, general catch
                Log.e(TAG, "Exception caught", e);
            }

            return null;
        }
        @Override
        protected void onPostExecute(List result) {
            // call populateListView method here
            populateListView();
            super.onPostExecute(result);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
} 

any help would be greatly appreciated,

如有任何帮助,我们将不胜感激。

Thank you!!

谢谢你! !

EDIT:

编辑:

Hi everyone,

大家好!

I have updated my code into a view holder pattern, created two separate views (one for a post with an image, one for a post with just text) and also included Picasso's new scroll detection capabilities.

我将代码更新为视图持有者模式,创建了两个独立的视图(一个用于有图像的post,一个用于只有文本的post),还包括毕加索的新滚动检测功能。

I have seen an improvement in some of the images loading quicker, at least when the view is focused while scrolling, the image is more likely to load now. However, on scroll up those same images that were once loaded, disappear. It feels as if Picasso is only loading 4-5 images at a time and replacing the ones already loaded to make room. My updated code is below:

我看到了一些图像加载速度更快的改进,至少当视图集中在滚动时,图像现在更有可能加载。然而,在滚动那些曾经被载入的图像时,消失了。这感觉就好像毕加索每次只加载4-5张图片,并替换已经加载的图片以腾出空间。我更新的代码如下:

public class MainActivity extends Activity {
    private List<Post> myPosts = new ArrayList<Post>();
    protected String[] mBlogPostTitles;
    public static final String TAG = MainActivity.class.getSimpleName();//prints name of class without package name

    ...

    private void populateListView() {
        Activity activity = MainActivity.this;

        ArrayAdapter<Post> adapter = new MyListAdapter();
        ListView list = (ListView) findViewById(R.id.postsListView);
        list.setAdapter(adapter);
        list.setOnScrollListener(new SampleScrollListener(activity));
    }

    private class MyListAdapter extends ArrayAdapter<Post>{
        public MyListAdapter() {
            super(MainActivity.this, R.layout.item_view, myPosts);
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public int getItemViewType(int position) {
            String imageURL = myPosts.get(position).getImage();
            if(imageURL == null || imageURL.isEmpty()){
                return 1; // text based
            }else{
                return 0; // image based
            }
        }

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

            PostViewHolder holder;

            int type = getItemViewType(position);

            View itemView = convertView;

            // make sure we have a view to work with
            if (itemView == null) {
                holder = new PostViewHolder();
                if(type == 1) {
                    itemView = getLayoutInflater().inflate(R.layout.item_view, parent, false);
                }else {
                    itemView = getLayoutInflater().inflate(R.layout.image_post_view, parent, false);
                    holder.image = (ImageView) itemView.findViewById(R.id.item_image);
                }
                holder.user = (TextView) itemView.findViewById(R.id.item_txtUser);
                holder.time = (TextView) itemView.findViewById(R.id.item_txtTime);
                holder.post = (TextView) itemView.findViewById(R.id.item_txtPost);
                holder.likes = (TextView) itemView.findViewById(R.id.item_txtLikes);

                itemView.setTag(holder);
            } else {
                holder = (PostViewHolder) itemView.getTag();
            }

            //find the post to work with
            Post currentPost = myPosts.get(position);

            if(type != 1) {
                Context context = itemView.getContext();
                String imageURL = currentPost.getImage();

                Picasso.with(context).setIndicatorsEnabled(true);
                //Picasso.with(context).setLoggingEnabled(true);
                Picasso.with(context)
                        .load(imageURL)
                        .tag(context)
                        .placeholder(R.drawable.kanye8080s)
                        //.skipMemoryCache()
                        .error(R.drawable.stadiumarcadium)
                        .fit()
                        .into(holder.image);
            }
            //Username
            holder.user.setText(currentPost.getUser());

            //Time of post
            holder.time.setText("" + currentPost.getTime());

            //The actual post
            holder.post.setText(currentPost.getPost());

            //Likes for the post
            holder.likes.setText("" + currentPost.getLikes());

            return itemView;
        }
    }
    public class SampleScrollListener implements AbsListView.OnScrollListener {
        private final Context context;

        public SampleScrollListener(Context context) {
            this.context = context;
        }

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            final Picasso picasso = Picasso.with(context);
            if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
                picasso.resumeTag(context);
            } else {
                picasso.pauseTag(context);
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                             int totalItemCount) {
            // Do nothing.
        }
    }


    ...
}

Where is the issue coming from? Should I be preloading these images somehow in the cache? While I have already looked into Picasso's new priority feature, should I be telling Picasso somehow to load images in the order in which they appear in my listview? Any ideas? How can I "keep" images that have already been loaded on scroll up? Thanks!

这个问题从何而来?我应该在缓存中预加载这些图像吗?虽然我已经研究过毕加索的新优先级功能,但我是否应该告诉毕加索在我的listview中,以某种方式加载图像呢?什么好主意吗?如何“保存”已经加载到滚动条上的图像?谢谢!

-- 24x7

——24 x7

5 个解决方案

#1


5  

use resize with picasso

使用调整毕加索

 Picasso.with(context)
.load(imageURL)
.tag(context)
.placeholder(R.drawable.kanye8080s)
.error(R.drawable.stadiumarcadium)
.into(imageView)
.resize(x,y);

//This would definitely help

/ /这肯定会有所帮助

#2


4  

The size of the memory cache of Picasso is limited so it does not generate out of memory errors when scrolling long lists. Once the images are out of the memory cache, the placeholder will be displayed while the image is reloaded from either the disk cache or the network.

Picasso的内存缓存大小有限,所以在滚动长列表时不会产生内存错误。当映像离开内存缓存后,在从磁盘缓存或网络重新加载映像时,将显示占位符。

The disk cache is enabled by default so the reload time should be very fast. You can use setIndicatorsEnabled(true) to see where the images are being loaded from.

磁盘缓存是默认启用的,所以重新加载时间应该非常快。您可以使用setIndicatorsEnabled(true)查看从何处加载图像。

If you are finding that Picasso is reloading the images from network, this is probably a problem with the HTTP headers being sent from the server. I don't believe Picasso actually caches the images on disk itself, instead relying on the HTTP layer, which will obey a no-cache header, and will reload from network if the expire time elapses.

如果您发现毕加索正在从网络重新加载图像,这可能是服务器发送HTTP头的问题。我不认为毕加索实际上是在磁盘上缓存图像,而是依赖于HTTP层,它将遵循一个无缓存的标题,如果过期时间过期,将从网络中重新加载。

#3


4  

I'd look at two things.

我会看两件事。

Number one is the sizes of the images being loaded. I don't know what the default maximum cache size is in Picasso, but it sounds like you may be exceeding it with just a few images, causing the others to be evicted from the cache.

首先是要加载的图像的大小。我不知道Picasso中默认的最大缓存大小是多少,但是听起来您可能仅仅用一些图像就超过了它,导致其他的被从缓存中删除。

Number two is probably not the core issue, but also contributes to performance. You are doing a lot findViewById() calls, which are fairly expensive. Look into the "ViewHolder" pattern for "caching" those lookups.

第二点可能不是核心问题,但也有助于表现。您正在执行许多findViewById()调用,这些调用非常昂贵。查看“ViewHolder”模式以“缓存”这些查找。

Edit - see Jake Wharton's answer to a similar question for more detail

编辑——详见杰克·沃顿对类似问题的回答

#4


1  

I would suggest you to use GLIDE for image loading. Since GLIDE is fast and with its cache loading feature you can get super fast image loading, With GLIDE you get lot of features..

我建议您使用GLIDE来加载图像。由于滑翔速度快,而且具有缓存加载功能,所以你可以得到超级快速的图像加载,随着滑翔,你会得到很多功能。

Download here https://github.com/bumptech/glide

在这里下载https://github.com/bumptech/glide

#5


0  

Use :

使用:

recyclerview.getRecycledViewPool().setMaxRecycledViews(0, 0);

This solved my issue

这解决了我的问题

#1


5  

use resize with picasso

使用调整毕加索

 Picasso.with(context)
.load(imageURL)
.tag(context)
.placeholder(R.drawable.kanye8080s)
.error(R.drawable.stadiumarcadium)
.into(imageView)
.resize(x,y);

//This would definitely help

/ /这肯定会有所帮助

#2


4  

The size of the memory cache of Picasso is limited so it does not generate out of memory errors when scrolling long lists. Once the images are out of the memory cache, the placeholder will be displayed while the image is reloaded from either the disk cache or the network.

Picasso的内存缓存大小有限,所以在滚动长列表时不会产生内存错误。当映像离开内存缓存后,在从磁盘缓存或网络重新加载映像时,将显示占位符。

The disk cache is enabled by default so the reload time should be very fast. You can use setIndicatorsEnabled(true) to see where the images are being loaded from.

磁盘缓存是默认启用的,所以重新加载时间应该非常快。您可以使用setIndicatorsEnabled(true)查看从何处加载图像。

If you are finding that Picasso is reloading the images from network, this is probably a problem with the HTTP headers being sent from the server. I don't believe Picasso actually caches the images on disk itself, instead relying on the HTTP layer, which will obey a no-cache header, and will reload from network if the expire time elapses.

如果您发现毕加索正在从网络重新加载图像,这可能是服务器发送HTTP头的问题。我不认为毕加索实际上是在磁盘上缓存图像,而是依赖于HTTP层,它将遵循一个无缓存的标题,如果过期时间过期,将从网络中重新加载。

#3


4  

I'd look at two things.

我会看两件事。

Number one is the sizes of the images being loaded. I don't know what the default maximum cache size is in Picasso, but it sounds like you may be exceeding it with just a few images, causing the others to be evicted from the cache.

首先是要加载的图像的大小。我不知道Picasso中默认的最大缓存大小是多少,但是听起来您可能仅仅用一些图像就超过了它,导致其他的被从缓存中删除。

Number two is probably not the core issue, but also contributes to performance. You are doing a lot findViewById() calls, which are fairly expensive. Look into the "ViewHolder" pattern for "caching" those lookups.

第二点可能不是核心问题,但也有助于表现。您正在执行许多findViewById()调用,这些调用非常昂贵。查看“ViewHolder”模式以“缓存”这些查找。

Edit - see Jake Wharton's answer to a similar question for more detail

编辑——详见杰克·沃顿对类似问题的回答

#4


1  

I would suggest you to use GLIDE for image loading. Since GLIDE is fast and with its cache loading feature you can get super fast image loading, With GLIDE you get lot of features..

我建议您使用GLIDE来加载图像。由于滑翔速度快,而且具有缓存加载功能,所以你可以得到超级快速的图像加载,随着滑翔,你会得到很多功能。

Download here https://github.com/bumptech/glide

在这里下载https://github.com/bumptech/glide

#5


0  

Use :

使用:

recyclerview.getRecycledViewPool().setMaxRecycledViews(0, 0);

This solved my issue

这解决了我的问题