1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util; 18 19 import static com.android.internal.util.Preconditions.checkArgumentFinite; 20 import static com.android.internal.util.Preconditions.checkNotNull; 21 22 import android.annotation.NonNull; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 /** 27 * Immutable class for describing width and height dimensions in some arbitrary 28 * unit. 29 * <p> 30 * Width and height are finite values stored as a floating point representation. 31 * </p> 32 */ 33 @android.ravenwood.annotation.RavenwoodKeepWholeClass 34 public final class SizeF implements Parcelable { 35 /** 36 * Create a new immutable SizeF instance. 37 * 38 * <p>Both the {@code width} and the {@code height} must be a finite number. 39 * In particular, {@code NaN} and positive/negative infinity are illegal values.</p> 40 * 41 * @param width The width of the size 42 * @param height The height of the size 43 * 44 * @throws IllegalArgumentException 45 * if either {@code width} or {@code height} was not finite. 46 */ SizeF(final float width, final float height)47 public SizeF(final float width, final float height) { 48 mWidth = checkArgumentFinite(width, "width"); 49 mHeight = checkArgumentFinite(height, "height"); 50 } 51 52 /** 53 * Get the width of the size (as an arbitrary unit). 54 * @return width 55 */ getWidth()56 public float getWidth() { 57 return mWidth; 58 } 59 60 /** 61 * Get the height of the size (as an arbitrary unit). 62 * @return height 63 */ getHeight()64 public float getHeight() { 65 return mHeight; 66 } 67 68 /** 69 * Check if this size is equal to another size. 70 * 71 * <p>Two sizes are equal if and only if both their widths and heights are the same.</p> 72 * 73 * <p>For this purpose, the width/height float values are considered to be the same if and only 74 * if the method {@link Float#floatToIntBits(float)} returns the identical {@code int} value 75 * when applied to each.</p> 76 * 77 * @return {@code true} if the objects were equal, {@code false} otherwise 78 */ 79 @Override equals(final Object obj)80 public boolean equals(final Object obj) { 81 if (obj == null) { 82 return false; 83 } 84 if (this == obj) { 85 return true; 86 } 87 if (obj instanceof SizeF) { 88 final SizeF other = (SizeF) obj; 89 return mWidth == other.mWidth && mHeight == other.mHeight; 90 } 91 return false; 92 } 93 94 /** 95 * Return the size represented as a string with the format {@code "WxH"} 96 * 97 * @return string representation of the size 98 */ 99 @Override toString()100 public String toString() { 101 return mWidth + "x" + mHeight; 102 } 103 invalidSizeF(String s)104 private static NumberFormatException invalidSizeF(String s) { 105 throw new NumberFormatException("Invalid SizeF: \"" + s + "\""); 106 } 107 108 /** 109 * Parses the specified string as a size value. 110 * <p> 111 * The ASCII characters {@code \}{@code u002a} ('*') and 112 * {@code \}{@code u0078} ('x') are recognized as separators between 113 * the width and height.</p> 114 * <p> 115 * For any {@code SizeF s}: {@code SizeF.parseSizeF(s.toString()).equals(s)}. 116 * However, the method also handles sizes expressed in the 117 * following forms:</p> 118 * <p> 119 * "<i>width</i>{@code x}<i>height</i>" or 120 * "<i>width</i>{@code *}<i>height</i>" {@code => new SizeF(width, height)}, 121 * where <i>width</i> and <i>height</i> are string floats potentially 122 * containing a sign, such as "-10.3", "+7" or "5.2", but not containing 123 * an {@code 'x'} (such as a float in hexadecimal string format).</p> 124 * 125 * <pre>{@code 126 * SizeF.parseSizeF("3.2*+6").equals(new SizeF(3.2f, 6.0f)) == true 127 * SizeF.parseSizeF("-3x-6").equals(new SizeF(-3.0f, -6.0f)) == true 128 * SizeF.parseSizeF("4 by 3") => throws NumberFormatException 129 * }</pre> 130 * 131 * @param string the string representation of a size value. 132 * @return the size value represented by {@code string}. 133 * 134 * @throws NumberFormatException if {@code string} cannot be parsed 135 * as a size value. 136 * @throws NullPointerException if {@code string} was {@code null} 137 */ parseSizeF(String string)138 public static SizeF parseSizeF(String string) 139 throws NumberFormatException { 140 checkNotNull(string, "string must not be null"); 141 142 int sep_ix = string.indexOf('*'); 143 if (sep_ix < 0) { 144 sep_ix = string.indexOf('x'); 145 } 146 if (sep_ix < 0) { 147 throw invalidSizeF(string); 148 } 149 try { 150 return new SizeF(Float.parseFloat(string.substring(0, sep_ix)), 151 Float.parseFloat(string.substring(sep_ix + 1))); 152 } catch (NumberFormatException e) { 153 throw invalidSizeF(string); 154 } catch (IllegalArgumentException e) { 155 throw invalidSizeF(string); 156 } 157 } 158 159 /** 160 * {@inheritDoc} 161 */ 162 @Override hashCode()163 public int hashCode() { 164 return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight); 165 } 166 167 private final float mWidth; 168 private final float mHeight; 169 170 /** 171 * Parcelable interface methods 172 */ 173 @Override describeContents()174 public int describeContents() { 175 return 0; 176 } 177 178 /** 179 * Write this size to the specified parcel. To restore a size from a parcel, use the 180 * {@link #CREATOR}. 181 * @param out The parcel to write the point's coordinates into 182 */ 183 @Override writeToParcel(@onNull Parcel out, int flags)184 public void writeToParcel(@NonNull Parcel out, int flags) { 185 out.writeFloat(mWidth); 186 out.writeFloat(mHeight); 187 } 188 189 public static final @NonNull Creator<SizeF> CREATOR = new Creator<SizeF>() { 190 /** 191 * Return a new size from the data in the specified parcel. 192 */ 193 @Override 194 public @NonNull SizeF createFromParcel(@NonNull Parcel in) { 195 float width = in.readFloat(); 196 float height = in.readFloat(); 197 return new SizeF(width, height); 198 } 199 200 /** 201 * Return an array of sizes of the specified size. 202 */ 203 @Override 204 public @NonNull SizeF[] newArray(int size) { 205 return new SizeF[size]; 206 } 207 }; 208 } 209