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.annotation.TargetApi;
19 import android.databinding.BindingAdapter;
20 import android.databinding.BindingMethod;
21 import android.databinding.BindingMethods;
22 import android.graphics.drawable.Drawable;
23 import android.os.Build;
24 import android.os.Build.VERSION;
25 import android.os.Build.VERSION_CODES;
26 import android.view.View;
27 import android.view.View.OnAttachStateChangeListener;
28 import com.android.databinding.library.baseAdapters.R;
29 
30 @BindingMethods({
31         @BindingMethod(type = View.class, attribute = "android:backgroundTint", method = "setBackgroundTintList"),
32         @BindingMethod(type = View.class, attribute = "android:fadeScrollbars", method = "setScrollbarFadingEnabled"),
33         @BindingMethod(type = View.class, attribute = "android:getOutline", method = "setOutlineProvider"),
34         @BindingMethod(type = View.class, attribute = "android:nextFocusForward", method = "setNextFocusForwardId"),
35         @BindingMethod(type = View.class, attribute = "android:nextFocusLeft", method = "setNextFocusLeftId"),
36         @BindingMethod(type = View.class, attribute = "android:nextFocusRight", method = "setNextFocusRightId"),
37         @BindingMethod(type = View.class, attribute = "android:nextFocusUp", method = "setNextFocusUpId"),
38         @BindingMethod(type = View.class, attribute = "android:nextFocusDown", method = "setNextFocusDownId"),
39         @BindingMethod(type = View.class, attribute = "android:requiresFadingEdge", method = "setVerticalFadingEdgeEnabled"),
40         @BindingMethod(type = View.class, attribute = "android:scrollbarDefaultDelayBeforeFade", method = "setScrollBarDefaultDelayBeforeFade"),
41         @BindingMethod(type = View.class, attribute = "android:scrollbarFadeDuration", method = "setScrollBarFadeDuration"),
42         @BindingMethod(type = View.class, attribute = "android:scrollbarSize", method = "setScrollBarSize"),
43         @BindingMethod(type = View.class, attribute = "android:scrollbarStyle", method = "setScrollBarStyle"),
44         @BindingMethod(type = View.class, attribute = "android:transformPivotX", method = "setPivotX"),
45         @BindingMethod(type = View.class, attribute = "android:transformPivotY", method = "setPivotY"),
46         @BindingMethod(type = View.class, attribute = "android:onDrag", method = "setOnDragListener"),
47         @BindingMethod(type = View.class, attribute = "android:onClick", method = "setOnClickListener"),
48         @BindingMethod(type = View.class, attribute = "android:onApplyWindowInsets", method = "setOnApplyWindowInsetsListener"),
49         @BindingMethod(type = View.class, attribute = "android:onCreateContextMenu", method = "setOnCreateContextMenuListener"),
50         @BindingMethod(type = View.class, attribute = "android:onFocusChange", method = "setOnFocusChangeListener"),
51         @BindingMethod(type = View.class, attribute = "android:onGenericMotion", method = "setOnGenericMotionListener"),
52         @BindingMethod(type = View.class, attribute = "android:onHover", method = "setOnHoverListener"),
53         @BindingMethod(type = View.class, attribute = "android:onKey", method = "setOnKeyListener"),
54         @BindingMethod(type = View.class, attribute = "android:onLongClick", method = "setOnLongClickListener"),
55         @BindingMethod(type = View.class, attribute = "android:onSystemUiVisibilityChange", method = "setOnSystemUiVisibilityChangeListener"),
56         @BindingMethod(type = View.class, attribute = "android:onTouch", method = "setOnTouchListener"),
57 })
58 public class ViewBindingAdapter {
59     public static int FADING_EDGE_NONE = 0;
60     public static int FADING_EDGE_HORIZONTAL = 1;
61     public static int FADING_EDGE_VERTICAL = 2;
62 
63     @BindingAdapter({"android:padding"})
setPadding(View view, float paddingFloat)64     public static void setPadding(View view, float paddingFloat) {
65         final int padding = pixelsToDimensionPixelSize(paddingFloat);
66         view.setPadding(padding, padding, padding, padding);
67     }
68 
69     @BindingAdapter({"android:paddingBottom"})
setPaddingBottom(View view, float paddingFloat)70     public static void setPaddingBottom(View view, float paddingFloat) {
71         final int padding = pixelsToDimensionPixelSize(paddingFloat);
72         view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(),
73                 padding);
74     }
75 
76     @BindingAdapter({"android:paddingEnd"})
setPaddingEnd(View view, float paddingFloat)77     public static void setPaddingEnd(View view, float paddingFloat) {
78         final int padding = pixelsToDimensionPixelSize(paddingFloat);
79         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
80             view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), padding,
81                     view.getPaddingBottom());
82         } else {
83             view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
84                     view.getPaddingBottom());
85         }
86     }
87 
88     @BindingAdapter({"android:paddingLeft"})
setPaddingLeft(View view, float paddingFloat)89     public static void setPaddingLeft(View view, float paddingFloat) {
90         final int padding = pixelsToDimensionPixelSize(paddingFloat);
91         view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
92                 view.getPaddingBottom());
93     }
94 
95     @BindingAdapter({"android:paddingRight"})
setPaddingRight(View view, float paddingFloat)96     public static void setPaddingRight(View view, float paddingFloat) {
97         final int padding = pixelsToDimensionPixelSize(paddingFloat);
98         view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), padding,
99                 view.getPaddingBottom());
100     }
101 
102     @BindingAdapter({"android:paddingStart"})
setPaddingStart(View view, float paddingFloat)103     public static void setPaddingStart(View view, float paddingFloat) {
104         final int padding = pixelsToDimensionPixelSize(paddingFloat);
105         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
106             view.setPaddingRelative(padding, view.getPaddingTop(), view.getPaddingEnd(),
107                     view.getPaddingBottom());
108         } else {
109             view.setPadding(padding, view.getPaddingTop(), view.getPaddingRight(),
110                     view.getPaddingBottom());
111         }
112     }
113 
114     @BindingAdapter({"android:paddingTop"})
setPaddingTop(View view, float paddingFloat)115     public static void setPaddingTop(View view, float paddingFloat) {
116         final int padding = pixelsToDimensionPixelSize(paddingFloat);
117         view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(),
118                 view.getPaddingBottom());
119     }
120 
121     @BindingAdapter({"android:requiresFadingEdge"})
setRequiresFadingEdge(View view, int value)122     public static void setRequiresFadingEdge(View view, int value) {
123         final boolean vertical = (value & FADING_EDGE_VERTICAL) != 0;
124         final boolean horizontal = (value & FADING_EDGE_HORIZONTAL) != 0;
125         view.setVerticalFadingEdgeEnabled(vertical);
126         view.setHorizontalFadingEdgeEnabled(horizontal);
127     }
128 
129     @BindingAdapter({"android:onClickListener", "android:clickable"})
setClickListener(View view, View.OnClickListener clickListener, boolean clickable)130     public static void setClickListener(View view, View.OnClickListener clickListener,
131             boolean clickable) {
132         view.setOnClickListener(clickListener);
133         view.setClickable(clickable);
134     }
135 
136     @BindingAdapter({"android:onClick", "android:clickable"})
setOnClick(View view, View.OnClickListener clickListener, boolean clickable)137     public static void setOnClick(View view, View.OnClickListener clickListener,
138             boolean clickable) {
139         view.setOnClickListener(clickListener);
140         view.setClickable(clickable);
141     }
142 
143     @BindingAdapter({"android:onLongClickListener", "android:longClickable"})
setOnLongClickListener(View view, View.OnLongClickListener clickListener, boolean clickable)144     public static void setOnLongClickListener(View view, View.OnLongClickListener clickListener,
145             boolean clickable) {
146         view.setOnLongClickListener(clickListener);
147         view.setLongClickable(clickable);
148     }
149 
150     @BindingAdapter({"android:onLongClick", "android:longClickable"})
setOnLongClick(View view, View.OnLongClickListener clickListener, boolean clickable)151     public static void setOnLongClick(View view, View.OnLongClickListener clickListener,
152             boolean clickable) {
153         view.setOnLongClickListener(clickListener);
154         view.setLongClickable(clickable);
155     }
156 
157     @BindingAdapter(value = {"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"},
158             requireAll = false)
setOnAttachStateChangeListener(View view, final OnViewDetachedFromWindow detach, final OnViewAttachedToWindow attach)159     public static void setOnAttachStateChangeListener(View view,
160             final OnViewDetachedFromWindow detach, final OnViewAttachedToWindow attach) {
161         if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
162             final OnAttachStateChangeListener newListener;
163             if (detach == null && attach == null) {
164                 newListener = null;
165             } else {
166                 newListener = new OnAttachStateChangeListener() {
167                     @Override
168                     public void onViewAttachedToWindow(View v) {
169                         if (attach != null) {
170                             attach.onViewAttachedToWindow(v);
171                         }
172                     }
173 
174                     @Override
175                     public void onViewDetachedFromWindow(View v) {
176                         if (detach != null) {
177                             detach.onViewDetachedFromWindow(v);
178                         }
179                     }
180                 };
181             }
182             final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
183                     newListener, R.id.onAttachStateChangeListener);
184             if (oldListener != null) {
185                 view.removeOnAttachStateChangeListener(oldListener);
186             }
187             if (newListener != null) {
188                 view.addOnAttachStateChangeListener(newListener);
189             }
190         }
191     }
192 
193     @BindingAdapter("android:onLayoutChange")
setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue, View.OnLayoutChangeListener newValue)194     public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
195             View.OnLayoutChangeListener newValue) {
196         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
197             if (oldValue != null) {
198                 view.removeOnLayoutChangeListener(oldValue);
199             }
200             if (newValue != null) {
201                 view.addOnLayoutChangeListener(newValue);
202             }
203         }
204     }
205 
206     @SuppressWarnings("deprecation")
207     @BindingAdapter("android:background")
setBackground(View view, Drawable drawable)208     public static void setBackground(View view, Drawable drawable) {
209         if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
210             view.setBackground(drawable);
211         } else {
212             view.setBackgroundDrawable(drawable);
213         }
214     }
215 
216     // Follows the same conversion mechanism as in TypedValue.complexToDimensionPixelSize as used
217     // when setting padding. It rounds off the float value unless the value is < 1.
218     // When a value is between 0 and 1, it is set to 1. A value less than 0 is set to -1.
pixelsToDimensionPixelSize(float pixels)219     private static int pixelsToDimensionPixelSize(float pixels) {
220         final int result = (int) (pixels + 0.5f);
221         if (result != 0) {
222             return result;
223         } else if (pixels == 0) {
224             return 0;
225         } else if (pixels > 0) {
226             return 1;
227         } else {
228             return -1;
229         }
230     }
231 
232     @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
233     public interface OnViewDetachedFromWindow {
onViewDetachedFromWindow(View v)234         void onViewDetachedFromWindow(View v);
235     }
236 
237     @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
238     public interface OnViewAttachedToWindow {
onViewAttachedToWindow(View v)239         void onViewAttachedToWindow(View v);
240     }
241 }
242