在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。
- 使用简单
- 可配置度高,自适应程度高
- 支持常见图片格式 Jpg png gif webp
- 支持多种数据源 网络、本地、资源、Assets 等
- 高效缓存策略 支持Memory和Disk图片缓存 默认Bitmap格式采用RGB_565内存使用至少减少一半
- 生命周期集成 根据Activity/Fragment生命周期自动管理请求
- 高效处理Bitmap 使用Bitmap Pool使Bitmap复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力.
然后呢,这篇文章的重点不完全放在了Glide上,重要的是完成了一个demo——RecyclerView+CardView+Glide加载图片实现瀑布流,写过瀑布流的都知道,这中间有个bug,滑动的时候item位置会变动,这效果就很难受了。本文参考了一位前辈的方法,将其移植到我们的demo中来,算是基本解决了这个问题。
去github上查看最新添加依赖:https://github.com/bumptech/glide
repositories {
mavenCentral() // jcenter() works as well because it pulls from Maven Central
}
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
}
如果你需要各种变换效果,你可以继续添加:
compile 'jp.wasabeef:glide-transformations:2.0.1'
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0'
最简单的使用:
Glide.with(this)
.load("http://inthecheesefactory.com/uploads/source/nestedfragment/fragments.png")
.into(imageView);
with()的参数:
load()的使用:
Glide基本可以load任何可以拿到的媒体资源,load的参数也不局限于String类型。
可以拿到的资源:
load()的参数类型:
看一下Demo效果:
Glide基本使用的demo:
private void initData() {
mTvGlide1.setText("加载网络图片");
Glide.with(this).load("http://7xi8d6.com1.z0.glb.clouddn.com/2017-04-28-18094719_120129648541065_8356500748640452608_n.jpg").into(mIvGlide1);
mTvGlide2.setText("加载资源图片");
Glide.with(this).load(R.mipmap.ic_launcher).into(mIvGlide2);
mTvGlide3.setText("加载手机SD卡图片");
String path = Environment.getExternalStorageDirectory() + "/back.jpg";
File file = new File(path);
Uri uri = Uri.fromFile(file);
Glide.with(this).load(uri).into(mIvGlide3);
mTvGlide4.setText("加载网络gif");
String gifUrl = "http://b.hiphotos.baidu.com/zhidao/pic/item/faedab64034f78f066abccc57b310a55b3191c67.jpg";
Glide.with(this).load(gifUrl).into(mIvGlide4);
mTvGlide5.setText("加载资源gif");
Glide.with(this).load(R.drawable.loading).into(mIvGlide5);
mTvGlide6.setText("加载本地gif");
String path1 = Environment.getExternalStorageDirectory() + "/meinv2.jpg";
File file1 = new File(path1);
Uri uri1 = Uri.fromFile(file1);
Glide.with(this).load(uri1).placeholder(R.mipmap.ic_launcher).into(mIvGlide6);
mTvGlide7.setText("加载本地小视频和快照");
String path2 = Environment.getExternalStorageDirectory() + "/video.mp4";
File file2 = new File(path2);
Uri uri2 = Uri.fromFile(file2);
Glide.with(this).load(uri2).placeholder(R.mipmap.ic_launcher).into(mIvGlide7);
mTvGlide8.setText("设置缩略图比例,然后,先加载缩略图,再加载原图");
Glide.with(this).load("http://7xi8d6.com1.z0.glb.clouddn.com/2017-04-28-18094719_120129648541065_8356500748640452608_n.jpg").thumbnail(0.1f).centerCrop().placeholder(R.mipmap.ic_launcher).into(mIvGlide8);
mTvGlide9.setText("先建立一个缩略图对象,然后,先加载缩略图,再加载原图");
DrawableRequestBuilder builder = Glide.with(this).load("http://7xi8d6.com1.z0.glb.clouddn.com/2017-04-28-18094719_120129648541065_8356500748640452608_n.jpg");
Glide.with(this).load(uri2).thumbnail(builder).centerCrop().placeholder(R.mipmap.ic_launcher).into(mIvGlide9);
}
Glide的各种变换效果:
效果都在注释中,你可以看效果来进行选择哪种变换。有些我也叫不出是啥,尴尬….还是看demo效果吧。
@Override
public void onBindViewHolder(TransViewHolder holder, int position) {
int integer = Integer.parseInt(mList.get(position));
holder.mTvTran.setText("样式"+(position+1));
switch (integer) {
//五角星形的外框Mask
case 1: {
int width = UIUtils.dip2px(mContext, 133.33f);
int height = UIUtils.dip2px(mContext, 126.33f);
Glide.with(mContext)
.load(R.drawable.demo)
.override(width, height)
.bitmapTransform(new CenterCrop(mContext),
new MaskTransformation(mContext, R.drawable.mask_starfish))
.into(holder.mIvTran);
break;
}
//点9图片的外框Mask
case 2: {
int width = UIUtils.dip2px(mContext, 150.0f);
int height = UIUtils.dip2px(mContext, 100.0f);
Glide.with(mContext)
.load(R.drawable.demo)
.override(width, height)
.bitmapTransform(new CenterCrop(mContext),
new MaskTransformation(mContext, R.drawable.mask_chat_right))
.into(holder.mIvTran);
break;
}
//裁剪图片的上方部分区域
case 3:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(
new CropTransformation(mContext, 300, 100, CropTransformation.CropType.TOP))
.into(holder.mIvTran);
break;
//裁剪图片的上方100-300区域
case 4:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new CropTransformation(mContext, 300, 100))
.into(holder.mIvTran);
break;
//裁剪图片的下方部分区域
case 5:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(
new CropTransformation(mContext, 300, 100, CropTransformation.CropType.BOTTOM))
.into(holder.mIvTran);
break;
//显示方形图
case 6:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new CropSquareTransformation(mContext))
.into(holder.mIvTran);
break;
//显示圆形图
case 7:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new CropCircleTransformation(mContext))
.into(holder.mIvTran);
break;
//彩色滤镜样式
case 8:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new ColorFilterTransformation(mContext, Color.argb(80, 255, 0, 0)))
.into(holder.mIvTran);
break;
//灰度图
case 9:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new GrayscaleTransformation(mContext))
.into(holder.mIvTran);
break;
//边框圆角化图片
case 10:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new RoundedCornersTransformation(mContext, 30, 0,
RoundedCornersTransformation.CornerType.BOTTOM))
.into(holder.mIvTran);
break;
//毛玻璃效果
case 11:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new BlurTransformation(mContext, 25))
.into(holder.mIvTran);
break;
case 12:
Glide.with(mContext)
.load(R.drawable.demo)
.bitmapTransform(new ToonFilterTransformation(mContext))
.into(holder.mIvTran);
break;
case 13:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new SepiaFilterTransformation(mContext))
.into(holder.mIvTran);
break;
case 14:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new ContrastFilterTransformation(mContext, 2.0f))
.into(holder.mIvTran);
break;
case 15:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new InvertFilterTransformation(mContext))
.into(holder.mIvTran);
break;
case 16:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new PixelationFilterTransformation(mContext, 20))
.into(holder.mIvTran);
break;
case 17:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new SketchFilterTransformation(mContext))
.into(holder.mIvTran);
break;
case 18:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(
new SwirlFilterTransformation(mContext, 0.5f, 1.0f, new PointF(0.5f, 0.5f)))
.into(holder.mIvTran);
break;
case 19:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new BrightnessFilterTransformation(mContext, 0.5f))
.into(holder.mIvTran);
break;
case 20:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new KuwaharaFilterTransformation(mContext, 25))
.into(holder.mIvTran);
break;
case 21:
Glide.with(mContext)
.load(R.drawable.check)
.bitmapTransform(new VignetteFilterTransformation(mContext, new PointF(0.5f, 0.5f),
new float[] { 0.0f, 0.0f, 0.0f }, 0f, 0.75f))
.into(holder.mIvTran);
break;
}
}
Glide的小综合案列(RecyclerView+CardView加载美女图片):
使用CardView:
app:cardBackgroundColor //这是设置背景颜色
app:cardCornerRadius //这是设置圆角大小
app:cardElevation //这是设置z轴的阴影
app:cardMaxElevation //这是设置z轴的最大高度值
app:cardUseCompatPadding //是否使用CompatPadding
app:cardPreventCornerOverlap //是否使用PreventCornerOverlap
app:contentPadding // 设置内容的padding
app:contentPaddingLeft //设置内容的左padding
app:contentPaddingTop //设置内容的上padding
app:contentPaddingRight //设置内容的右padding
app:contentPaddingBottom //设置内容的底padding
我的item_mm.xml如下,其中CardView的高度要设置为wrap_content,ImageView的高度也要设置为wrap_content。别问我为什么,我也不准备回答这个问题,因为我也不知道,你可以设置成别的试试就知道了。
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="3dp"
app:cardElevation="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_mm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"/>
android.support.v7.widget.CardView>
配置权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
编写Addapter类,在onBindViewHolder中使用Glide通过URL加载图片即可:
public class MeiziAdapter extends RecyclerView.Adapter<MeiziAdapter.ViewHolder>{
private Context mContext;
private List mData;
public MeiziAdapter(Context context, List data) {
mContext = context;
mData = data;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_meizi, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
String url = mData.get(position).getResults().get(0).getUrl();
Glide.with(mContext).load(url).into(holder.mIvMm);
}
@Override
public int getItemCount() {
if(mData == null)
return 0;
return mData.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.iv_mm)
ImageView mIvMm;
ViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
}
问题:现在我们已经可以实现加载图片的效果了,但是在上下滑动时,出现重新定义宽高,导致cardview滑动状态。这篇文章也提到这件事。我参考一个开源的软件中的写法,附在下面:
在Adapter中重写getItemViewType方法;
@Override
public int getItemViewType(int position) {
WindowManager windowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
Display display = windowManager.getDefaultDisplay();
display.getMetrics(dm);
final int screenWidth = dm.widthPixels;
return Math.round((float) screenWidth / (float) mData.get(position).getHeight() * 10f);
}
对我们的Meizi.java添加一个属性——高度height,并生成getter、setter方法;
......
private int height;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public boolean isError() {
return error;
}
.......
下面着重是我们的onBindViewHolder方法,主体思想还是根据宽度获取该显示的高度。
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
//存在记录的高度时先Layout再异步加载图片
if (mData.get(holder.getAdapterPosition()).getHeight() > 0) {
ViewGroup.LayoutParams layoutParams = holder.mIvMm.getLayoutParams();
layoutParams.height = mData.get(holder.getAdapterPosition()).getHeight();
}
//获取屏幕宽度
WindowManager windowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
Display display = windowManager.getDefaultDisplay();
display.getMetrics(dm);
final int screenWidth = dm.widthPixels;
String url = mData.get(position).getResults().get(0).getUrl();
Glide.with(mContext).load(url).asBitmap().diskCacheStrategy(DiskCacheStrategy.ALL)
.into(new SimpleTarget(screenWidth, screenWidth) {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation super Bitmap> glideAnimation) {
if(holder.getAdapterPosition() != RecyclerView.NO_POSITION) {
if (mData.get(holder.getAdapterPosition()).getHeight() <= 0) {
int width = resource.getWidth();
int height = resource.getHeight();
int realHeight = screenWidth * height / width / 2;
mData.get(holder.getAdapterPosition()).setHeight(realHeight);
ViewGroup.LayoutParams lp = holder.mIvMm.getLayoutParams();
lp.height = realHeight;
if(width < screenWidth / 2)
lp.width = screenWidth / 2;
}
holder.mIvMm.setImageBitmap(resource);
}
}
});
}
源码链接