解决ImageView超出父控件(或屏幕边界)时,图片挤压问题

1、需求

在屏幕边缘显示一张图片,超出屏幕宽度时,只显示图片的左边部分,而且不被挤压,其他部分剪切。但我在实际开发中,踩了个坑,这里作个记录,下面经过图片直观了解一下状况:php

理想 现实

目前能够肯定,这种状况会出如今使用RelativeLayout做为ImageView父控件的状况下,其余类型的ViewGroup效果如何,暂不肯定。java

2、分析

这部分是我对问题研究的记录,心急的朋友能够直接跳到第三部分,看源码实现。android

一、使用 HorizontalScrollView

在网上也百度到了一个相似的问题: Android ImageView 超出屏幕边界 图片会被挤压app

这位朋友遇到的问题跟我是差很少的,当子控件ImageView超过父控件尺寸时,ImageView显示的图片就会挤压,热心的网友们给他支了一个招:把ImageVIew放在HorizontalScrollView控件里面。可是,我遇到的需求是不要滚动效果(不要跟我说能够屏蔽ScrollView滚动。。。),就单单显示图片的部分区域而已,使用HorizontalScrollView控件明显不适用,只能另寻出路了。ide

二、使用 LinearLayout

实践证实,使用LinearLayout做为ImageView的父控件,当ImageView超出父控件尺寸时,不会挤压图片。函数

这种方式是一种不错的解决方案,可是,局限性太大,大多数状况下,会但愿使用RelativeLayout做为控件的父控件,因此,这种状况也不适用我目前的状况。源码分析

三、使用ImageView的ScaleType

由于前面的方案都要求将ImageView放置到特定父控件中,局限性太大,严重的,可能会形成UI屡次绘制,下降性能,形成画面卡顿,因此,感受仍是从ImageView自己下手比较合理。布局

ImageView的ScaleType有以下几种:post

源自ImageView (一) ——从源码的角度分析ScaleType (缩放模式)性能

缩放模式 裁剪 按比例 放大 缩小 描述
MATIRX 未知 未知 未知 未知 经过设定setImageMatrix函数相应的矩阵,来完成。
FIT_XY 直接将图片铺满整个view
CENTER 1)若是图片较小,直接居中所有显示2) 若是图片较大,裁剪后居中显示
CENTER_CROP 按比例缩放,按照缩放比例大的进行缩放,而后居中显示(除非图片和view的形状同样,不然必定存在裁剪,因此有的描述为图片的长宽≥View的长宽)
CENTER_INSIDE 1) 若是图片较小,直接放入view,不进行放大处理,居中显示2) 将图片按比例进行缩放,使得图片彻底展现,而且长或者宽等于view的长和宽(因此有的描述为图片的长宽≤View的长宽)
FIT_START 对图片的处理和FIT_CENTER,区别为:1)view的横向有空,居左边2)view的纵向有空,居上边
FIT_CENTER(默认) 将图片按比例进行缩放,使得图片彻底展现,而且长或者宽等于view的长和宽(因此有的描述为图片的长宽≤View的长宽),最后居中显示
FIT_END 对图片的处理和FIT_CENTER,区别为:1) view的横向有空,居右边2) view的纵向有空,居下边

经过上表描述,能够肯定只有CENTER_CROP的效果比较符合,但仍是有点区别,CENTER_CROP只会显示中间区域部分,而我要的是显示左边区域,因此,下面就进行ImageView源码分析。

在ImageView中搜索CENTER_CROP,定位到核心逻辑就在configureBounds()方法中,代码以下:

如下是 博客:ImageView的ScaleType原理及效果分析 中对CENTER_CROP的解释。

该模式按比例扩大图片的尺寸并居中显示,使得图片长(宽)等于或大于View的长(宽)。

若是dwidth/dheight>vwidth/vheight(图片的宽高比大于ImageView的宽高比),即vheight/dheight>vwidth/dwidth,也就是说ImageView和图片高度比小于ImageView和图片的宽度比,这时候取vheight/dheight的比例进行图片缩放,这样就能保证图片宽度在进行同等比例缩放的时候,图片宽度大于或等于ImageView的宽度,由于(vheight/dheight)* dwidth>vwidth。

同理,若是vwidth/dwidth>vheight/dheight时,取vwidth/dwidth做为图片的缩放比例,能够保证缩放完成后图片宽度等于ImageView的宽度,图片的高度大于或等于ImageView的高度,由于(vwidth/dwidth)*dheight>vheight。图片在缩放以后再进行CENTER操做便可。

vwidth/dwidth>vheight/dheight

上图所示,vwidth/dwidth>vheight/dheight,图片先按照vwidth/dwidth进行缩放,缩放后的图片高度>=vheight,而后再进行向上移位。

vheight/dheight>vwidth/dwidth

上图所示,vheight/dheight>vwidth/dwidth,图片先按照vheight/dheight进行缩放,缩放后的图片宽度>=vwidth,而后再进行想左移位。

