【问题标题】:Android RecyclerView freezes when image loading is done using Picasso使用毕加索完成图像加载时,Android RecyclerView 冻结
【发布时间】:2015-07-18 07:31:31
【问题描述】:

在我的 android 应用程序中,主页上有一个 recyclerview,其中包含作为每个项目的卡片视图。首先,我调用 ParseServer 来获取服务器上的数据并将其传递给 recyclerview 适配器。我将图像 url 以及其他详细信息传递给适配器类。 在我的 MainActivity.java 中

Parse.initialize(this, "key1", "key2");
        ParsePush.subscribeInBackground("", new SaveCallback() {

            @Override
            public void done(ParseException e) {
                if (e == null) {
                      Log.d("com.parse.push", "successfully subscribed to the broadcast channel.");
                    } else {
                      Log.e("com.parse.push", "failed to subscribe for push", e);
                    }
            }
        });
        if(haveNetworkConnection()){
                new task().execute();

mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
        mRecyclerView.setHasFixedSize(true);
        mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);

从服务器获取数据的类任务:

public class task extends AsyncTask<Void, Void, Void>{


        ProgressDialog mProgressDialog;
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            rel.setVisibility(View.VISIBLE);
            pro.setVisibility(View.VISIBLE);
            titles.clear();
            images.clear();
            venues.clear();
            timings.clear();
            urgent.clear();
        }
        @Override
        protected Void doInBackground(Void... params) {
            ParseQuery<ParseObject> query = ParseQuery.getQuery("notices");
                try {
                    List<ParseObject> ob = query.find();
                    for(ParseObject obj:ob){
                        String str = obj.getString("title");
                        String str1 = obj.getString("venue");
                        String str2 = obj.getString("timing");
                        ParseFile file = obj.getParseFile("images");
                        String str3 = obj.getString("urgent");
                        String url;
                        if(file!=null)
                            url = file.getUrl();
                        else{
                            url = "https://www.medidirect.com.au/MEDIstores/_images/no-thumb.png";
                        }
                        titles.add(str);
                        images.add(url);
                        venues.add(str1);
                        timings.add(str2);
                        urgent.add(str3);
                    }
                } catch (ParseException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }


            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            AdapNotice mAdapter = new AdapNotice(MainActivity.this,titles,images,venues,timings,urgent);
            if(titles.size()<1)
                Toast.makeText(MainActivity.this, "Oops, Something went wrong. Please try again", 300).show();
            mRecyclerView.setAdapter(mAdapter);
            pro.setVisibility(View.GONE);
            rel.setVisibility(View.GONE);
        }
    }

在 Recyclerview 适配器的 AdapNotice 类中:

public class AdapNotice extends RecyclerView.Adapter<AdapNotice.ViewHolder>{


