Home | History | Annotate | Download | only in adapters
      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  */
     16 package android.databinding.adapters;
     17 
     18 import android.databinding.BindingAdapter;
     19 import android.databinding.BindingMethod;
     20 import android.databinding.BindingMethods;
     21 import android.graphics.drawable.Drawable;
     22 import android.os.Build;
     23 import android.text.Editable;
     24 import android.text.InputFilter;
     25 import android.text.InputType;
     26 import android.text.TextWatcher;
     27 import android.text.method.DialerKeyListener;
     28 import android.text.method.DigitsKeyListener;
     29 import android.text.method.KeyListener;
     30 import android.text.method.PasswordTransformationMethod;
     31 import android.text.method.TextKeyListener;
     32 import android.util.Log;
     33 import android.util.TypedValue;
     34 import android.widget.TextView;
     35 
     36 import 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 })
     50 public 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