关于Fresco加载图片的处理,例如旋转、裁剪切割图片,在官方文档也都有提到,只是感觉写的不太详细,正好最近项目里有类似需求,所以分享一些使用小tip,后面的朋友就不用再走弯路浪费时间了。(测试图片分辨率1200*800)

原图:

裁剪图片实现

旋转图片

/**
* 旋转图片
*
* @param rotate ,例如:RotationOptions.ROTATE_90
*/
private void rotate(SimpleDraweeView img, int rotate) {
RotationOptions rotationOptions = RotationOptions.forceRotation(rotate);
ImageRequest build = ImageRequestBuilder.newBuilderWithSource(getUriForFresco(this, R.mipmap.test_img))
.setRotationOptions(rotationOptions)
.build();
PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
.setImageRequest(build)
.build();
mImageView.setController(controller);
}

使用效果:

监听图片下载

首先构造监听器:

//监听图片下载进度,这里只重写了onFinalImageSet,当图片下载完成时获得图片宽高等信息
ControllerListener controllerListener = new BaseControllerListener<ImageInfo>() {
@Override
public void onFinalImageSet(String id, com.facebook.imagepipeline.image.ImageInfo imageInfo, Animatable animatable) {
int viewWidth = imageInfo.getWidth();
int viewHeight = imageInfo.getHeight();
Toast.makeText(MainActivity.this, viewWidth + "--" + viewHeight, Toast.LENGTH_SHORT).show();
}
};
/**
* 获得图片宽高
*
* @param controllerListener 图片下载监听器
*/
private void getImageInfo(ControllerListener<? super ImageInfo> controllerListener) {
PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
.setControllerListener(controllerListener)
.setUri(getUriForFresco(this, R.mipmap.test_img))
.build();
mImageView.setController(controller);
}

如果我想要1:1在手机端展示呢?

我首先想到的是1:1按照图片的尺寸设置SimpleDraweeView的宽高并设置缩放方式为fitXY,但是果不其然,view超出屏幕的部分是无效的。

裁剪切割图片

既然view超出屏幕无效,那就曲线救国,让图片超出屏幕部分不显示在view里就好了。

裁剪图片首先要写一个processor类:

/**
* 切割图片processor类
* 四个成员变量和createBitmap时的参数一致,即起点的X/Y坐标、要裁剪的宽高。因为项目里还涉及到缩放,所以我调整了下参数设成百分比方便换算
*/
public class CutProcess extends BasePostprocessor {
private float mBeginXPercent;
private float mBeginYPercent;
private float mCutWidthPercent;
private float mCutHeightPercent; public CutProcess(float beginXPercent, float beginYPercent, float cutWidthPercent, float cutHeightPercent) {
this.mBeginXPercent = beginXPercent;
this.mBeginYPercent = beginYPercent;
this.mCutWidthPercent = cutWidthPercent;
this.mCutHeightPercent = cutHeightPercent;
} @Override
public CloseableReference<Bitmap> process(
Bitmap sourceBitmap,
PlatformBitmapFactory bitmapFactory) {
int viewWidth = sourceBitmap.getWidth();
int viewHeight = sourceBitmap.getHeight();
int beginx = (int) (mBeginXPercent * viewWidth);
int beginy = (int) (mBeginYPercent * viewHeight);
int width = (int) (mCutWidthPercent * viewWidth);
int height = (int) (mCutHeightPercent * viewHeight);
CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap
(sourceBitmap, beginx, beginy, width, height);
return CloseableReference.cloneOrNull(bitmapRef);
}
}

然后在ImageRequest里setProcessor:

/**
* 裁剪图片
* @param processor
*/
private void cutPic(BasePostprocessor processor) {
ImageRequest build = ImageRequestBuilder.newBuilderWithSource(getUriForFresco(this, R.mipmap.test_img))
.setPostprocessor(processor)
.build(); PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
.setImageRequest(build)
.build(); mImageView.setController(controller);
}

调用方法:

mImageView.setLayoutParams(new RelativeLayout.LayoutParams(600, 400));
CutProcess cutProcess = new CutProcess(0, 0, 0.5f, 0.5f);
cutPic(cutProcess);

