功能:
自定义 ImageView 设置显示图片,如果图片的宽与高小于控件的宽与高,就将图片设置显示到控件的中央,如果图片的宽与高有一项大于控件的宽与高,那么就将图片进行缩放显示,两者者是显示在控件的中央效果图:这里是加载了一个小图片
创建一个自定义的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);}
在自定义的ImageView中获取我们所设置的图片
实现 ViewTreeObserver.OnGlobalLayoutListener
在这里,我们可以实现 ViewTreeObserver.OnGlobalLayoutListener这个接口,然后在它的相关实现方法(onGlobalLayout)中进行获取图片的操作
@Overridepublic void onGlobalLayout() {}}
OnGlobalLayoutListener 是ViewTreeObserver的内部类,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到,这是一个注册监听视图树的观察者(observer),在视图树的全局事件改变时得到通知
需要注意的是OnGlobalLayoutListener可能会被多次触发,因此在得到了高度之后,要将OnGlobalLayoutListener注销掉。
注册与注销OnGlobalLayoutListener
在自定义ImageView中我们可以复写View的两个方法
/*** View出现的时候执行这个方法*/@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();//注册OnGlobalLayoutListenergetViewTreeObserver().addOnGlobalLayoutListener(this);}
/*** View从屏幕上消失的时候执行这个方法*/@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();//注销OnGlobalLayoutListenergetViewTreeObserver().removeOnGlobalLayoutListener(this);}
获取图片信息
//设置只执行一次的判断标识private boolean mOnce = false;//由于我们整个过程都是操作的同一个图片,所以初始加载图片的时候我们只获取一次就可以了@Overridepublic 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();
@Overridepublic 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>
这样我们定义的属性为boolean类型
接下来就是使用这个自定义属性
<RelativeLayout xmlns:android="/apk/res/android"xmlns:custom="/apk/res/com.androidlongs.imageviewapplication"android:layout_width="match_parent"android:layout_height="match_parent"><com.androidlongs.imageviewapplication.ScaleAttrsImageViewandroid: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();}
接下来就 是修改我们操作图片并设置图片显示那一块,加一次判断就可以了
@Overridepublic 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",那么自定义控件将会在这种情况下对图片进行适当放大的操作