使用毕加索编码图库Android应用

编程入门 行业动态 更新时间:2024-10-26 14:27:03

使用<a href=https://www.elefans.com/category/jswz/34/1770028.html style=毕加索编码图库Android应用"/>

使用毕加索编码图库Android应用

您将要创造的

Picasso是一个流行的开源Android库,用于加载本地和远程图像。 了解如何轻松使用它来处理图像加载需求。

1.什么是毕加索?

毕加索 (这个名字的灵感来自法国著名画家巴勃罗·毕加索(Pablo Picasso))是一个非常流行的开源Android库,用于在您的Android应用程序中加载图像。 根据官方文档,它指出:

... Picasso允许在您的应用程序中轻松加载图像-通常只需一行代码!

请注意,毕加索在后台使用OkHttp (同一开发人员的网络库)在Internet上加载图像。

2.为什么要使用毕加索?

现在您已经了解了毕加索的全部含义,您可能要问的下一个问题是为什么要使用它?

用Java或Kotlin开发自己的媒体加载和显示功能可能是一个真正的痛苦:您必须注意缓存,解码,管理网络连接,线程,异常处理等。 毕加索(Picasso)是一个易于使用,经过精心计划,有据可查且经过全面测试的库,可以为您节省很多宝贵的时间,也可以避免一些麻烦。

根据官方文档,以下是毕加索为您处理的许多在Android上加载图像的常见陷阱:

  • 在适配器中处理ImageView回收和下载取消
  • 最少的内存使用即可完成复杂的图像转换
  • 自动内存和磁盘缓存

将图像添加到您的应用程序可以使您的Android应用程序活跃起来。 因此,在本教程中,我们将通过构建一个简单的图库应用程序来了解Picasso 2。 它将通过互联网加载图像并在RecyclerView中将其显示为缩略图,并且当用户单击图像时,它将打开包含较大图像的详细信息活动。

可以在我们的GitHub存储库中找到本教程的示例项目(在Kotlin中),因此您可以轻松地继续学习。

优秀的艺术家复制,伟大的艺术家窃取。 - 巴勃罗毕加索

3.先决条件

要遵循本教程,您需要:

  • 对核心Android API和Kotlin的基本了解
  • Android Studio 3.1.1或更高版本
  • Kotlin插件 1.2.30或更高版本

启动Android Studio并创建一个名为MainActivity的空活动的新项目(您可以将其命名为PicassoDemo )。 确保同时选中“ 包括Kotlin支持”复选框。


4.声明依赖

创建新项目后,在build.gradle中指定以下依赖 。 在撰写本文时,毕加索的最新版本是2.71828

dependencies {implementation 'com.android.support:recyclerview-v7:27.1.1'implementation 'com.squareup.picasso:picasso:2.71828'
}

或使用Maven:

<dependency><groupId>com.squareup.picasso</groupId><artifactId>picasso</artifactId><version>2.71828</version>
</dependency>

添加毕加索和RecyclerView v7工件后,请确保同步项目。

5.添加Internet权限

由于Picasso将执行网络请求以通过Internet加载图像,因此我们需要在我们的AndroidManifest.xml中包含INTERNET权限。

所以现在就去做!

<uses-permission android:name="android.permission.INTERNET" />

请注意,仅当您要从互联网加载图像时才需要这样做。 如果仅在设备上本地加载图像,则不需要这样做。

6.创建布局

我们将从在activity_main.xml布局文件内创建RecyclerView开始。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android=""android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv_images"android:layout_width="match_parent"android:layout_height="match_parent"/>
</RelativeLayout>

创建自定义项目布局

接下来,让我们创建XML布局( item_image.xml ),该布局将用于RecyclerView每个项目( ImageView )。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/iv_photo"android:adjustViewBounds="true"android:layout_height="200dp"android:scaleType="centerCrop"android:layout_margin="2dp"android:layout_width="match_parent"/>
</LinearLayout>

现在,我们已经创建了简单图库应用程序所需的布局,下一步是创建用于填充数据的RecyclerView适配器。 但是,在执行此操作之前,让我们创建简单的数据模型。

7.创建一个数据模型

我们将为RecyclerView定义一个简单的数据模型。 此模型实现了Parcelable,用于将数据从Android的一个组件高效传输到另一个组件。 在我们的情况下,数据将从SunsetGalleryActivity传输到SunsetPhotoActivity

