Android Fresco图片处理库用法API英文原文文档2-2(Facebook开源Android图片库)

这是英文文档的第二部分(2):DRAWEE GUIDE

由于第二部分内容多一些,所以分为2个文章发。方便大家查看。

Using the ControllerBuilder

SimpleDraweeViewhas two methods for specifying an image. The easy way is to just callsetImageURI.

If you want more control over how the Drawee displays your image, you can use aDraweeController. This page explains how to build and use one.

Building a DraweeController

Then pass the image request to aPipelineDraweeControllerBuilder. You then specify additional options for the controller:

ControllerListener listener = new BaseControllerListener() {...}DraweeController controller = Fresco.newDraweeControllerBuilder()    .setUri(uri)    .setTapToRetryEnabled(true)    .setOldController(mSimpleDraweeView.getController())    .setControllerListener(listener)    .build();mSimpleDraweeView.setController(controller);

You should always callsetOldControllerwhen building a new controller. This prevents an unneeded memory allocation.

More details:

  • Controller Listeners

Customizing the ImageRequest

For still more advanced usage, you might need to send anImageRequestto the pipeline, instead of merely a URI. An example of this is using apostprocessor.

Uri uri;Postprocessor myPostprocessor = new Postprocessor() { ... }ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)    .setPostprocessor(myPostprocessor)    .build();DraweeController controller = Fresco.newDraweeControllerBuilder()    .setImageRequest(request)    .setOldController(mSimpleDraweeView.getController())    // other setters as you need    .build();

More details:

  • Postprocessors
  • Requesting Multiple Images
  • Resizing and Rotating

Progressive JPEGs

Note: the API in this page is still preliminary and subject to change.

Fresco supports the streaming of progressive JPEG images over the network.

Scans of the image will be shown in the view as you download them. Users will see the quality of the image start out low and gradually become clearer.

This is only supported for the network images. Local images are decoded at once.

Initialization

When youconfigurethe image pipeline, you must pass in an instance ofProgressiveJpegConfig. We plan to remove this requirement.

This example will decode no more than every other scan of the image, using less CPU than decoding every scan.

ProgressiveJpegConfig pjpegConfig = new ProgressiveJpegConfig() {  @Override  public int getNextScanNumberToDecode(int scanNumber) {    return scanNumber + 2;  }      public QualityInfo getQualityInfo(int scanNumber) {    boolean isGoodEnough = (scanNumber >= 5);    return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);  }}ImagePipelineConfig config = ImagePipelineConfig.newBuilder()    .setProgressiveJpegConfig(pjpeg)    .build();

Instead of implementing this interface yourself, you can also instantiate theSimpleProgressiveJpegConfigclass.

At Request Time

Currently, you must explicitly request progressive rendering while building the image request:

Uri uri;ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)    .setProgressiveRenderingEnabled(true)    .build();DraweeController controller = Fresco.newDraweeControllerBuilder()    .setImageRequest(request)    .setOldController(mSimpleDraweeView.getController())    .build();mSimpleDraweeView.setController(controller);

We hope to add support for using progressive images withsetImageURIin a future release.

Animated Images

Fresco supports animated GIF and WebP images.

We support WebP animations, even in the extended WebP format, on versions of Android going back to 2.3, even those that don't have built-in native support.

Playing animations automatically

If you want your animated image to start playing automatically when it comes on-screen, and stop when it goes off, just say so in yourimage request:

Uri uri;DraweeController controller = Fresco.newDraweeControllerBuilder()    .setUri(uri)    .setAutoPlayAnimations(true)    . // other setters    .build();mSimpleDraweeView.setController(controller);

Playing animations manually

