TextViewBindingAdapter.java revision 716ba89e7f459f49ea85070d4710c1d79d715298
1/* 2 * Copyright (C) 2015 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 */ 16package android.databinding.adapters; 17 18import android.databinding.BindingAdapter; 19import android.databinding.BindingMethod; 20import android.databinding.BindingMethods; 21import android.graphics.drawable.Drawable; 22import android.os.Build; 23import android.text.Editable; 24import android.text.InputFilter; 25import android.text.InputType; 26import android.text.TextWatcher; 27import android.text.method.DialerKeyListener; 28import android.text.method.DigitsKeyListener; 29import android.text.method.KeyListener; 30import android.text.method.PasswordTransformationMethod; 31import android.text.method.TextKeyListener; 32import android.util.Log; 33import android.util.TypedValue; 34import android.widget.TextView; 35 36import com.android.databinding.library.baseAdapters.R; 37 38@BindingMethods({ 39 @BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"), 40 @BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"), 41 @BindingMethod(type = TextView.class, attribute = "android:editorExtras", method = "setInputExtras"), 42 @BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"), 43 @BindingMethod(type = TextView.class, attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"), 44 @BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps"), 45 @BindingMethod(type = TextView.class, attribute = "android:textColorHighlight", method = "setHighlightColor"), 46 @BindingMethod(type = TextView.class, attribute = "android:textColorHint", method = "setHintTextColor"), 47 @BindingMethod(type = TextView.class, attribute = "android:textColorLink", method = "setLinkTextColor"), 48 @BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener"), 49}) 50public class TextViewBindingAdapter { 51 52 private static final String TAG = "TextViewBindingAdapters"; 53 54 public static final int INTEGER = 0x01; 55 56 public static final int SIGNED = 0x03; 57 58 public static final int DECIMAL = 0x05; 59 60 @BindingAdapter({"android:autoText"}) 61 public static void setAutoText(TextView view, boolean autoText) { 62 KeyListener listener = view.getKeyListener(); 63 64 TextKeyListener.Capitalize capitalize = TextKeyListener.Capitalize.NONE; 65 66 int inputType = listener != null ? listener.getInputType() : 0; 67 if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) { 68 capitalize = TextKeyListener.Capitalize.CHARACTERS; 69 } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0) { 70 capitalize = TextKeyListener.Capitalize.WORDS; 71 } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) { 72 capitalize = TextKeyListener.Capitalize.SENTENCES; 73 } 74 view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize)); 75 } 76 77 @BindingAdapter({"android:capitalize"}) 78 public static void setCapitalize(TextView view, TextKeyListener.Capitalize capitalize) { 79 KeyListener listener = view.getKeyListener(); 80 81 int inputType = listener.getInputType(); 82 boolean autoText = (inputType & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0; 83 view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize)); 84 } 85 86 @BindingAdapter({"android:bufferType"}) 87 public static void setBufferType(TextView view, TextView.BufferType bufferType) { 88 view.setText(view.getText(), bufferType); 89 } 90 91 @BindingAdapter({"android:digits"}) 92 public static void setDigits(TextView view, CharSequence digits) { 93 if (digits != null) { 94 view.setKeyListener(DigitsKeyListener.getInstance(digits.toString())); 95 } else if (view.getKeyListener() instanceof DigitsKeyListener) { 96 view.setKeyListener(null); 97 } 98 } 99 100 @BindingAdapter({"android:numeric"}) 101 public static void setNumeric(TextView view, int numeric) { 102 view.setKeyListener(DigitsKeyListener.getInstance((numeric & SIGNED) != 0, 103 (numeric & DECIMAL) != 0)); 104 } 105 106 @BindingAdapter({"android:phoneNumber"}) 107 public static void setPhoneNumber(TextView view, boolean phoneNumber) { 108 if (phoneNumber) { 109 view.setKeyListener(DialerKeyListener.getInstance()); 110 } else if (view.getKeyListener() instanceof DialerKeyListener) { 111 view.setKeyListener(null); 112 } 113 } 114 115 @BindingAdapter({"android:drawableBottom"}) 116 public static void setDrawableBottom(TextView view, Drawable drawable) { 117 Drawable[] drawables = view.getCompoundDrawables(); 118 view.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawable); 119 } 120 121 @BindingAdapter({"android:drawableLeft"}) 122 public static void setDrawableLeft(TextView view, Drawable drawable) { 123 Drawable[] drawables = view.getCompoundDrawables(); 124 view.setCompoundDrawables(drawable, drawables[1], drawables[2], drawables[3]); 125 } 126 127 @BindingAdapter({"android:drawableRight"}) 128 public static void setDrawableRight(TextView view, Drawable drawable) { 129 Drawable[] drawables = view.getCompoundDrawables(); 130 view.setCompoundDrawables(drawables[0], drawables[1], drawable, drawables[3]); 131 } 132 133 @BindingAdapter({"android:drawableTop"}) 134 public static void setDrawableTop(TextView view, Drawable drawable) { 135 Drawable[] drawables = view.getCompoundDrawables(); 136 view.setCompoundDrawables(drawables[0], drawable, drawables[2], drawables[3]); 137 } 138 139 @BindingAdapter({"android:drawableStart"}) 140 public static void setDrawableStart(TextView view, Drawable drawable) { 141 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 142 setDrawableLeft(view, drawable); 143 } else { 144 Drawable[] drawables = view.getCompoundDrawablesRelative(); 145 view.setCompoundDrawablesRelative(drawable, drawables[1], drawables[2], drawables[3]); 146 } 147 } 148 149 @BindingAdapter({"android:drawableEnd"}) 150 public static void setDrawableEnd(TextView view, Drawable drawable) { 151 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 152 setDrawableRight(view, drawable); 153 } else { 154 Drawable[] drawables = view.getCompoundDrawablesRelative(); 155 view.setCompoundDrawablesRelative(drawables[0], drawables[1], drawable, drawables[3]); 156 } 157 } 158 159 @BindingAdapter({"android:imeActionLabel"}) 160 public static void setImeActionLabel(TextView view, CharSequence value) { 161 view.setImeActionLabel(value, view.getImeActionId()); 162 } 163 164 @BindingAdapter({"android:imeActionId"}) 165 public static void setImeActionLabel(TextView view, int value) { 166 view.setImeActionLabel(view.getImeActionLabel(), value); 167 } 168 169 @BindingAdapter({"android:inputMethod"}) 170 public static void setInputMethod(TextView view, CharSequence inputMethod) { 171 try { 172 Class<?> c = Class.forName(inputMethod.toString()); 173 view.setKeyListener((KeyListener) c.newInstance()); 174 } catch (ClassNotFoundException e) { 175 Log.e(TAG, "Could not create input method: " + inputMethod, e); 176 } catch (InstantiationException e) { 177 Log.e(TAG, "Could not create input method: " + inputMethod, e); 178 } catch (IllegalAccessException e) { 179 Log.e(TAG, "Could not create input method: " + inputMethod, e); 180 } 181 } 182 183 @BindingAdapter({"android:lineSpacingExtra"}) 184 public static void setLineSpacingExtra(TextView view, float value) { 185 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 186 view.setLineSpacing(value, view.getLineSpacingMultiplier()); 187 } else { 188 view.setLineSpacing(value, 1); 189 } 190 } 191 192 @BindingAdapter({"android:lineSpacingMultiplier"}) 193 public static void setLineSpacingMultiplier(TextView view, float value) { 194 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 195 view.setLineSpacing(view.getLineSpacingExtra(), value); 196 } else { 197 view.setLineSpacing(0, value); 198 } 199 } 200 201 @BindingAdapter({"android:maxLength"}) 202 public static void setMaxLength(TextView view, int value) { 203 InputFilter[] filters = view.getFilters(); 204 if (filters == null) { 205 filters = new InputFilter[]{ 206 new InputFilter.LengthFilter(value) 207 }; 208 } else { 209 boolean foundMaxLength = false; 210 for (int i = 0; i < filters.length; i++) { 211 InputFilter filter = filters[i]; 212 if (filter instanceof InputFilter.LengthFilter) { 213 foundMaxLength = true; 214 boolean replace = true; 215 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 216 replace = ((InputFilter.LengthFilter) filter).getMax() != value; 217 } 218 if (replace) { 219 filters[i] = new InputFilter.LengthFilter(value); 220 } 221 break; 222 } 223 } 224 if (!foundMaxLength) { 225 // can't use Arrays.copyOf -- it shows up in API 9 226 InputFilter[] oldFilters = filters; 227 filters = new InputFilter[oldFilters.length + 1]; 228 System.arraycopy(oldFilters, 0, filters, 0, oldFilters.length); 229 filters[filters.length - 1] = new InputFilter.LengthFilter(value); 230 } 231 } 232 view.setFilters(filters); 233 } 234 235 @BindingAdapter({"android:password"}) 236 public static void setPassword(TextView view, boolean password) { 237 if (password) { 238 view.setTransformationMethod(PasswordTransformationMethod.getInstance()); 239 } else if (view.getTransformationMethod() instanceof PasswordTransformationMethod) { 240 view.setTransformationMethod(null); 241 } 242 } 243 244 @BindingAdapter({"android:shadowColor"}) 245 public static void setShadowColor(TextView view, int color) { 246 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 247 float dx = view.getShadowDx(); 248 float dy = view.getShadowDy(); 249 float r = view.getShadowRadius(); 250 view.setShadowLayer(r, dx, dy, color); 251 } 252 } 253 254 @BindingAdapter({"android:shadowDx"}) 255 public static void setShadowDx(TextView view, float dx) { 256 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 257 int color = view.getShadowColor(); 258 float dy = view.getShadowDy(); 259 float r = view.getShadowRadius(); 260 view.setShadowLayer(r, dx, dy, color); 261 } 262 } 263 264 @BindingAdapter({"android:shadowDy"}) 265 public static void setShadowDy(TextView view, float dy) { 266 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 267 int color = view.getShadowColor(); 268 float dx = view.getShadowDx(); 269 float r = view.getShadowRadius(); 270 view.setShadowLayer(r, dx, dy, color); 271 } 272 } 273 274 @BindingAdapter({"android:shadowRadius"}) 275 public static void setShadowRadius(TextView view, float r) { 276 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 277 int color = view.getShadowColor(); 278 float dx = view.getShadowDx(); 279 float dy = view.getShadowDy(); 280 view.setShadowLayer(r, dx, dy, color); 281 } 282 } 283 284 @BindingAdapter({"android:textSize"}) 285 public static void setTextSize(TextView view, float size) { 286 view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); 287 } 288 289 @BindingAdapter("android:afterTextChanged") 290 public static void setListener(TextView view, AfterTextChanged after) { 291 setListener(view, null, null, after); 292 } 293 294 @BindingAdapter("android:beforeTextChanged") 295 public static void setListener(TextView view, BeforeTextChanged before) { 296 setListener(view, before, null, null); 297 } 298 299 @BindingAdapter("android:onTextChanged") 300 public static void setListener(TextView view, OnTextChanged onTextChanged) { 301 setListener(view, null, onTextChanged, null); 302 } 303 304 @BindingAdapter({"android:beforeTextChanged", "android:afterTextChanged"}) 305 public static void setListener(TextView view, final BeforeTextChanged before, 306 final AfterTextChanged after) { 307 setListener(view, before, null, after); 308 } 309 310 @BindingAdapter({"android:beforeTextChanged", "android:onTextChanged"}) 311 public static void setListener(TextView view, final BeforeTextChanged before, 312 final OnTextChanged on) { 313 setListener(view, before, on, null); 314 } 315 316 @BindingAdapter({"android:onTextChanged", "android:afterTextChanged"}) 317 public static void setListener(TextView view,final OnTextChanged on, 318 final AfterTextChanged after) { 319 setListener(view, null, on, after); 320 } 321 322 @BindingAdapter({"android:beforeTextChanged", "android:onTextChanged", "android:afterTextChanged"}) 323 public static void setListener(TextView view, final BeforeTextChanged before, 324 final OnTextChanged on, final AfterTextChanged after) { 325 final TextWatcher newValue; 326 if (before == null && after == null && on == null) { 327 newValue = null; 328 } else { 329 newValue = new TextWatcher() { 330 @Override 331 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 332 if (before != null) { 333 before.beforeTextChanged(s, start, count, after); 334 } 335 } 336 337 @Override 338 public void onTextChanged(CharSequence s, int start, int before, int count) { 339 if (on != null) { 340 on.onTextChanged(s, start, before, count); 341 } 342 } 343 344 @Override 345 public void afterTextChanged(Editable s) { 346 if (after != null) { 347 after.afterTextChanged(s); 348 } 349 } 350 }; 351 } 352 final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher); 353 if (oldValue != null) { 354 view.removeTextChangedListener(oldValue); 355 } 356 if (newValue != null) { 357 view.addTextChangedListener(newValue); 358 } 359 } 360 361 public interface AfterTextChanged { 362 void afterTextChanged(Editable s); 363 } 364 365 public interface BeforeTextChanged { 366 void beforeTextChanged(CharSequence s, int start, int count, int after); 367 } 368 369 public interface OnTextChanged { 370 void onTextChanged(CharSequence s, int start, int before, int count); 371 } 372} 373