     Activity activity;
     ArrayList<String> _titles = new ArrayList<String>();
     ArrayList<String> _images = new ArrayList<String>();
     ArrayList<String> _venues = new ArrayList<String>();
    ArrayList<String> _timings = new ArrayList<String>();
    ArrayList<String> _urgents = new ArrayList<String>();
    private int lastPosition = -1;
    int pos;
     public AdapNotice(Activity a, ArrayList<String> titles, ArrayList<String> images, ArrayList<String> venues, ArrayList<String> timings, ArrayList<String> urgent) {
         activity = a;
         _titles = titles;
         _images = images;
         _venues = venues;
         _timings = timings;
         _urgents = urgent;
     }
     public static class ViewHolder extends RecyclerView.ViewHolder{
        // each data item is just a string in this case
        public TextView title;
         public ImageView imgview,urgent;
         public TextView venue,venuetext,abctext;
         public TextView timing,timingtext,abc1text;
        public ViewHolder(View v) {
            super(v);
            title = (TextView) v.findViewById(R.id.title);
            imgview = (ImageView) v.findViewById(R.id.img_notice);
            venue = (TextView) v.findViewById(R.id.venue);
            timing = (TextView) v.findViewById(R.id.timing);
            venuetext = (TextView)v.findViewById(R.id.venuetext);
            abctext = (TextView)v.findViewById(R.id.abctext);
            timingtext = (TextView)v.findViewById(R.id.timingtext);
            abc1text = (TextView)v.findViewById(R.id.abc1text);
            urgent = (ImageView)v.findViewById(R.id.urgentstar);
        }

    }
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        if(_titles.size()<=0)
            return 0;
        return _titles.size();
    }
    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        // TODO Auto-generated method stub
        try{
            holder.title.setText(_titles.get(position));
            if(_urgents.get(position).contains("yes")){
                holder.urgent.setVisibility(View.VISIBLE);
            }
            pos = position;
            if(!_venues.get(position).contentEquals("---")){
                holder.venuetext.setVisibility(View.VISIBLE);
                holder.venue.setVisibility(View.VISIBLE);
                holder.abctext.setVisibility(View.VISIBLE);
                holder.venue.setText(_venues.get(position));
            }
            if(!_timings.get(position).contentEquals("---")){
                holder.timingtext.setVisibility(View.VISIBLE);
                holder.timing.setVisibility(View.VISIBLE);
                holder.abc1text.setVisibility(View.VISIBLE);
                holder.timing.setText(_timings.get(position));
            }
            Target target = new Target() {

                @Override
                public void onPrepareLoad(Drawable arg0) {
                    // TODO Auto-generated method stub
                    Animation anim = AnimationUtils.loadAnimation(activity, R.anim.rotate);
                    holder.imgview.setImageResource(R.drawable.loading);
                    holder.imgview.startAnimation(anim);

                }

                @Override
                public void onBitmapLoaded(Bitmap arg0, LoadedFrom arg1) {
                    // TODO Auto-generated method stub
                    holder.imgview.clearAnimation();
                    holder.imgview.setImageBitmap(arg0);
                    Log.i("completed", pos+" completed");
                    storeDB obj = new storeDB(_titles.get(pos), _venues.get(pos), _timings.get(pos), DbBitmapUtility.getBytes(arg0), _urgents.get(pos));
                    obj.execute();
                }

                @Override
                public void onBitmapFailed(Drawable arg0) {
                    // TODO Auto-generated method stub
                    holder.imgview.clearAnimation();
                    holder.imgview.setImageResource(R.drawable.error);

                }
            };
            Picasso.with(activity).load(_images.get(position)).into(target);
        }catch(Exception e){
            e.printStackTrace();
        }



    }
    private class storeDB extends AsyncTask<Void, Void, Void>{

        String title,venue,timing,urgent;
        byte[] image;
        public storeDB(String one, String two, String three, byte[] four, String five){
            title = one;
            venue = two;
            timing = three;
            image = four;
            urgent = five;
        }
        @Override
        protected Void doInBackground(Void... params) {
            SQLiteHandler handler = new SQLiteHandler(activity);
            handler.addNotice(title, venue, timing, image, urgent);
            handler.close();
            return null;
        }

    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
            View v = LayoutInflater.from(parent.getContext()).inflate(
                    R.layout.item, parent, false);
            Animation animation = AnimationUtils.loadAnimation(activity, (arg1>lastPosition)?R.anim.up_from_bottom:R.anim.down_from_top);
            v.startAnimation(animation);
            ViewHolder vh = new ViewHolder(v);
            return vh;

    }
}

我将图像 url 传递给 picasso 库,该库将图像加载到目标中。直到毕加索没有提取图像,我正在显示另一个加载图像并赋予它旋转动画效果。

有时,即使图像是由毕加索加载的,它也不会显示在 imageview 中。我知道这是因为单击该 imageview 时,完整的图像会加载到另一个可以正常工作的活动中。旋转动画停止并且加载的图像也未显示在 imageview 中。此外,在加载 imageview 时,主视图有时会冻结。

那么,我该如何解决这个问题,让我的应用运行更流畅?

提前致谢。

更新我发现冻结问题是由于对目标的弱引用。我通过强大的参考解决了它,但我该如何解决悬挂问题?当我滚动时,应用程序会挂起一段时间。

【问题讨论】:

  • Picasso 已经缓存了图像,无需将其存储在 SQLlite 中,另外尝试缩小加载到图像视图中的图像Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(imageView)
  • 我在用户离线时使用 sqlite。缓存不会一直留在那里吗?
  • Android 系统需要回收内存时不会清理缓存,因此如果您需要 SQLlite 存储图像,请查看存储 blob。
  • 我已经这样做了。这很好用。图像已成功存储为 blob
  • 在主页上,我在较小的图像视图中显示该图像。单击该按钮后,将打开另一个活动,其中包含完整大小的图像。那么,如果我使用 .resize ,如何管理两种尺寸的图像加载?

标签: android android-layout picasso android-recyclerview android-cardview


【解决方案1】:

你可以使用一个简单的方法

Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);

在加载图像之前会显示占位符图像,如果发生错误将显示错误图像

更新 要存储在数据库中,只需编写这么多样板代码

class MyTarget implements Target {

private ImageView imageView;

 public MyTarget(ImageView imageView) {
     this.imageView = imageView;
 }

@Override
 public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
     this.imageView.setImageBitmap(bitmap);
     //store your bitmap as you were storing 
 }

}

然后

Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(new MyTarget(imageView));

【讨论】:

  • 是的,我知道,但我希望该占位符图像旋转,使其看起来更像加载操作。
  • 我删除了目标并直接将图像加载到 imageview。现在更流畅但没有目标,我如何将加载的图像存储到本地数据库?我使用目标有两个目的,一个动画和另一个存储到本地数据库中。即使我删除动画,我如何达到第二个目的?
  • 我已经更新了代码,我知道这是一个妥协,否则如果你想要动画和存储数据我建议link
  • 我认为将图像存储到 sqlite 会导致应用程序中出现一点点挂起。我仍然为此使用了 asynctask。您能建议将图像存储到 sqlite 的最佳方法吗?
  • 在存储图像方面不太专业,尝试在stackoverflow上搜索您会找到答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多