You may prefer to directly control the animation in your own code. In that case you'll need to listen for when the image has loaded, so it's even possible to do that.

ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {    @Override    public void onFinalImageSet(        String id,        @Nullable ImageInfo imageInfo,        @Nullable Animatable anim) {    if (anim != null) {      // app-specific logic to enable animation starting      anim.start();    }};Uri uri;DraweeController controller = Fresco.newDraweeControllerBuilder()    .setUri(uri)    .setControllerListener(controllerListener)    // other setters    .build();mSimpleDraweeView.setController(controller);

The controller exposes an instance of theAnimatableinterface. If non-null, you can drive your animation with it:

Animatable animatable = mSimpleDraweeView.getController().getAnimatable();if (animatable != null) {  animatable.start();  // later  animatable.stop();}

Limitations

Animations do not currently supportpostprocessors.

Requesting Multiple Images (Multi-URI)

The methods on this page requiresetting your own image request.

Going from low to high resolution

Suppose you want to show users a high-resolution, slow-to-download image. Rather than let them stare a placeholder for a while, you might want to quickly download a smaller thumbnail first.

You can set two URIs, one for the low-res image, one for the high one:

Uri lowResUri, highResUri;DraweeController controller = Fresco.newDraweeControllerBuilder()    .setLowResImageRequest(ImageRequest.fromUri(lowResUri))    .setImageRequest(ImageRequest.fromUri(highResUri))    .setOldController(mSimpleDraweeView.getController())    .build();mSimpleDraweeView.setController(controller);

Using thumbnail previews

This option is supported only for local URIs, and only for images in the JPEG format.

If your JPEG has a thumbnail stored in its EXIF metadata, the image pipeline can return that as an intermediate result. Your Drawee will first show the thumbnail preview, then the full image when it has finished loading and decoding.

Uri uri;ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)    .setLocalThumbnailPreviewsEnabled(true)    .build();DraweeController controller = Fresco.newDraweeControllerBuilder()    .setImageRequest(request)    .setOldController(mSimpleDraweeView.getController())    .build();mSimpleDraweeView.setController(controller);

Loading the first available image

Most of the time, an image has no more than one URI. Load it, and you're done.

But suppose you have multiple URIs for the same image. For instance, you might have uploaded an image taken from the camera. Original image would be too big to upload, so the image is downscaled first. In such case, it would be beneficial to first try to get the local-downscaled-uri, then if that fails, try to get the local-original-uri, and if even that fails, try to get the network-uploaded-uri. It would be a shame to download the image that we may have already locally.

The image pipeline normally searches for images in the memory cache first, then the disk cache, and only then goes out to the network or other source. Rather than doing this one by one for each image, we can have the pipeline check forallthe images in the memory cache. Only if none were found would disk cache be searched in. Only if none were found there either would an external request be made.

Just create an array of image requests, and pass it to the builder.

Uri uri1, uri2;ImageRequest request = ImageRequest.fromUri(uri1);ImageRequest request2 = ImageRequest.fromUri(uri2);ImageRequest[] requests = { request1, request2 };DraweeController controller = Fresco.newDraweeControllerBuilder()    .setFirstAvailableImageRequests(requests)    .setOldController(mSimpleDraweeView.getController())    .build();mSimpleDraweeView.setController(controller);

Only one of the requests will be displayed. The first one found, whether at memory, disk, or network level, will be the one returned. The pipeline will assume the order of requests in the array is the preference order.

Listening to Download Events

Motivation

Your app may want to execute actions of its own when an image arrives - perhaps make another view visible, or show a caption. You may also want to do something in case of a network failure, like showing an error message to the user.

Loading images is, of course, asynchronous. So you need some way of listening to events posted by the DraweeController. The mechanism for doing this is a controller listener.

Note: this does not allow you to modify the image itself. To do that, use aPostprocessor.

Usage

To use it, you merely define an instance of theControllerListenerinterface. We recommend subclassingBaseControllerListener:

ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {    @Override    public void onFinalImageSet(        String id,        @Nullable ImageInfo imageInfo,        @Nullable Animatable anim) {      if (imageInfo == null) {        return;      }      QualityInfo qualityInfo = imageInfo.getQualityInfo();      FLog.d("Final image received! " +           "Size %d x %d",          "Quality level %d, good enough: %s, full quality: %s",          imageInfo.getWidth(),          imageInfo.getHeight(),          qualityInfo.getQuality(),          qualityInfo.isOfGoodEnoughQuality(),          qualityInfo.isOfFullQuality());    }    @Override     public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {      FLog.d("Intermediate image received");    }    @Override    public void onFailure(String id, Throwable throwable) {      FLog.e(getClass(), throwable, "Error loading %s", id)    }};Uri uri;DraweeController controller = Fresco.newControllerBuilder()    .setControllerListener(controllerListener)    .setUri(uri);    // other setters    .build();mSimpleDraweeView.setController(controller);

onFinalImageSetoronFailureis called for all image loads.

Ifprogressive decodingis enabled, and the image supports it,onIntermediateImageSetis called in response to each scan that gets decoded. Which scans get decoded is determined by yourconfiguration.

Resizing and Rotating

These features require you toconstruct an image requestdirectly.

Resizing Images

Terminology: resizing vs scaling

  • Resizingis a pipeline operation executed in software. It returns a completely new bitmap, of a different size.
  • Scalingis a canvas operation and is usually hardware accelerated. The bitmap itself is always the same size. It just gets drawn upscaled or downscaled.

Should you resize or scale?

Resizing is rarely necessary. Scaling is almost always preferred, even with resizing.

There are several limitations with resizing:

  • Resize is restricted so that it never returns a bigger image. It can only make the image smaller.
  • At the moment, only JPEG images can be resized.
  • There is only a rough control over the resulting image dimensions. Image cannot be resized to the exact size, but will be reduced by one of the supported resize factors. That means that even resized images need to be scaled before displaying.
  • Only the following resize factors are supported:N/8with1 <= N <= 8.
  • Resize is performed in software, which is much slower than hardware-accelerated scaling.

Scaling, on the other hand, doesn't suffer any of these limitations. Scaling uses Android's own built-in facilities to match the image to the view size. On Android 4.0 and later, this is hardware-accelerated on devices with a GPU. Most of the time, it is the fastest and most effective way to display the image in the size you want. The only downside is if the image is much bigger than the view, then the memory gets wasted.

Why should you ever use resizing then? It's a trade-off. You should only ever use resize if you need to display an image that is much bigger than the view in order to save memory. One valid example is when you want to display an 8MP photo taken by the camera in a 1280x720 (roughly 1MP) view. An 8MP image would occupy 32MB of memory when decoded to 4 bytes-per-pixel ARGB bitmap. If resized to the view dimensions, it would occupy less than 4 MB.

When it comes to network images, before thinking about resizing, try requesting the image of the proper size first. Don't request an 8MP high-resolution photo from a server if it can return a smaller version. Your users pay for their data plans and you should be considerate of that. Besides, fetching a smaller image saves internal storage and CPU time in your app.

Only if the server doesn't provide an alternate URI with the smaller image, or if you are using local photos, should you resort to resizing. In all other cases, including upscaling the image, scaling should be used. To scale, simply specify thelayout_widthandlayout_heightof yourSimpleDraweeView, as you would for any Android view. Then specify ascale type.

Resizing

Resizing does not modify the original file. Resizing just resizes an encoded image in memory, prior to being decoded.

This can carry out a much greater range of resizing than is possible with Android's facilities. Images taken with the device's camera, in particular, are often much too large to scale and need to be resized before display on the device.

We currently only support resizing for images in the JPEG format, but this is the most widely used image format anyway and most Android devices with cameras store files in the JPEG format.

To resize pass aResizeOptionsobject when constructing anImageRequest:

Uri uri = "file:///mnt/sdcard/MyApp/myfile.jpg";int width = 50, height = 50;ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)    .setResizeOptions(new ResizeOptions(width, height))    .build();PipelineDraweeController controller = Fresco.newDraweeControllerBuilder()    .setOldController(mDraweeView.getController())    .setImageRequest(request)    .build();mSimpleDraweeView.setController(controller);

Auto-rotation

It's very annoying to users to see their images show up sideways! Many devices store the orientation of the image in metadata in the JPEG file. If you want images to be automatically rotated to match the device's orientation, you can say so in the image request:

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)    .setAutoRotateEnabled(true)    .build();// as above

Modifying the Image

Motivation

Sometimes the image downloaded from the server, or fetched from local storage, is not exactly what you want to display on the screen. If you want to apply custom code to the image in-place, use aPostprocessor.

Example

The following example applies a red mesh to the image:

Uri uri;Postprocessor redMeshPostprocessor = new Postprocessor() {   @Override  public String getName() {    return "redMeshPostprocessor";  }  @Override  public void process(Bitmap bitmap) {    for (int x = 0; x < bitmap.getWidth(); x+=2) {      for (int y = 0; y < bitmap.getHeight(); y+=2) {        bitmap.setPixel(x, y, Color.RED);      }    }  }}ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)    .setPostprocessor(redMeshPostprocessor)    .build();PipelineDraweeController controller = Fresco.newDraweeControllerBuilder()    .setImageRequest(request)    .setOldController(mSimpleDraweeView.getOldController())    // other setters as you need    .build();mSimpleDraweeView.setController(controller);