data class SunsetPhoto(val url: String) : Parcelable {constructor(parcel: Parcel) : this(parcel.readString())override fun writeToParcel(parcel: Parcel, flags: Int) {parcel.writeString(url)}override fun describeContents(): Int {return 0}companion object CREATOR : Parcelable.Creator<SunsetPhoto> {override fun createFromParcel(parcel: Parcel): SunsetPhoto {return SunsetPhoto(parcel)}override fun newArray(size: Int): Array<SunsetPhoto?> {return arrayOfNulls(size)}}
}

请注意,此模型SunsetPhoto仅具有一个称为url字段(出于演示目的),但是如果需要,您可以有更多字段。 此类实现Parcelable ,这意味着我们必须重写一些方法。

我们可以利用Android Studio IDEA为我们生成这些方法,但是这样做的缺点是维护。 怎么样? writeToParcel我们向此类添加任何新字段时,我们都可能忘记显式更新constructorwriteToParcel方法,如果不更新方法,可能会导致一些错误。

现在,为了避免更新或编写这些样板方法,Kotlin 1.1.14引入了@Parcelize注释。 此批注将帮助我们在writeToParcel自动生成writeToParcelwriteFromParceldescribeContents方法。

@Parcelize
data class SunsetPhoto(val url: String) : Parcelable

现在,我们的代码SunsetPhoto类只有两行! 太棒了!

请记住将以下代码添加到您的应用程序模块build.gradle

androidExtensions {experimental = true
}

另外,我在SunsetPhoto模型类中包含了一个伴随对象(或Java中的静态方法) getSunsetPhotos() ,该类将在调用时简单地返回SunsetPhotoArrayList

@Parcelize
data class SunsetPhoto(val url: String) : Parcelable {companion object {fun getSunsetPhotos(): Array<SunsetPhoto> {return arrayOf<SunsetPhoto>(SunsetPhoto(""),SunsetPhoto(""),SunsetPhoto(""),SunsetPhoto(""),SunsetPhoto(""),SunsetPhoto(""))}}
}

8.创建适配器

我们将创建一个适配器,以用数据填充RecyclerView 。 我们还将实现一个单击侦听器以打开详细信息活动SunsetPhotoActivity并向其SunsetPhoto一个SunsetPhoto实例作为额外的意图。 细节活动将显示图像的特写。 我们将在后面的部分中创建它。

class MainActivity : AppCompatActivity() {//...private inner class ImageGalleryAdapter(val context: Context, val sunsetPhotos: Array<SunsetPhoto>) : RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageGalleryAdapter.MyViewHolder {val context = parent.contextval inflater = LayoutInflater.from(context)val photoView = inflater.inflate(R.layout.item_image, parent, false)return MyViewHolder(photoView)}override fun onBindViewHolder(holder: ImageGalleryAdapter.MyViewHolder, position: Int) {val sunsetPhoto = sunsetPhotos[position]val imageView = holder.photoImageView}override fun getItemCount(): Int {return sunsetPhotos.size}inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {var photoImageView: ImageView = itemView.findViewById(R.id.iv_photo)init {itemView.setOnClickListener(this)}override fun onClick(view: View) {val position = adapterPositionif (position != RecyclerView.NO_POSITION) {val sunsetPhoto = sunsetPhotos[position]val intent = Intent(context, SunsetPhotoActivity::class.java).apply {putExtra(SunsetPhotoActivity.EXTRA_SUNSET_PHOTO, sunsetPhoto)}startActivity(intent)}}}}
}

请注意,我们使用了apply扩展功能将一个对象添加到了意图之外。 提醒一下, apply函数返回作为参数传递给它的对象(即接收器对象)。

9.从URL加载图像

我们将需要毕加索在本节中进行工作,而不是为我们绘制艺术品,而是从互联网上获取图像并进行显示。 当用户滚动应用程序时,我们将在RecyclerView onBindViewHolder()方法内的各自的ImageView中分别显示这些图像。

override fun onBindViewHolder(holder: ImageGalleryAdapter.MyViewHolder, position: Int) {val sunsetPhoto = sunsetPhotos[position]val imageView = holder.photoImageViewPicasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().into(imageView)}

逐步操作,这是对Picasso的通话:

get()方法

这将返回使用以下默认配置初始化的全局Picasso实例(单实例):

  • LRU内存缓存为可用应用程序RAM的15%。
  • 2%存储空间的磁盘缓存,最大为50MB,但不少于5MB。 注意:仅在API 14+上可用。
  • 三个下载线程用于磁盘和网络访问。