图片是1200800的,这里设置view的宽高为600400,可以看到图片成功裁剪只保留原图左上四分之一。通过设置view宽高,配合裁剪图片,即可达到1:1显示的效果。

旋转+裁剪

如果是要旋转90度后再裁剪呢?那还不简单,直接在裁剪的基础上,在ImageRequest里调用旋转方法不就好了。

/**
* 旋转+裁剪图片
* @param processor
*/
private void rotateAndcutPic(BasePostprocessor processor, int rotate) {
RotationOptions rotationOptions = RotationOptions.forceRotation(rotate); ImageRequest build = ImageRequestBuilder.newBuilderWithSource(getUriForFresco(this, R.mipmap.test_img))
.setPostprocessor(processor)
.setRotationOptions(rotationOptions)
.build(); PipelineDraweeController controller = (PipelineDraweeController) Fresco.newDraweeControllerBuilder()
.setImageRequest(build)
.build(); mImageView.setController(controller);
}

然后调用:

//例如我需要旋转90度且宽度不变,高度方向裁剪掉一半(即保留(0,0)-(1200,400))
mImageView.setLayoutParams(new RelativeLayout.LayoutParams(400, mScreenHeight));
CutProcess cutProcess = new CutProcess(0, 0, 1f, 0.5f);
rotateAndcutPic(cutProcess, RotationOptions.ROTATE_90);

然而得到的并不是我们想要的

可以看到得到的是左半边(0,0)-(600,800)的图,即宽度方向被裁剪掉一般,高度方向不变,明明我在cutProcess里是设置宽度方向不变,高度方向裁剪50%,但是因为旋转了90度,结果却正好相反。难道是因为旋转90度后横纵方向也发生改变?那调换一下横纵方向的切割比例试试看:

CutProcess cutProcess = new CutProcess(0, 0, 0.5f, 1f);
rotateAndcutPic(cutProcess, RotationOptions.ROTATE_90);

可以看到,调换横纵切割比例后,却得到的是下半边(0,400)-(1200,800)。还是不正确,难道是原点也改变了?再测试一下,如果要裁剪后保留右下四分之一(600,400)-(1200,800)区域,正常无旋转的情况下是这样的:

mImageView.setLayoutParams(new RelativeLayout.LayoutParams(600, 400));
CutProcess cutProcess = new CutProcess(0.5f, 0.5f, 0.5f, 0.5f);
rotateAndcutPic(cutProcess, RotationOptions.NO_ROTATION);

但如果旋转270度后,同样代码得到的结果却是这样的:

看到这里我们就清楚了,旋转图片后,其实(0,0)点,也就是所谓的原点也随之变换。默认情况下,原点是(0,0),顺时针旋转90度后,原点就变成了(0,800),以此类推旋转180度原点为(1200,800),旋转270度原点为(1200,0)(和旋转后的图片的左上角相对应)。虽然是在构建ImageRequest时同时传入旋转和裁剪参数的,但实际上可以看作是先完成了旋转,然后在旋转后的基础上,以屏幕的左上角为原点,左上角往右为x正方向,左上角往下为y正方向。

小试牛刀一下,旋转270度后,想要裁剪后只保留原图的左上四分之一(0,0)-(600,600),那推测就应该是(0, 0.5f, 0.5f, 0.5f)。

mImageView.setLayoutParams(new RelativeLayout.LayoutParams(400, 600));
CutProcess cutProcess = new CutProcess(0, 0.5f, 0.5f, 0.5f);
rotateAndcutPic(cutProcess, RotationOptions.ROTATE_270);

Bingo!推测正确。

旋转+裁剪就是这个原点的变换要注意下。另外看代码里的几个方法,裁剪、旋转、获得宽高等,有没有觉得老是要重复写PipelineDraweeController、ImageRequest的代码好麻烦啊。其实裁剪、旋转等方法无非也就是添加一个参数,类似这种可变参数的复杂类的构造可以使用Builder模式封装一下。封装代码就不贴在这里了。demo下载地址

//Builder模式封装后
new FrescoBuilder(mImageView, getUriForFresco(this, R.mipmap.test_img))
.cutPic(0f, 0.5f, 0.5f, 0.5f) //裁剪
.setRotate(RotationOptions.ROTATE_270) //旋转
.setControllerListener(controllerListener) //设置监听
.build();

使用Matrix实现