Things to Know

The image is copied before it enters your postprocessor. The copy of the image in cache isnotaffected by any changes you make in your postprocessor. On Android 4.x and lower, the copy is stored outside the Java heap, just as the original image was.

If you show the same image repeatedly, you must specify the postprocessor each time it is requested. You are free to use different postprocessors on different requests for the same image.

Postprocessors arenotcurrently supported foranimatedimages.

Repeated Postprocessors

What if you want to post-process the same image more than once? No problem at all. Just subclassBaseRepeatedPostprocessor. This class has a methodupdatewhich can be invoked at any time to run the postprocessor again.

The example below allows you to change the color of the mesh at any time.

public class MeshPostprocessor extends BaseRepeatedPostprocessor {   private int mColor = Color.TRANSPARENT;  public void setColor(int color) {    mColor = color;    update();  }  @Override  public String getName() {    return "meshPostprocessor";  }  @Override  public void process(Bitmap bitmap) {    for (int x = 0; x < bitmap.getWidth(); x+=2) {      for (int y = 0; y < bitmap.getHeight(); y+=2) {        bitmap.setPixel(x, y, mColor);      }    }  }}MeshPostprocessor meshPostprocessor = new MeshPostprocessor();/// setPostprocessor as in above examplemeshPostprocessor.setColor(Color.RED);meshPostprocessor.setColor(Color.BLUE);

