• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.support.design.internal;
18 
19 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20 
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.graphics.Canvas;
24 import android.graphics.Rect;
25 import android.graphics.drawable.Drawable;
26 import android.support.annotation.NonNull;
27 import android.support.annotation.RequiresApi;
28 import android.support.annotation.RestrictTo;
29 import android.support.design.R;
30 import android.support.v7.widget.LinearLayoutCompat;
31 import android.util.AttributeSet;
32 import android.view.Gravity;
33 
34 /**
35  * @hide
36  */
37 @RestrictTo(LIBRARY_GROUP)
38 public class ForegroundLinearLayout extends LinearLayoutCompat {
39 
40     private Drawable mForeground;
41 
42     private final Rect mSelfBounds = new Rect();
43 
44     private final Rect mOverlayBounds = new Rect();
45 
46     private int mForegroundGravity = Gravity.FILL;
47 
48     protected boolean mForegroundInPadding = true;
49 
50     boolean mForegroundBoundsChanged = false;
51 
ForegroundLinearLayout(Context context)52     public ForegroundLinearLayout(Context context) {
53         this(context, null);
54     }
55 
ForegroundLinearLayout(Context context, AttributeSet attrs)56     public ForegroundLinearLayout(Context context, AttributeSet attrs) {
57         this(context, attrs, 0);
58     }
59 
ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle)60     public ForegroundLinearLayout(Context context, AttributeSet attrs, int defStyle) {
61         super(context, attrs, defStyle);
62 
63         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundLinearLayout,
64                 defStyle, 0);
65 
66         mForegroundGravity = a.getInt(
67                 R.styleable.ForegroundLinearLayout_android_foregroundGravity, mForegroundGravity);
68 
69         final Drawable d = a.getDrawable(R.styleable.ForegroundLinearLayout_android_foreground);
70         if (d != null) {
71             setForeground(d);
72         }
73 
74         mForegroundInPadding = a.getBoolean(
75                 R.styleable.ForegroundLinearLayout_foregroundInsidePadding, true);
76 
77         a.recycle();
78     }
79 
80     /**
81      * Describes how the foreground is positioned.
82      *
83      * @return foreground gravity.
84      * @see #setForegroundGravity(int)
85      */
86     @Override
getForegroundGravity()87     public int getForegroundGravity() {
88         return mForegroundGravity;
89     }
90 
91     /**
92      * Describes how the foreground is positioned. Defaults to START and TOP.
93      *
94      * @param foregroundGravity See {@link android.view.Gravity}
95      * @see #getForegroundGravity()
96      */
97     @Override
setForegroundGravity(int foregroundGravity)98     public void setForegroundGravity(int foregroundGravity) {
99         if (mForegroundGravity != foregroundGravity) {
100             if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
101                 foregroundGravity |= Gravity.START;
102             }
103 
104             if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
105                 foregroundGravity |= Gravity.TOP;
106             }
107 
108             mForegroundGravity = foregroundGravity;
109 
110             if (mForegroundGravity == Gravity.FILL && mForeground != null) {
111                 Rect padding = new Rect();
112                 mForeground.getPadding(padding);
113             }
114 
115             requestLayout();
116         }
117     }
118 
119     @Override
verifyDrawable(Drawable who)120     protected boolean verifyDrawable(Drawable who) {
121         return super.verifyDrawable(who) || (who == mForeground);
122     }
123 
124     @RequiresApi(11)
125     @Override
jumpDrawablesToCurrentState()126     public void jumpDrawablesToCurrentState() {
127         super.jumpDrawablesToCurrentState();
128         if (mForeground != null) {
129             mForeground.jumpToCurrentState();
130         }
131     }
132 
133     @Override
drawableStateChanged()134     protected void drawableStateChanged() {
135         super.drawableStateChanged();
136         if (mForeground != null && mForeground.isStateful()) {
137             mForeground.setState(getDrawableState());
138         }
139     }
140 
141     /**
142      * Supply a Drawable that is to be rendered on top of all of the child
143      * views in the frame layout.  Any padding in the Drawable will be taken
144      * into account by ensuring that the children are inset to be placed
145      * inside of the padding area.
146      *
147      * @param drawable The Drawable to be drawn on top of the children.
148      */
149     @Override
setForeground(Drawable drawable)150     public void setForeground(Drawable drawable) {
151         if (mForeground != drawable) {
152             if (mForeground != null) {
153                 mForeground.setCallback(null);
154                 unscheduleDrawable(mForeground);
155             }
156 
157             mForeground = drawable;
158 
159             if (drawable != null) {
160                 setWillNotDraw(false);
161                 drawable.setCallback(this);
162                 if (drawable.isStateful()) {
163                     drawable.setState(getDrawableState());
164                 }
165                 if (mForegroundGravity == Gravity.FILL) {
166                     Rect padding = new Rect();
167                     drawable.getPadding(padding);
168                 }
169             } else {
170                 setWillNotDraw(true);
171             }
172             requestLayout();
173             invalidate();
174         }
175     }
176 
177     /**
178      * Returns the drawable used as the foreground of this FrameLayout. The
179      * foreground drawable, if non-null, is always drawn on top of the children.
180      *
181      * @return A Drawable or null if no foreground was set.
182      */
183     @Override
getForeground()184     public Drawable getForeground() {
185         return mForeground;
186     }
187 
188     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)189     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
190         super.onLayout(changed, left, top, right, bottom);
191         mForegroundBoundsChanged |= changed;
192     }
193 
194     @Override
onSizeChanged(int w, int h, int oldw, int oldh)195     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
196         super.onSizeChanged(w, h, oldw, oldh);
197         mForegroundBoundsChanged = true;
198     }
199 
200     @Override
draw(@onNull Canvas canvas)201     public void draw(@NonNull Canvas canvas) {
202         super.draw(canvas);
203 
204         if (mForeground != null) {
205             final Drawable foreground = mForeground;
206 
207             if (mForegroundBoundsChanged) {
208                 mForegroundBoundsChanged = false;
209                 final Rect selfBounds = mSelfBounds;
210                 final Rect overlayBounds = mOverlayBounds;
211 
212                 final int w = getRight() - getLeft();
213                 final int h = getBottom() - getTop();
214 
215                 if (mForegroundInPadding) {
216                     selfBounds.set(0, 0, w, h);
217                 } else {
218                     selfBounds.set(getPaddingLeft(), getPaddingTop(),
219                             w - getPaddingRight(), h - getPaddingBottom());
220                 }
221 
222                 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
223                         foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
224                 foreground.setBounds(overlayBounds);
225             }
226 
227             foreground.draw(canvas);
228         }
229     }
230 
231     @RequiresApi(21)
232     @Override
drawableHotspotChanged(float x, float y)233     public void drawableHotspotChanged(float x, float y) {
234         super.drawableHotspotChanged(x, y);
235         if (mForeground != null) {
236             mForeground.setHotspot(x, y);
237         }
238     }
239 
240 }
241