继承 SimpleDraweeView 自定义控件,使用Matrix实现旋转缩放:

public class MyFresco extends SimpleDraweeView {

    private Matrix mMatrix;
private float mScaleX = 1f;
private float mScaleY = 1f;
private int mViewWidth = -1;
private int mViewHeight = -1;
private RectF mDisplayRect = new RectF();
private int mDegree = -1; public MyFresco(Context context, GenericDraweeHierarchy hierarchy) {
super(context, hierarchy);
init();
} public MyFresco(Context context) {
super(context);
init();
} public MyFresco(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public void setViewInfo(int width, int Height) {
mViewWidth = width;
mViewHeight = Height;
} private void init() {
mMatrix = new Matrix();
mMatrix.postScale(mScaleX, mScaleY);
} /**
* 缩放
* @param scaleX 缩放倍数
*/
public void setScale(float scaleX, float scaleY) {
mScaleX = scaleX;
mScaleY = scaleY;
mMatrix.postScale(scaleX, scaleY);
invalidate();
} /**
* 旋转
* @param degree 角度
*/
public void rotate(int degree) {
if (mDegree == -1) {
mDegree = degree;
if (mDegree != 0) {
mMatrix.postRotate(degree);
invalidate();
if (mDegree == 90) {
//旋转后图片超出边界,所以要再做平移
mMatrix.postTranslate(getRectWidth(), 0);
} else if (mDegree == 180) {
mMatrix.postTranslate(getRectWidth(), getRectHieght());
} else if (mDegree == 270) {
mMatrix.postTranslate(0, getRectHieght());
}
}
} else {
mDegree += degree;
mMatrix.postRotate(degree); //getRectWidth是旋转后的width
invalidate();
mMatrix.postTranslate(getRectWidth(), 0);
}
invalidate();
} /**
* 还原设置
*/
public void reset() {
mScaleX = 1f;
mScaleY = 1f;
mMatrix = new Matrix();
mMatrix.setScale(mScaleX, mScaleY);
mViewWidth = -1;
mViewHeight = -1;
mDegree = -1;
} /**
* 获得旋转后超出边界的高度
* @return
*/
public float getRectHieght() {
RectF displayRect = getDisplayRect(mMatrix);
if (displayRect != null) {
return displayRect.height();
} else {
return -1;
}
} /**
* 获得旋转后超出边界的宽度
* @return
*/
public float getRectWidth() {
RectF displayRect = getDisplayRect(mMatrix);
if (displayRect != null) {
return displayRect.width();
} else {
return -1;
}
} private RectF getDisplayRect(Matrix matrix) {
if (mViewWidth == -1 || mViewHeight == -1) {
return null;
}
mDisplayRect.set(0.0F, 0.0F, mViewWidth, mViewHeight);
getHierarchy().getActualImageBounds(mDisplayRect);
//将matrix映射到rectf
matrix.mapRect(mDisplayRect);
return mDisplayRect;
} @Override
protected void onDraw(Canvas canvas) {
int save = canvas.save();
canvas.concat(mMatrix);
super.onDraw(canvas);
canvas.restoreToCount(save);
}
}

就是酱~

Fresco 二三事:图片处理之旋转、缩放、裁剪切割图片的更多相关文章

  1. 图片IO域 旋转画面的组态 图片是4个静止的风扇 PLC的MW6为风扇指针..

    图片IO域 旋转画面的组态 图片是4个静止的风扇 PLC的MW6为风扇指针.. Plc在循环中断组织块 OB35 中 将MW6 每100ms 加1 加到4 清0 图片[MW6] MW6 是图片指针 对 ...

  2. wpf图片浏览器,实现缩放平移操作图片切换等功能

    wpf经常要用到控件来查看图片,尤其是高清图片,于是做了一个例子: 1.定义图片的队列,用list来存. private readonly List<string> files; 2.切换 ...

  3. iOS transform解决连续多次旋转缩放,实现图片旋转缩放效果

    一.需求 实现imageView的缩放旋转效果,一般有两种方式: 1.底层加scrollview,利用scrollview的属性实现.(推荐这种,这是我比较后发现的,手势做缩放旋转会有点弊端) 2.利 ...

  4. 转载:Android调用相册、拍照实现缩放、切割图片

