在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>