You should have still have onePostprocessorinstance per image request, as internally the class is stateful.

Image Requests

If you need anImageRequestthat consists only of a URI, you can use the helper methodImageRequest.fromURI. Loadingmultiple-imagesis a common case of this.

If you need to tell the image pipeline anything more than a simple URI, you need to useImageRequestBuilder:

Uri uri;ImageDecodeOptions decodeOptions = ImageDecodeOptions.newBuilder()    .setBackgroundColor(Color.GREEN)    .build();ImageRequest request = ImageRequestBuilder    .newBuilderWithSource(uri)    .setAutoRotateEnabled(true)    .setLocalThumbnailPreviewsEnabled(true)    .setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)    .setProgressiveRenderingEnabled(false)    .setResizeOptions(new ResizeOptions(width, height))    .build();

Fields in ImageRequest

  • uri- the only mandatory field. SeeSupported URIs
  • autoRotateEnabled- whether to enableauto-rotation.
  • progressiveEnabled- whether to enableprogressive loading.
  • postprocessor- component topostprocessthe decoded image.
  • resizeOptions- desired width and height. Use with caution. SeeResizing.

Lowest Permitted Request Level

The image pipeline follows adefinite sequencein where it looks for the image.

  1. Check the bitmap cache. This is nearly instant. If found, return.
  2. Check the encoded memory cache. If found, decode the image and return.
  3. Check the "disk" (local storage) cache. If found, load from disk, decode, and return.
  4. Go to the original file on network or local file. Download, resize and/or rotate if requested, decode, and return. For network images in particular, this will be the slowest by a long shot.

ThesetLowestPermittedRequestLevelfield lets you control how far down this list the pipeline will go. Possible values are:

  • BITMAP_MEMORY_CACHE
  • ENCODED_MEMORY_CACHE
  • DISK_CACHE
  • FULL_FETCH

This is useful in situations where you need an instant, or at least relatively fast, image or none at all.

Writing Custom Views

DraweeHolders

There will always be times whenDraweeViewswon't fit your needs. You may need to show additional content inside the same view as your image. You might to show multiple images inside a single view.

We provide two alternate classes you can use to host your Drawee:

  • DraweeHolderfor a single image
  • MultiDraweeHolderfor multiple images

Responsibilities of custom views

Android lays out View objects, and only they get told of system events.DraweeViewshandle these events and use them to manage memory effectively. When using the holders, you must implement some of this functionality yourself.

Handling attach/detach events

Your app may leak memory if this steps are not followed.

There is no point in images staying in memory when Android is no longer displaying the view - it may have scrolled off-screen, or otherwise not be drawing. Drawees listen for detaches and release memory when they occur. They will automatically restore the image when it comes back on-screen.

All this is automatic in aDraweeView,but won't happen in a custom view unless you handle four system events. These must be passed to theDraweeHolder. Here's how:

DraweeHolder mDraweeHolder;@Overridepublic void onDetachedFromWindow() {  super.onDetachedFromWindow();  mDraweeHolder.onDetach();}@Overridepublic void onStartTemporaryDetach() {  super.onStartTemporaryDetach();  mDraweeHolder.onDetach();}@Overridepublic void onAttachedToWindow() {  super.onAttachedToWindow();  mDraweeHolder.onAttach();}@Overridepublic void onFinishTemporaryDetach() {  super.onFinishTemporaryDetach();  mDraweeHolder.onAttach();}

Handling touch events

If you have enabledtap to retryin your Drawee, it will not work unless you tell it that the user has touched the screen. Like this:

@Overridepublic boolean onTouchEvent(MotionEvent event) {  return mDraweeHolder.onTouchEvent(event) || super.onTouchEvent(event);}

Your custom onDraw

You must call

Drawable drawable = mDraweeHolder.getHierarchy().getTopLevelDrawable();drawable.setBounds(...);

or the Drawee won't appear at all.

  • Do not downcast this Drawable.
  • Do not translate it.

Other responsibilities

  • OverrideverifyDrawable:
