功能:
自定义 ImageView 设置显示图片,如果图片的宽与高小于控件的宽与高,就将图片设置显示到控件的中央, 如果图片的宽与高有一项大于控件的宽与高,那么就将图片进行缩放显示,两者者是显示在控件的中央 效果图: 这里是加载了一个小图片
public class ScaleImageView extends ImageView { public ScaleImageView(Context context) { this(context,null); } public ScaleImageView(Context context, AttributeSet attrs) { this(context, attrs,0); } public ScaleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
实现 ViewTreeObserver.OnGlobalLayoutListener
在这里,我们可以实现 ViewTreeObserver.OnGlobalLayoutListener这个接口,然后在它的相关实现方法(onGlobalLayout)中进行获取图片的操作
@Override public void onGlobalLayout() { } }
OnGlobalLayoutListener 是ViewTreeObserver的内部类,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到,
这是一个注册监听视图树的观察者(observer),在视图树的全局事件改变时得到通知
需要注意的是OnGlobalLayoutListener可能会被多次触发,因此在得到了高度之后,要将OnGlobalLayoutListener注销掉。
注册与注销OnGlobalLayoutListener
在自定义ImageView中我们可以复写View的两个方法
/** * View出现的时候执行这个方法 */ @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //注册OnGlobalLayoutListener getViewTreeObserver().addOnGlobalLayoutListener(this); }
/** * View从屏幕上消失的时候执行这个方法 */ @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); //注销OnGlobalLayoutListener getViewTreeObserver().removeOnGlobalLayoutListener(this); }
获取图片信息
//设置只执行一次的判断标识 private boolean mOnce = false; //由于我们整个过程都是操作的同一个图片,所以初始加载图片的时候我们只获取一次就可以了 @Override public void onGlobalLayout() { //获取加载完后的图片 if (!mOnce) { //得到自定义控件的宽与高 final int width = getWidth(); final int height = getHeight(); //得到图片 final Drawable drawable = getDrawable(); if (drawable == null) { return; } //获取图片的宽与高 final int intrinsicHeight = drawable.getIntrinsicHeight(); final int intrinsicWidth = drawable.getIntrinsicWidth(); mOnce = true; } }
依据获取到的信息设置显示图片
当图片的宽高小于屏幕的宽高时,我们将其显示在控件的中间位置(当然也可以将其进行适当的放大)
当图片的宽高大于屏幕的宽高时,我们对其进行适当的缩小,并显示在控件的中间位置
由于这里涉及到了图片的相关放大与缩小,所以这里需要使用到Matrix这个类进行图片的相关操作
定义 Matrix 并在构造方法中进行初始化操作
private Matrix mScaleMatrix; mScaleMatrix = new Matrix();
@Override public void onGlobalLayout() { //获取加载完后的图片 if (!mOnce) { //得到控件的宽与高 final int width = getWidth(); final int height = getHeight(); //得到图片,并获取宽与高 final Drawable drawable = getDrawable(); if (drawable == null) { return; } final int intrinsicHeight = drawable.getIntrinsicHeight(); final int intrinsicWidth = drawable.getIntrinsicWidth(); //设置缩放比例 float scale = 1.0f; //如果图片宽度大于控件宽度,图片高度小于控件高度 图片缩小 if (intrinsicWidth > width && intrinsicHeight < height) { scale = intrinsicWidth * 1.0f / width; } //如果图片的高度大于控件的高度,图片的宽度小于控件的宽度 图片缩小 if (intrinsicHeight > height && intrinsicWidth < width) { scale = intrinsicHeight * 1.0f / height; } //如果图片的宽与高都大于控件的宽与高 if (intrinsicHeight > height && intrinsicWidth > width) { scale = Math.min(width * 1.0f / intrinsicWidth, height * 1.0f / intrinsicHeight); } //得到初始化时图片需要进行缩放的值 mInitScale = scale; //获取将图片移动到控件的中心的坐标 int moveX = getWidth() / 2 - intrinsicWidth / 2; int moveY = getHeight() / 2 - intrinsicHeight / 2; //设置图片的平移与缩放 mScaleMatrix.postTranslate(moveX, moveY); mScaleMatrix.postScale(mInitScale, mInitScale, getWidth() / 2, getHeight() / 2); setImageMatrix(mScaleMatrix); mOnce = true; } }
走到这里,我们使用这个ImageView就可以适当的加载我们的图片了,当然我们还可以设置一个自定义的属性或者是方法来控制当图片的宽度小于控件的宽度的时候,是否将图片进行适当的放大
在res/values/下建立一个attrs.xml文件,在里面定义我们的属性和声明我们的属性和样式
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="isScaleImage" format="boolean"/> <declare-styleable name="ScaleImageView"> <attr name="isScaleImage" /> </declare-styleable> </resources>
接下来就是使用这个自定义属性
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res/com.androidlongs.imageviewapplication" android:layout_width="match_parent" android:layout_height="match_parent" > <com.androidlongs.imageviewapplication.ScaleAttrsImageView android:scaleType="matrix" android:src="@mipmap/ic_launcher" custom:isScaleImage = "true" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
接下来在我们的自定义Imageview中使用属性
注: 这里是在三个参数的构造中进行初始化操作的,API版本3.0以上才支持
private boolean isScale = false; public ScaleAttrsImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取所有的自定义属性和样式 final TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ScaleImageView, defStyleAttr, 0); //获取自定义属性的个数 final int indexCount = typedArray.getIndexCount(); //获取相关设定的值 for (int i = 0; i < indexCount; i++) { final int attr = typedArray.getIndex(i); switch (attr){ case R.styleable.ScaleImageView_isScaleImage: isScale = typedArray.getBoolean(attr,false); } } mMatrix = new Matrix(); }
@Override public void onGlobalLayout() { .... .... if (isScale&&(intrinsicHeight < height && intrinsicWidth < width)) { scale = Math.min(width * 1.0f / intrinsicWidth, height * 1.0f / intrinsicHeight); } //得到初始化时图片需要进行缩放的值 final int moveX = getWidth() / 2 - intrinsicWidth / 2; final int moveY = getHeight() / 2 - intrinsicHeight / 2; mMatrix.postTranslate(moveX, moveY); mMatrix.postScale(scale,scale,getWidth()/2,getHeight()/2); setImageMatrix(mMatrix); mOnce=true; } }
这里是加载的小图片,因为设置了属性isScale = "true",那么自定义控件将会在这种情况下对图片进行适当放大的操作
Android自定义ImageView(二)——实现双击放大与缩小图片 | 点击打开链接 |
Android自定义控件ImageViwe(三)——随手指进行图片的缩放 | 点击打开链接 |
Android自定义控件ImageViwe(四)——多点触控实现图片的自由移动 | 点击打开链接 |
Android ListView分组排序显示数据 | 点击打开链接 |
Android自定义下拉刷新功能的ListView | 点击打开链接 |
Android自定义控件之流式布局 | 点击打开链接 |
Android音乐播放器高级开发 | 点击打开链接 |
Android自定义下拉刷新功能的ListView | 点击打开链接 |