Android自定View——可以设置宽高比例的ImageView
2016-01-28 13:43
585 查看
如果对android自定义view还不太属性,可以查看我之前写的文章《Android自定义View——基础知识篇》
在App中展示最多的除了文字外,应该就是图片了。为了适配屏幕,有些情况下在布局时图片大小是不固定的,如下图:
中间是三张图片(ImageView),图片左右两边的间距都已经确定,但图片大小不确定。可以看出三张图片占用剩下的空间,这里可以用LinearLayout布局,设置中间三个ImageView的layout_weight=1。但这里还有一个需求,图片不能被变形,必须按比例缩放。因为不确定ImageView的大小,所以无法事先根据比例设置宽高。
我们可以自定义一个ImageView,根据图片的比例自动确定ImageView的大小,或者指定宽高的比例。效果图如下:
我们可以看出,ImageView根据屏幕的大小不同自动适配。下面是关键代码:
res/values/attrs.xml
关于自定义样式,可以查看上篇文章《Android自定义View——自定义样式》
完整的代码放在了github上:https://github.com/1993hzw/Androids
在App中展示最多的除了文字外,应该就是图片了。为了适配屏幕,有些情况下在布局时图片大小是不固定的,如下图:
中间是三张图片(ImageView),图片左右两边的间距都已经确定,但图片大小不确定。可以看出三张图片占用剩下的空间,这里可以用LinearLayout布局,设置中间三个ImageView的layout_weight=1。但这里还有一个需求,图片不能被变形,必须按比例缩放。因为不确定ImageView的大小,所以无法事先根据比例设置宽高。
我们可以自定义一个ImageView,根据图片的比例自动确定ImageView的大小,或者指定宽高的比例。效果图如下:
我们可以看出,ImageView根据屏幕的大小不同自动适配。下面是关键代码:
public class RatioImageView extends ImageView { /* 优先级从大到小: mIsWidthFitDrawableSizeRatio mIsHeightFitDrawableSizeRatio mWidthRatio mHeightRatio 即如果设置了mIsWidthFitDrawableSizeRatio为true,则优先级较低的三个值不生效 */ private float mDrawableSizeRatio = -1f; // src图片(前景图)的宽高比例 // 根据前景图宽高比例测量View,防止图片缩放变形 private boolean mIsWidthFitDrawableSizeRatio; // 宽度是否根据src图片(前景图)的比例来测量(高度已知) private boolean mIsHeightFitDrawableSizeRatio; // 高度是否根据src图片(前景图)的比例来测量(宽度已知) // 宽高比例 private float mWidthRatio = -1; // 宽度 = 高度*mWidthRatio private float mHeightRatio = -1; // 高度 = 宽度*mHeightRatio public RatioImageView(Context context) { this(context, null); } public RatioImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 虽然此处会调用setImageDrawable,但此时成员变量还未被正确初始化 init(attrs); // 一定要有此代码 if (getDrawable() != null) { mDrawableSizeRatio = 1f * getDrawable().getIntrinsicWidth() / getDrawable().getIntrinsicHeight(); } } /** * 初始化变量 */ private void init(AttributeSet attrs) { TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RatioImageView); mIsWidthFitDrawableSizeRatio = a.getBoolean(R.styleable.RatioImageView_is_width_fix_drawable_size_ratio, mIsWidthFitDrawableSizeRatio); mIsHeightFitDrawableSizeRatio = a.getBoolean(R.styleable.RatioImageView_is_height_fix_drawable_size_ratio, mIsHeightFitDrawableSizeRatio); mHeightRatio = a.getFloat( R.styleable.RatioImageView_height_to_width_ratio, mHeightRatio); mWidthRatio = a.getFloat( R.styleable.RatioImageView_width_to_height_ratio, mWidthRatio); a.recycle(); } @Override public void setImageResource(int resId) { super.setImageResource(resId); if (getDrawable() != null) { mDrawableSizeRatio = 1f * getDrawable().getIntrinsicWidth() / getDrawable().getIntrinsicHeight(); if (mDrawableSizeRatio > 0 && (mIsWidthFitDrawableSizeRatio || mIsHeightFitDrawableSizeRatio)) { requestLayout(); } } } @Override public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); if (getDrawable() != null) { mDrawableSizeRatio = 1f * getDrawable().getIntrinsicWidth() / getDrawable().getIntrinsicHeight(); if (mDrawableSizeRatio > 0 && (mIsWidthFitDrawableSizeRatio || mIsHeightFitDrawableSizeRatio)) { requestLayout(); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 优先级从大到小: // mIsWidthFitDrawableSizeRatio mIsHeightFitDrawableSizeRatio // mWidthRatio mHeightRatio if (mDrawableSizeRatio > 0) { // 根据前景图宽高比例来测量view的大小 if (mIsWidthFitDrawableSizeRatio) { mWidthRatio = mDrawableSizeRatio; } else if (mIsHeightFitDrawableSizeRatio) { mHeightRatio = 1 / mDrawableSizeRatio; } } if (mHeightRatio > 0 && mWidthRatio > 0) { throw new RuntimeException("高度和宽度不能同时设置百分比!!"); } if (mWidthRatio > 0) { // 高度已知,根据比例,设置宽度 int height = MeasureSpec.getSize(heightMeasureSpec); super.onMeasure(MeasureSpec.makeMeasureSpec( (int) (height * mWidthRatio), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } else if (mHeightRatio > 0) { // 宽度已知,根据比例,设置高度 int width = MeasureSpec.getSize(widthMeasureSpec); super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( (int) (width * mHeightRatio), MeasureSpec.EXACTLY)); } else { // 系统默认测量 super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } }
res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="RatioImageView"> <!--宽度是否根据src图片的比例来测量(高度已知)--> <attr name="is_width_fix_drawable_size_ratio" format="boolean"/> <!--高度是否根据src图片的比例来测量(宽度已知)--> <attr name="is_height_fix_drawable_size_ratio" format="boolean"/> <!-- 高度设置,参考宽度,如0.5 , 表示 高度=宽度×0.5 --> <attr name="height_to_width_ratio" format="float"/> <!-- 宽度设置,参考高度,如0.5 , 表示 宽度=高度×0.5 --> <attr name="width_to_height_ratio" format="float"/> </declare-styleable> </resources>
关于自定义样式,可以查看上篇文章《Android自定义View——自定义样式》
完整的代码放在了github上:https://github.com/1993hzw/Androids
相关文章推荐
- Android界面——LinearLayout和RelativeLayout 属性对比
- android ImageView加载动画
- Android中通知的使用
- Android Studio的基本设置
- android Bitmap 处理
- RxAndroid基本使用1
- android mediaplayer使用注意
- Android开发笔记-下拉刷新上拉加载控件,对所有View通用!
- android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事项
- Android开发学习之路--Android Studio项目目录结构简介
- 安装Android adb驱动
- Android开发学习之路--Android Studio项目目录结构简介
- Android 混淆详解
- android-IPC进程间通信
- android基础夯实2
- android学习笔记——BroadcastReceiver实现强制下线
- Android Studio
- android studio 小技巧之 图片预览
- Content Provider
- Android数据储存之SQLiteDatabase SQLiteOpenHelper类的简单使用