自定义控件的方式适配图片,以及里面的一些技巧
转载本专栏每一篇博客请注明转载出处地址,尊重原创。此博客转载链接地址:小杨的博客 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();
当自定义控件的属性很多的时候,这种方式就比较前卫一点了。
所有知识都介绍完了,欢迎关注本博客,不定期推送文章哦~~