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