请注意,如果这些设置不满足您的应用程序要求,则可以使用Picasso.Builder随意构建对这些配置完全控制的Picasso实例。

val picassoBuilder = Picasso.Builder(context)
// do custom configurations// Specify the {@link Downloader} that will be used for downloading images.
picassoBuilder.downloader() 
// Specify the ExecutorService for loading images in the background.
picassoBuilder.executor()
// Specify the memory Cache used for the most recent images.
picassoBuilder.memoryCache()
// and more
val picasso = picassoBuilder.build()

最后,您调用build()方法以使用自己的配置返回一个Picasso实例。

建议您在Application.onCreate执行此操作,然后使用该方法中的Picasso.setSingletonInstance将其设置为单例实例,以确保Picasso实例是全局实例。

load()方法

load(String path)使用指定的路径启动图像请求。 该路径可以是远程URL,文件资源,内容资源或Android资源。

  • placeholder(int placeholderResId) :加载和显示图像时要使用的本地占位符资源ID或可绘制对象。 在下载图像时显示占位符图像是一种很好的用户体验。

请注意,Picasso首先检查所请求的图像是否在内存缓存中,如果存在,它将从那里显示图像(我们将在后面的部分中进一步讨论Picasso中的缓存)。

其他方法
  • error(int errorResId) :如果无法加载请求的图像(可能是由于网站关闭error(int errorResId)而使用的可绘制对象。
  • noFade() :毕加索总是在要显示在ImageView中的图像中淡入淡出。 如果您不希望这种淡入式动画,只需调用noFade()方法。
  • into(ImageView imageView) :将要放置图像的目标图像视图。

图像调整大小和变换

如果您从中请求图像的服务器未提供所需大小的所需图像,则可以使用resize(int targetWidth, int targetHeight)轻松调整该图像的resize(int targetWidth, int targetHeight) 。 调用此方法将调整图像的大小,然后将其显示在ImageView 。 请注意,尺寸单位为像素(px),而不是dp。

Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).resize(400, 200).into(imageView)

您可以使用resizeDimen(int targetWidthResId, int targetHeightResId)方法resizeDimen(int targetWidthResId, int targetHeightResId)宽度和高度的Android尺寸资源。 此方法会将尺寸大小转换为原始像素,然后在后台调用resize() -将转换后的尺寸(以像素为单位)作为参数传递。

Picasso.get()//....resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)//...

请注意,这些调整大小方法不会考虑长宽比。 换句话说,您的图像宽高比可能会失真。

幸运的是, Picasso为我们提供了一些解决此问题的有用方法:

  • centerCrop() :均匀缩放图像(保持图像的宽高比),以使图像填充给定区域,并尽可能多地显示图像。 如果需要,图像将水平或垂直裁切以适合图像。 调用此方法可在resize()指定的范围内裁剪图像。
  • centerInside() :缩放图像,使两个尺寸均等于或小于请求的范围。 这将使图像居中于resize()指定的边界内。
  • onlyScaleDown() :仅当原始图像大小大于resize()指定的目标大小时,才调整图像大小。
  • fit() :尝试调整图像大小以完全适合目标ImageView的边界。

影像旋转

毕加索(Picasso)具有简单的API,可以旋转图像然后显示该图像。 rotate(float degrees)方法将图像旋转指定的角度。

Picasso.get()//....rotate(90f)//...

在上面的示例中,这会将图像旋转90度。 rotate(float degrees, float pivotX, float pivotY)方法将图像围绕枢轴点旋转指定的角度。

Picasso.get()//....rotate(30f, 200f, 100f)//...

在这里,我们将围绕枢轴点200(100个像素)将图像旋转30度。

转型

除了仅通过旋转图像来操纵图像外,毕加索还为我们提供了在显示图像之前对图像应用自定义转换的选项。

您只需创建一个实现Picasso Transformation接口的类。 然后,您必须重写两个方法:

  • Bitmap transform(Bitmap source) :这会将源位图转换为新的位图。
  • String key() :返回用于转换的唯一键,用于缓存。

创建自定义转换后,只需在Picasso实例上调用transform(Transformation transformation)即可执行该transform(Transformation transformation) 。 请注意,您还可以将Transformation列表传递给transform()

Picasso.get()// ....transform(CropCircleTransformation()).into(imageView)