回到主线,根据上面的分析,明白了CENTER_CROP的工做原理(可能也不太明白,但不要紧),咱们只须要在CENTER_CROP的基础上控制dx或dy便可,同时,在分析完整个configureBounds()方法以后,能够肯定,不论是哪一种ScaleType,其原理都是操控图片矩阵mDrawMatrix来实现的,这时候ScaleType中的MATRIX就能够派上用场了,下面是我对MATRIX作的一些封装,你也能够根据本身的项目需求,编写本身的处理逻辑。

3、代码

用了在布局中使用方便,我将它作成一个自定义控件,命名为ImageViewExt。

一、自定义属性attrs_image_view_ext

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ImageViewExt">
        <attr name="ive_scale_type_matrix_ext" format="enum">
            <enum name="left_crop" value="1"/>
            <enum name="right_crop" value="2"/>
            <enum name="center_crop" value="3"/>
        </attr>
    </declare-styleable>
</resources>
复制代码

一、自定义控件ImageViewExt

/** * @建立者 CSDN_LQR * @时间 2018/9/4 * @描述 ImageView扩展控件 * <p> * 一、对 ScaleType.MATRIX 进行封装拓展 */
public class ImageViewExt extends ImageView {

    public static final int SCALE_TYPE_MATRIX_LEFT_CROP   = 1; // 等比例缩放,当图像超出控件尺寸时,保留左边,其他部分剪切掉。
    public static final int SCALE_TYPE_MATRIX_RIGHT_CROP  = 2; // 等比例缩放,当图像超出控件尺寸时,保留右边,其他部分剪切掉。
    public static final int SCALE_TYPE_MATRIX_CENTER_CROP = 3; // 等比例缩放,当图像超出控件尺寸时,保留中间,其他部分剪切掉。

    @IntDef({SCALE_TYPE_MATRIX_LEFT_CROP, SCALE_TYPE_MATRIX_RIGHT_CROP, SCALE_TYPE_MATRIX_CENTER_CROP})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ScaleTypeMatrixExt {
    }

    private int mScaleTypeMatrixExt;

    public ImageViewExt(Context context) {
        this(context, null);
    }

    public ImageViewExt(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageViewExt(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    protected void init(Context context, @Nullable AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ImageViewExt);
        mScaleTypeMatrixExt = typedArray.getInt(R.styleable.ImageViewExt_ive_scale_type_matrix_ext, -1);
        typedArray.recycle();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        handScaleTypeMatrixExt();
    }

    @Override
    public void requestLayout() {
        super.requestLayout();
        handScaleTypeMatrixExt();
    }

    private void handScaleTypeMatrixExt() {
        if (this.getScaleType() == ScaleType.MATRIX && mScaleTypeMatrixExt != -1) {
            // 图片实际尺寸
            final int dwidth = getDrawable().getIntrinsicWidth();
            final int dheight = getDrawable().getIntrinsicHeight();
            // ImageView图片显示尺寸
            final int vwidth = getWidth() - getPaddingLeft() - getPaddingRight();
            final int vheight = getHeight() - getPaddingTop() - getPaddingBottom();
            float scale;
            float dx = 0, dy = 0;
            if (dwidth * vheight > vwidth * dheight) {      // 图片宽高比 > 控件宽高比
                scale = (float) vheight / (float) dheight;
                switch (mScaleTypeMatrixExt) {
                    case SCALE_TYPE_MATRIX_LEFT_CROP:
                        dx = 0;                             // 保留左边
                        break;
                    case SCALE_TYPE_MATRIX_RIGHT_CROP:
                        dx = (vwidth - dwidth * scale);     // 保留右边
                        break;
                    case SCALE_TYPE_MATRIX_CENTER_CROP:
                        dx = (vwidth - dwidth * scale) * 0.5f; // 保留中间(效果与 CENTER_CROP 同样)
                        break;
                    default:
                        break;
                }
            } else {
                scale = (float) vwidth / (float) dwidth;
                // dy = (vheight - dheight * scale) * 0.5f; // 根据实际状况编写,默认为0,保留上边
            }
            Matrix matrix = new Matrix();
            matrix.setScale(scale, scale);
            matrix.postTranslate(Math.round(dx), Math.round(dy));
            this.setImageMatrix(matrix);
        }
    }

    public void setScaleTypeMatrixExt(@ScaleTypeMatrixExt int scaleTypeMatrixExt) {
        this.mScaleTypeMatrixExt = scaleTypeMatrixExt;
        requestLayout();
    }
}
复制代码

4、使用

须要使用matrix做为ImageView的ScaleType,再指定ive_scale_type_matrix_ext属性便可,也能够在代码中调用setScaleType()、setScaleTypeMatrixExt()来指定。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="382.5dp" android:background="@android:color/white" android:orientation="horizontal">

    <com.lqr.widget.ImageViewExt android:layout_width="275dp" android:layout_height="135.5dp" android:layout_marginLeft="450dp" android:scaleType="matrix" android:src="@mipmap/main_recommand_1_4_sample3" app:ive_scale_type_matrix_ext="left_crop"/>

</LinearLayout>
复制代码

效果以下:

相关文章
相关标签/搜索