自定义控件的方式适配图片,以及里面的一些技巧

转载本专栏每一篇博客请注明转载出处地址,尊重原创。此博客转载链接地址:小杨的博客  http://blog.csdn.net/qq_32059827/article/details/52718489


开题前先给出一种几乎所有人都经常见的问题,如下:

布局代码

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content""
        android:orientation="vertical" >

        <ImageView
            android:src="@drawable/recommend_56"
            android:id="@+id/iv_list_item_subject_icon"
            android:layout_width="match_parent"
            android:layout_height="150dp"/>

    </LinearLayout>

IDE渲染效果图(无需运行程序也可以看出):


问题:图片没办法填充布局,看蓝色渲染效果就能看出。

到这里,相信很多人都遇到此类事情,我想让图片整个填充我的布局,他却偏偏无法填充,是那么的难看。那么大多人,都是这么解决问题的——

在imageView里面加一行代码:android:scaleType="fitXY"。运行效果:

正好填充完毕,而且看起来感觉还不错。

但是,这样其实是有问题的,如果我把图片的高度设置的高一些,问题就很明显了。例如,我修改为高度修改为200dp。运行效果:

可以看到,都教授和女神身子都被拉长了相信把这个放到一款APP里,用户对于二位的粉丝而言,开发者要遭骂名了。

如果非要在布局文件中做这种适配,建议用android:scaleType="centerCrop",这个属性不会对图片做拉伸,他原来是对图片中心开始,按照一定的比例裁剪,保留裁剪的部分。

因为不去做拉伸,效果肯定比上边拉伸效果好很多。运行看看效果:


看!图片没被拉伸!!但是,但是这个时候估计都教授粉丝火气比上边还大,这尼玛连人都看不到了!呵呵,你把图片高度设置小一点就好了,比如设置150dp,效果肯定很好。

android:layout_height="150dp"运行:

虽然说,女神的耳朵被剪走了,但是图片应该是上边情况最好的情景了。


那么,我们肯定会想,到底有没有好的办法,让图片既可以不被拉伸,也不所示图片的内容吗?回答是肯定的!那就是今天的主题——使用自定义控件方式,解决图片适配。

思路:自定义一个控件,控件按照图片的比例来确定自己的宽高,控件确定了宽高,让Imageview匹配这个自定义控件。这样按照比例来放置的图片就可以解决上边的问题。

按照比例来确定布局高度的自定义控件。

首先,自定义一个RatioLayout继承自FrameLayout。那么到底是多大的比例?通过自定属性的方式把这个比例值传递给我们自定义的控件里面。

然后配置针对这个控件的自定义属性文件,里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="RatioLayout">
        <attr name="ratio" format="float" />
    </declare-styleable>

</resources>

然后计算图片的比例。宽/高。这个图片的比例结果等于2.43

那么布局中就这么写:

<com.itydl.googlepaly.ui.view.RatioLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            itydl:ratio="2.43">

            <ImageView
                android:id="@+id/iv_list_item_subject_icon"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@drawable/recommend_15" />
        </com.itydl.googlepaly.ui.view.RatioLayout>

(提示:记得在根布局加入自定义的命名空间)

这个歌时候,就能在自定义控件的代码中拿到这个自定义的属性值了。接下来,具体的自定义控件时测量方法,以及一些详细的diamante解释如下:

public class RatioLayout extends FrameLayout {
	private float ratio;
	public RatioLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public RatioLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		// “高端”手法获取属性:
		// 获取自定义控件下的属性集合。如果你的属性很多的话,返回的这个集合就对应有多大
		TypedArray typedArray = context.obtainStyledAttributes(attrs,
				R.styleable.RatioLayout);//表示我从attrs属性集合里面,拿到了名字叫RatioLayout的属性
		// 根据属性id获取属性值, 方式:
		// R.styleable.名称_属性。参数一:系统生成的id=自定义控件名_属性名;第二个参数:在属性文件中的位置是第几个
		ratio = typedArray.getFloat(R.styleabut_ratio, 0);
		// 使用此方式拿属性必须回收TypedArray, 以释放内存le.RatioLayo
		typedArray.recycle();
	}

	public RatioLayout(Context context) {
		super(context);
	}
	
	/**
	 * 当前控件测量自己的宽高时,调用此方法
	 */
	// 要不要绘制控件的大小?
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获取宽度模式
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取高度模式

		//控件的宽高,并非图片的宽高
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);

		// MeasureSpec.EXACTLY 确定值, 比如把宽高值写死dp,类似match_parent
		// MeasureSpec.AT_MOST 至多, 能撑多大就多大, 类似wrap_content
		// MeasureSpec.UNSPECIFIED 未指定大小

		//按比例计算图片高度的前提是,宽度模式是确定的,即是MeasureSpec.AT_MOST,高度是不确定的。这两个条件少一个,都没有下面重新按照比例计算的必要了。
		if (widthMode == MeasureSpec.EXACTLY//ratio要大于零、宽度确定、高度不确定才有必要计算高度值
				&& heightMode != MeasureSpec.EXACTLY && ratio > 0) {
			// 1. 根据布局宽度推算图片宽度
			int imageWidth = widthSize - getPaddingLeft() - getPaddingRight();//【在布局文件中如果控件设置了padding的话图片小于控件宽度,不设置则图片宽与控件一样】
			// 2. 根据图片宽度和宽高比,推算图片高度
			int imageHeight = (int) (imageWidth / ratio+ 0.5f);//ratio是通过宽/高计算出来的
			// 3. 根据图片高度, 推算控件(布局)最新高度
			heightSize = imageHeight + getPaddingTop() + getPaddingBottom();//如果设置了padding就大于图片高度,如果没加,则相等
			// 4. 重新定义高度模式。根据布局高度以及高度模式, 推算heightMeasureSpec
			heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize,
					MeasureSpec.EXACTLY);
		}

		//根据最新的高度宽度去测量
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}


}


这个时候,通过渲染是看不出来,可以直接运行一下看看是否达到了惊人的效果:


可以看到,这是最好的展示效果。都教授还是那个都教授,女神也都展示了出来。

到此,通过自定义控件适配图片就完毕了。再说一下里面的“小技巧”,就是另一种“高端”方式获取属性值。过程如下:

// “高端”手法获取属性:
		// 获取自定义控件下的属性集合。如果你的属性很多的话,返回的这个集合就对应有多大
		TypedArray typedArray = context.obtainStyledAttributes(attrs,
				R.styleable.RatioLayout);
		// 根据属性id获取属性值, 方式:
		// R.styleable.名称_属性。参数一:系统生成的id=自定义控件名_属性名;第二个参数:在属性文件中的位置是第几个
		ratio = typedArray.getFloat(R.styleable.RatioLayout_ratio, 0);
		// 使用此方式拿属性必须回收TypedArray, 以释放内存
		typedArray.recycle();

当自定义控件的属性很多的时候,这种方式就比较前卫一点了。

所有知识都介绍完了,欢迎关注本博客,不定期推送文章哦~~


原文地址:https://www.cnblogs.com/wanghang/p/6299574.html