在这里,我对Picasso Transformations开源Android库中的图像应用了圆形裁剪变换。 该库具有许多可用于使用毕加索的图像的变换,包括用于模糊或灰度图像的变换。 如果要对图像应用一些很酷的转换,请检查一下。

10.初始化适配器

在这里,我们只需使用GridLayoutManager作为布局管理器来创建RecyclerView ,初始化适配器,然后将其绑定到RecyclerView

class MainActivity : AppCompatActivity() {private lateinit var recyclerView: RecyclerViewprivate lateinit var imageGalleryAdapter: ImageGalleryAdapteroverride fun onCreate(savedInstanceState: Bundle?) {//...val layoutManager = GridLayoutManager(this, 2)recyclerView = findViewById(R.id.rv_images)recyclerView.setHasFixedSize(true)recyclerView.layoutManager = layoutManagerimageGalleryAdapter = ImageGalleryAdapter(this, SunsetPhoto.getSunsetPhotos())}override fun onStart() {super.onStart()recyclerView.adapter = imageGalleryAdapter}// ...
}

11.创建明细活动

创建一个新的活动并将其命名为SunsetPhotoActivity 。 我们像SunsetPhoto额外获得了SunsetPhoto并在SunsetPhoto onStart()内加载图像。

class SunsetPhotoActivity : AppCompatActivity() {companion object {const val EXTRA_SUNSET_PHOTO = "SunsetPhotoActivity.EXTRA_SUNSET_PHOTO"}private lateinit var imageView: ImageViewprivate lateinit var sunsetPhoto: SunsetPhotooverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_sunset_photo)sunsetPhoto = intent.getParcelableExtra(EXTRA_SUNSET_PHOTO)imageView = findViewById(R.id.image)}override fun onStart() {super.onStart()Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().into(imageView)}
}

详细布局

这是显示详细信息活动的布局。 它只显示一个ImageView ,它将显示已加载图像的全分辨率版本。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"><ImageViewandroid:id="@+id/image"android:layout_width="match_parent"android:layout_height="wrap_content"android:adjustViewBounds="true"android:scaleType="fitCenter"android:layout_gravity="center"/>
</LinearLayout>

12.毕加索的缓存机制

如果仔细观察,您会注意到,当您重新访问以前加载的图像时,它的加载速度甚至比以前更快。 是什么使它更快? 这就是Picasso的缓存机制。

这是引擎盖下发生的事情。 从Internet加载一次图像后,毕加索会将其缓存在内存和磁盘中,从而节省了重复的网络请求并允许更快地检索图像。 当再次需要该图像时,毕加索将首先检查该图像在内存中是否可用,如果存在,将立即将其返回。 如果该映像不在内存中,毕加索将检查下一个磁盘,如果该磁盘在那里,它将返回该磁盘。 如果不存在,毕加索最终将对该图像发出网络请求并显示它。

总而言之,这是图像请求的幕后工作:内存->磁盘->网络。

但是,根据您的应用程序,您可能要避免缓存-例如,如果所显示的图像可能经常更改并且没有重新加载,则可能会有所不便。

那么如何禁用缓存?

您可以通过调用 memoryPolicy(MemoryPolicy.NO_CACHE) 来避免内存缓存 。 在处理图像请求时,这将仅跳过内存缓存查找。

Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().memoryPolicy(MemoryPolicy.NO_CACHE).into(imageView)

请注意,还有另一个枚举: MemoryPolicy.NO_STORE 。 如果您可以确定只请求一次图像,这将很有用。 应用此选项也不会将图像存储在内存缓存中,因此不会从内存缓存中强制删除其他位图。

但请注意,映像仍将缓存在磁盘上,为防止这种情况,您还可以使用 networkPolicy(@NonNull NetworkPolicy policy, @NonNull NetworkPolicy... additional) ,它采用以下一个或多个枚举值:

  • NetworkPolicy.NO_CACHE :跳过检查磁盘缓存并强制通过网络加载。
  • NetworkPolicy.NO_STORE :跳过将结果存储到磁盘缓存中的过程。
  • NetworkPolicy.OFFLINE :仅通过磁盘缓存强制请求,从而跳过网络。

要完全避免内存和磁盘缓存,只需一个接一个地调用这两个方法:

Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)workPolicy(NetworkPolicy.NO_CACHE).into(imageView)

13.请求监听器

