Android图片加载框架Picasso(Square公司)

本文详细介绍Picasso图片加载库的使用方法与高级特性,包括基本配置、自定义Adapter、处理图片加载错误、图片缓存机制及性能优化技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


    };

2: 然后写一个简单的Activity,需要一个Adapter,并将Adapter设置到ListView中填充数据

public class MainActivity extends AppCompatActivity {

    private ListView lv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.lv);
        lv.setAdapter(new ImageListAdapter(this,imageUrls));
    }
}

3:我们需要在Adapter中加载一个ListView子item的layout文件,当然也很简单

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" 
    android:layout_height="200dp">

</ImageView>

4: 我们还需要一个自定义的Adapter,功能很简单,只显示一张图片即可

 public class ImageListAdapter extends ArrayAdapter{
        private Context context;

        private String[] imageUrls;

        public ImageListAdapter(Context context,String[] imageUrls){
            super(context,R.layout.item_picasso,imageUrls);

            this.context = context;
            this.imageUrls = imageUrls;

        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView==null){
                convertView = View.inflate(context,R.layout.item_picasso,null);
            }

            //加载图片
            Picasso
                    .with(context)
                    .load(imageUrls[position])
                    .into((ImageView) convertView);

            return convertView;
        }
    }
 

注意:

  • 我们一般会复用ConvertView来保持listview的快速平滑的滚动,而Picasso的一个优点就是会自动处理划出屏幕外的图片请求,并给对应的ImageView加载出正确的资源;
  • 另外,你会发现当你上下滚动后,会发现图片加载速度有了明显的提升,这正是因为Picasso的高速缓存,而且不需要再去从网络加载,Picasso所实现的缓存的大小取决于你自己的设备;
  • Picasso加载图片的资源会从三个地方进行获取, 内存,磁盘,和网络,这些操作都不需要你自己处理,Picasso已经能智能完成;

一个GridView的小示例

ListViewGridView的展示及使用上并没有什么区别,很简单,上代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.smallcheric.picasso.MainActivity">
    <android.support.v7.widget.AppCompatButton
        android:id="@+id/bt"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换"/>
    <ListView
        android:id="@+id/lv"
        android:layout_below="@id/bt"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />
    <GridView
        android:id="@+id/grid"
        android:layout_below="@id/bt"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:numColumns="2"/>
</RelativeLayout>

最后,附上Github地址点我

如果图片地址不存在或为空怎么处理

上面我们写的代码都是在正常的情况下,但是如果我们的图片地址错误将怎么处理呢,如果不去处理,网络可能会一直请求或者我们的屏幕上会出现一片空白,这都不是我们希望看到的.

Picasso给了我们两种解决方案:

  • 在判断为空的地址时,取消网络请求,调用cancelRequest(),然后调用imageView.setImageDrawable(null)
  • 或者调用Picasso的.placeHolder()方法进行图片的替换展示
  • 如果图片网址错误,我们也可以调用.error()方法进行图片替换
 @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView==null){
                convertView = View.inflate(context,R.layout.item_picasso,null);
            }
            ImageView imageView = (ImageView)convertView;
            if (TextUtils.isEmpty(imageUrls[position])){
                Picasso
                        .with(context)
                        .cancelRequest(imageView);
                imageView.setImageDrawable(null);
            }else {
                //加载图片
                Picasso
                        .with(context)
                        .load(imageUrls[position])
                        .placeholder(R.mipmap.ic_launcher)
                        .error(R.mipmap.ic_launcher)
                        .into((ImageView) convertView);
            }
            return convertView;
        }

注意:.placeholder().error()所传的参数与.load()相同

OK,到现在为止,我们已经基本掌握了Picasso的基本用法,后面将为大家分析到Picasso性能方面的特性,让我们共同期待,愿大家都有美好的一天.


调用.noFade()

  Picasso的默认图片加载方式有一个淡入的效果,如果调用了noFade(),加载的图片将直接显示在ImageView

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher)  
    .error(R.mipmap.future_studio_launcher)  
    .noFade()
    .into(imageViewFade);

调用.noPlaceholder()

  有一个场景,当你从网上加载了一张图片到Imageview上,过了一段时间,想在同一个ImageView上展示另一张图片,这个时候你就会去调用Picasso,进行二次请求,这时Picasso就会把之前的图片进行清除,可能展示的是.placeholder()的图片,给用户并不是很好的体验,如果调用了noPlaceholder(),就不会出现这种情况.

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher)  
    .into(imageViewNoPlaceholder, new Callback() {
        @Override
        public void onSuccess() {
            // 当上次加载完成后,进行二次加载
            Picasso
                .with(context)
               .load(UsageExampleListViewAdapter.eatFoodyImages[1])
               .noPlaceholder() 
               .into(imageViewNoPlaceholder);
        }

        @Override
        public void onError() {

        }
    });

调用resize(x, y)来自定义图片的加载大小

如果图片很大或者想自定义图片的显示样式,可以调用该API来解决这个问题;

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .resize(600, 200)  
    .into(imageViewResize);

调用`onlyScaleDown()来缩短图片的加载计算时间

如果我们调用了resize(x,y)方法的话,Picasso一般会重新计算以改变图片的加载质量,比如一张小图变成一张大图进行展示的时候,但是如果我们的原图是比我们从新resize的新图规格大的时候,我们就可以调用onlyScaleDown()来直接进行展示而不再重新计算.

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .resize(6000, 2000)
    .onlyScaleDown() // 如果图片规格大于6000*2000,将只会被resize
    .into(imageViewResizeScaleDown);

对拉伸图片的处理

如果图片被操作了,可能在展示的时候就会比较丑,我们是想改变这种情景的,Picasso给我们提供了两种选择进行图片展示,centerCrop() 或者 centerInside().

  • centerCrop() - 图片会被剪切,但是图片质量看着没有什么区别

  • Inside()- 图片会被完整的展示,可能图片不会填充满ImageView`,也有可能会被拉伸或者挤压

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .resize(600, 200)
    .centerInside() 或者调用 .centerCrop()
    .into(imageViewResizeCenterInside);