@Overrideprotected boolean verifyDrawable(Drawable who) {  if (who == mDraweeHolder.getHierarchy().getTopLevelDrawable()) {    return true;  }  // other logic for other Drawables in your view, if any}
  • Make sureinvalidateDrawableinvalidates the region occupied by your Drawee.

Constructing a DraweeHolder

This should be done carefully.

Arranging your Constructors

We recommend the following pattern for constructors:

  • Override all three of the three View constructors.
  • Each constructor calls its superclass counterpart and then a privateinitmethod.
  • All of your initialization happens ininit.

That is, do not use thethisoperator to call one constructor from another.

This approach guarantees that the correct initialization is called no matter what constructor is used. It is in theinitmethod that your holder is created.

Creating the Holder

If possible, always create Drawees when your view gets created. Creating a hierarchy is not cheap so it's best to do it only once.

class CustomView extends View {  DraweeHolder<GenericDraweeHierarchy> mDraweeHolder;  // constructors following above pattern  private void init() {    GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources());      .set...      .set...      .build();    mDraweeHolder = DraweeHolder.create(hierarchy, context);  }}

Setting an image

Use acontroller builder, but callsetControlleron the holder instead of a View:

DraweeController controller = Fresco.newControllerBuilder()    .setUri(uri)    .setOldController(mDraweeHolder.getController())    .build();mDraweeHolder.setController(controller);

MultiDraweeHolder

Instead of using aDraweeHolder, use aMultiDraweeHolder. There areadd,remove, andclearmethods for dealing with Drawees:

MultiDraweeHolder<GenericDraweeHierarchy> mMultiDraweeHolder;private void init() {  GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources());    .set...    .build();  mMultiDraweeHolder = new MultiDraweeHolder<GenericDraweeHierarchy>();  mMultiDraweeHolder.add(new DraweeHolder<GenericDraweeHierarchy>(hierarchy, context));  // repeat for more hierarchies}

You must override system events, set bounds, and do all the same responsibilities as for a singleDraweeHolder.

Gotchas

Don't downcast

It is tempting to downcast objects returns by Fresco classes into actual objects that appear to give you greater control. At best, this will result in fragile code that gets broken next release; at worst, it will lead to very subtle bugs.

Don't use getTopLevelDrawable

DraweeHierarchy.getTopLevelDrawable()shouldonlybe used by DraweeViews. Client code should almost never interact with it.

The sole exception iscustom views. Even there, the top-level drawable should never be downcast. We may change the actual type of the drawable in future releases.

Don't re-use DraweeHierarchies

Never callDraweeView.setHierarchywith the same argument on two different views. Hierarchies are made up of Drawables, and Drawables on Android cannot be shared among multiple views.

Don't use Drawables in more than one DraweeHierarchy

This is for the same reason as the above. Drawables cannot be shared in multiple views.

You are completely free, of course, to use the same resourceID in multiple hierarchies and views. Android will create a separate instance of each Drawable for each view.

Don't set images directly on a DraweeView

CurrentlyDraweeViewis a subclass of Android's ImageView. This has various methods to set an image (such as setImageBitmap, setImageDrawable)

If you set an image directly, you will completely lose yourDraweeHierarchy, and will not get any results from the image pipeline.

Don't use ImageView attributes or methods with DraweeView

Any XML attribute or method of ImageView not found inViewwill not work on a DraweeView. Typical cases arescaleType,src, etc. Don't use those. DraweeView has its own counterparts as explained in the other sections of this documentation. Any ImageView attrribute or method will be removed in the upcoming release, so please don't use those.



更多相关文章

  1. Android官方文档翻译 三 1.1Creating an Android Project
  2. Android安卓开发官方文档国内镜像
  3. Android 官方文档:(二)应用清单 —— 2.2 <action>标签
  4. Android [Camera 源码] 相机 HAL3(Camera3) Google官方文档(二)
  5. Android 中文 API 文档 (45) ―― AbsoluteLayout.LayoutParams
  6. Android中文文档v0.1 beta低调发布,期待更多同学来参加review
  7. Android图形报表之AchartEngine(附开发包+API文档)

随机推荐

  1. EditText编辑框
  2. Android解析自定义xml文件--Pull解析xml
  3. Android JellyBean Keyguard锁屏
  4. android webView错误处理
  5. Android中内存占用的含义:(VSS,PSS,RSS,USS)
  6. Android中各种drawable的使用
  7. 用android中的Preferencescreen 启动一个
  8. android studio上imageloader初探
  9. linux android 如何把android项目打成jar
  10. android C编程技巧 及 C/C++开发测试