在毕加索中,您可以实现侦听器或回调,以监视图像加载时发出的请求的状态。 如果您在请求上实现Target接口,则将仅调用这些方法之一。

  • void onBitmapFailed(e: Exception?, errorDrawable: Drawable?) :每当无法成功加载图像时触发。 在这里,我们可以访问引发的异常。
  • void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) :每当成功加载图像时触发。 在这里,我们得到了位图以显示给用户。
  • void onPrepareLoad(Drawable placeHolderDrawable) :在提交您的请求之前void onPrepareLoad(Drawable placeHolderDrawable)调用。
Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).into(object : Target {override fun onPrepareLoad(placeHolderDrawable: Drawable?) {}override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {}override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {}})

在这里,您还可以显示然后隐藏进度对话框(如果有的话)。

如果需要,您可以实现另一个回调侦听器,称为Callback 。 该接口只有两个方法: onSuccess()onError(Exception e) 。 当图像请求加载成功时调用前者,而在处理请求时出错则调用后者。

回到我们的图片库应用程序(在SunsetPhotoActivity内部),让我们使用一个Callback对象对显示进行一些修改,该对象将位图设置为ImageView并且还通过提取图像的深色和鲜艳颜色来更改布局的背景色使用Android Palette API 。

因此,将调色板工件包含在应用程序模块的build.gradle中

dependencies {//... implementation 'com.android.support:palette-v7:27.1.1'
}

现在让我们在Picasso请求中实现Callback接口。

override fun onStart() {super.onStart()Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().into(imageView, object : Callback {override fun onSuccess() {val bitmap = (imageView.drawable as BitmapDrawable).bitmaponPalette(Palette.from(bitmap).generate())}override fun onError(e: Exception?) {}})
}fun onPalette(palette: Palette?) {if (null != palette) {val parent = imageView.parent.parent as ViewGroupparent.setBackgroundColor(palette.getDarkVibrantColor(Color.GRAY))}
}

14.测试应用

最后,您可以运行该应用程序! 单击缩略图以获取完整尺寸的图像。


15.优先处理请求

当您想同时在同一屏幕上加载不同的图像时,可以选择订购哪一个比另一个重要。 换句话说,您可以先加载重要的图像。

您只需在Picasso请求实例上调用priority()并传入任何枚举: Priority.LOWPriority.NORMALPriority.HIGH

Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().priority(Picasso.Priority.HIGH).into(imageView)Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().priority(Picasso.Priority.NORMAL).into(imageView)Picasso.get().load(sunsetPhoto.url).placeholder(R.drawable.placeholder).error(R.drawable.error).fit().priority(Picasso.Priority.LOW).into(imageView)

16.标记请求

通过标记您的Picasso请求,您可以继续,暂停或取消与特定标签关联的请求。 根据您的用例,您可以使用字符串或对象标记请求,这些字符串或对象应将请求的范围定义为ContextActivityFragment 。 您可以通过在一个上调用tag(@NonNull Object tag)来轻松标记Picasso请求。 向其传递一个用作标签的Object实例。

您可以对带标签的毕加索请求执行以下操作:

  • pauseTag(Object tag) :暂停与给定标签关联的所有请求。
  • resumeTag(Object tag) :使用给定标签恢复已暂停的请求。
  • cancelTag(Object tag) :使用给定标签取消所有现有请求。
Picasso.get()// ....tag(context

尽管标记请求可以使您对请求有所控制,但是使用标记时应格外小心,因为这可能会导致内存泄漏。 官方文件说的是:

只要此标签暂停和/或有有效请求,毕加索就会保留对该标签的引用。 注意潜在的泄漏。

从文件系统加载

将图像本地加载到应用程序中很简单。

File file = new File("your/pic/file/path/file.png")
Picasso.get().load(file).fit().into(imageView)

结论

不错的工作! 在本教程中,您已经使用Picasso构建了一个完整的图片库应用程序,并且沿途学习了库的工作原理以及如何将其集成到自己的项目中。

您还学习了如何同时显示本地和远程图像,标记请求,确定请求的优先级,以及如何应用图像转换(如调整大小)。 不仅如此,而且您已经看到启用和禁用缓存,错误处理和自定义请求侦听器非常容易。

要了解有关毕加索的更多信息,请参阅其官方文档 。 要了解有关Android编码的更多信息,请在Envato Tuts +上查看我们的其他一些课程和教程!

翻译自:

更多推荐

使用毕加索编码图库Android应用

本文发布于:2024-02-27 14:38:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1706930.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:毕加索   图库   Android

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!