调用.fit()来智能展示图片

如果调用了该API, Picasso会对图片的大小及ImageView进行测量,计算出最佳的大小及最佳的图片质量来进行图片展示,减少内存,并对视图没有影响;

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .fit()
    .into(imageViewHero);

调用.priority()设置图片加载的优先级

如果一个屏幕上顶部图片较大,而底部图片较小,因为Picasso是异步加载,所以小图会先加载出来,但是对于用户来说,更希望看到的是上面的图片先加载,底部的图片后加载,Picasso支持设置优先级,分为HIGH, MEDIUM, 和 LOW,所有的加载默认优先级为MEDIUM;

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .fit()
    .priority(Picasso.Priority.HIGH)
    .into(imageViewHero);

注意:设置优先级并不能保证图片就一定会被优先加载,只是会偏向倾斜于先加载;

调用tag()为请求添加标记提升用户体验

  我们都知道,在一个ListView的子item中加载一张图片是很常见的,这些图片都来源于网络请求,如果这个listview有上千条数据,当用户快速滑动的时候,每个item会不断的被复用,当然Picasso的请求也不断地进行请求,取消请求,再次请求,再次取消的操作(对屏幕外的自动取消请求),但是如果有一个方案,可以在用户在快速滑动的时候全部停止请求,只有在滑动停止时再去请求,就非常完美了;

Picasso提供了三种设置Tag的方式

  • 暂停标记 pauseTag()
  • 可见标记 resumeTag()
  • 取消标记 cancleTag()

pauseTag()resumeTag()的用法

在图片请求时添加标记

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .tag("Profile ListView") //参数为 Object
    .into(imageViewWithTag);

然后让listview实现滑动监听

@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("Profile ListView");
    } else {
          picasso.pauseTag("Profile ListView");
    }
  }

cancleTag()的使用场景

试想一下,当你在浏览购物车的时候,这个时候就会去展示所有被选中item的图片资源,如果这个时候用户点击了购买按钮,就会弹出一个progressdialog去请求数据以进行页面跳转,这个时候原来的请求就需要取消掉了;

public void buyButtonClick(View v) {
     showDiaolg();

    // 取消网络请求
    Picasso
        .with(context)
        .cancelTag("ShoppingCart");
}

注意:如果tag状态为pause或者resume的话,Picasso会对tag持有一个引用,如果此时用户退出了当前Activity,垃圾回收机制进行回收的时候,就会出现内存泄露,所以需要在onDestory()方法中进行相应处理;

.fetch() , .get()Target之间的区别

  • .fetch() - 该方法会在后台异步加载一张图片,但是不会展示在ImageView上,也不会返回Bitmap,这个方法只是为了将获取到的资源加载到本地和内存中,为了后期加载缩短时间;
  • .get() - 该方法也是一个异步线程,不过加载完成后会返回一个Bitmap,但是需要注意,该方法不能在主线程中调用,因为会造成线程阻塞;
  • Target - 我们之前调用.into()方法,只是将获取到的资源加载到ImageView中,但我们还可以将资源作为回调放到Target中,上代码:
private Target target = new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        //加载成功后会得到一个bitmap,可以自定义操作
    }

    @Override
    public void onBitmapFailed(Drawable errorDrawable) {
        // 加载失败进行相应处理
    }

    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {

    }
};

Picasso
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .into(target);

注意:你可以使用.get()或者Target获取图片的Bitmap,但是当你使用Target时,不能使用匿名内部类的方式,因为垃圾回收机制在你获取不到Bitmap的时候会把对象回收;

Picasso在自定义Notifications上的使用

Picasso有一个功能是可以加载图片到RemoteViews上,而RemoteViews是用在Widgets及自定义notification布局上的,下面通过一个小的示例来看Picasso是如何起作用的;

 private void testRemoteView() {
        RemoteViews remoteViews = new RemoteViews(getPackageName(),R.layout.item_picasso);
        remoteViews.setImageViewResource(R.id.iv_remoteview,R.mipmap.abc);

        remoteViews.setTextViewText(R.id.tv_title,"This Title");
        remoteViews.setTextViewText(R.id.tv_desc,"This desc");

        remoteViews.setTextColor(R.id.tv_title,getResources().getColor(android.R.color.black));
        remoteViews.setTextColor(R.id.tv_desc,getResources().getColor(android.R.color.holo_blue_bright));

        NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this)
                                                .setSmallIcon(R.mipmap.notifation)
                                                .setContentTitle("Context Title")
                                                .setContentText("Content Text")
                                                .setContent(remoteViews)
                                                .setPriority(NotificationCompat.PRIORITY_MIN);

        Notification notification = builder.build();

        if (Build.VERSION.SDK_INT > 16){
            notification.bigContentView = remoteViews;
        }
        NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(NOTIFICATION_ID,notification);

        Picasso.with(MainActivity.this)
                .load("http://www.jycoder.com/json/Image/3.jpg")
                .into(remoteViews,R.id.iv_remoteview,NOTIFICATION_ID,notification);
    }

上面可以看到,Picasso的使用也是非常简单,只需要调用.into()的另一个重载方法即可:
.into(Android.widget.RemoteViews remoteViews, int viewId, int notificationId, android.app.Notification notification)

效果如下


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值