TextViewBindingAdapter.java revision 96b22e7bbbf942aea1079dc8e8d0c4657663e5a7
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(value = {"android:beforeTextChanged", "android:onTextChanged", 290 "android:afterTextChanged"}, requireAll = false) 291 public static void setTextWatcher(TextView view, final BeforeTextChanged before, 292 final OnTextChanged on, final AfterTextChanged after) { 293 final TextWatcher newValue; 294 if (before == null && after == null && on == null) { 295 newValue = null; 296 } else { 297 newValue = new TextWatcher() { 298 @Override 299 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 300 if (before != null) { 301 before.beforeTextChanged(s, start, count, after); 302 } 303 } 304 305 @Override 306 public void onTextChanged(CharSequence s, int start, int before, int count) { 307 if (on != null) { 308 on.onTextChanged(s, start, before, count); 309 } 310 } 311 312 @Override 313 public void afterTextChanged(Editable s) { 314 if (after != null) { 315 after.afterTextChanged(s); 316 } 317 } 318 }; 319 } 320 final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher); 321 if (oldValue != null) { 322 view.removeTextChangedListener(oldValue); 323 } 324 if (newValue != null) { 325 view.addTextChangedListener(newValue); 326 } 327 } 328 329 public interface AfterTextChanged { 330 void afterTextChanged(Editable s); 331 } 332 333 public interface BeforeTextChanged { 334 void beforeTextChanged(CharSequence s, int start, int count, int after); 335 } 336 337 public interface OnTextChanged { 338 void onTextChanged(CharSequence s, int start, int before, int count); 339 } 340} 341