    好几天没有写博客了,感觉都有点懈怠了.笔者参加了大学生第二届软件设计大赛,这几天 一直在弄大赛的事情,没有花些时间来整理博客.好在经过一些时日比赛的东西也弄得差不多了, 接下来就是将这段时间学习里面有 ...

  5. android项目 之 记事本(12) ----- 图片的等比例缩放及给图片加入边框

    本文是自己学习所做笔记.欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 在Android的UI开发中常常会遇到图片的缩放,就比方记事本,如今的图片都比較 ...

  6. VS+Qt+Halcon——显示图片,实现鼠标缩放、移动图片

    摘要 本篇博文记录一下,用VS+Qt+Halcon实现对图片的读取以及鼠标缩放,移动(鼠标事件调用了halcon自带的算子)的过程.以及遇到的坑..... 先来看一下动态效果图: 主要控件: 添加一个 ...

  7. jquery.roundabout.js图片叠加3D旋转插件多功能图片翻转切换效果

    http://www.17sucai.com/pins/4880.html DEMO演示地址:http://www.17sucai.com/pins/demoshow/4880

  8. canvas 图片拖拽旋转之二——canvas状态保存(save和restore)

    引言 在上一篇日志“canvas 图片拖拽旋转之一”中,对坐标转换有了比较深入的了解,但是仅仅利用坐标转换实现的拖拽旋转,会改变canvas坐标系的状态,从而影响画布上其他元素的绘制.因此,这个时候需 ...

  9. java多图片上传--前端实现预览--图片压缩 、图片缩放,区域裁剪,水印,旋转,保持比例。

    java多图片上传--前端实现预览 前端代码: https://pan.baidu.com/s/1cqKbmjBSXOhFX4HR1XGkyQ 解压后: java后台: <!--文件上传--&g ...

随机推荐

  1. T-SQL——基础语法

    目录 0. 定义变量 1. 批处理 2. 条件逻辑--IF 3. 逻辑分支--CASE 4. 循环语句--WHILE 5. 附录--关于CASE表达式的实现函数 6. 参考 shanzm-2020年1 ...

  2. Kubernetes日志系统新贵Loki-Stack

    Loki简介 Grafana Loki是可以组成功能齐全的日志记录堆栈的一组组件. 与其他日志记录系统不同,Loki是基于仅索引有关日志的元数据的想法而构建的:标签(就像Prometheus标签一样) ...

  3. 【常见踩坑】】USB调试安装失败(Installation failed with message INSTALL_CANCELED_BY_USER)

    [参考]http://www.cnblogs.com/liushilin/p/6553918.html 问题:在USB安装调试(小米手机),出现如下错误 解决:1.小米手机解决办法见参考.登录小米账号 ...

  4. 20200322_【转载】关于C#中使用SQLite自适应Any CPU程序的说明

    本文转载, 原文链接: http://luyutao.com/2016/09/14/csharp-sqlite-any-cpu.html 在C#中如果要使程序自适应32位和64位系统,只需要将项目的& ...

  5. 通过Apache Hudi和Alluxio建设高性能数据湖

    T3出行的杨华和张永旭描述了他们数据湖架构的发展.该架构使用了众多开源技术,包括Apache Hudi和Alluxio.在本文中,您将看到我们如何使用Hudi和Alluxio将数据摄取时间缩短一半.此 ...

  6. PyQt(Python+Qt)学习随笔:通过QMainWindow的resizeDocks方法调整QDockWidget停靠窗大小

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 QMainWindow的resizeDocks用于将QMainWind ...

  7. php 序列化键、值逃逸

    转自https://www.cnblogs.com/wangtanzhi/p/12261610.html PHP反序列化的对象逃逸(很重要一点,引号的匹配是从左到右按字符串长度进行匹配) 任何具有一定 ...

  8. Kubernetes的Local Persistent Volumes使用小记

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. java中的反射(三)

    目录 一.反射 1.class类 2.访问字段 3.调用方法 4.调用构造方法 5.获取继承对象 6.动态代理 二.sping中的反射 本篇转自:https://depp.wang/2020/05/0 ...

  10. Codeforces Edu Round 62 A-E

    A. Detective Book 模拟题,有一些细节需要注意. #include <cstdio> #include <iostream> #include <cmat ...