NonOverlappingLinearLayoutWithForeground.java revision c39d9c75590eca86a5e7e32a8824ba04a0d42e9b
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.content.Context;
17import android.content.res.TypedArray;
18import android.graphics.Canvas;
19import android.graphics.Rect;
20import android.graphics.drawable.Drawable;
21import android.os.Build.VERSION;
22import android.support.annotation.RestrictTo;
23import android.util.AttributeSet;
24import android.widget.LinearLayout;
25
26import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
27
28/**
29 * Implements foreground drawable before M and falls back to M's foreground implementation.
30 */
31class NonOverlappingLinearLayoutWithForeground extends LinearLayout {
32
33    private static final int VERSION_M = 23;
34
35    private Drawable mForeground;
36    private boolean mForegroundBoundsChanged;
37    private final Rect mSelfBounds = new Rect();
38
39    public NonOverlappingLinearLayoutWithForeground(Context context) {
40        this(context, null);
41    }
42
43    public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs) {
44        this(context, attrs, 0);
45    }
46
47    public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs,
48            int defStyle) {
49        super(context, attrs, defStyle);
50        if (context.getApplicationInfo().targetSdkVersion >= VERSION_M
51                && VERSION.SDK_INT >= VERSION_M) {
52            // don't need do anything, base View constructor >=M already reads the foreground if
53            // targetSDK is >= M.
54        } else {
55            // in other cases, including M but targetSDK is less than M, we need setForeground in
56            // code.
57            TypedArray a = context.obtainStyledAttributes(attrs,
58                    new int[] { android.R.attr.foreground });
59            Drawable d = a.getDrawable(0);
60            if (d != null) {
61                setForegroundCompat(d);
62            }
63        }
64    }
65
66    public void setForegroundCompat(Drawable d) {
67        if (VERSION.SDK_INT >= VERSION_M) {
68            // From M,  foreground is naturally supported.
69            ForegroundHelper.getInstance().setForeground(this, d);
70        } else {
71            // before M, do our own customized foreground draw.
72            if (mForeground != d) {
73                mForeground = d;
74                mForegroundBoundsChanged = true;
75                setWillNotDraw(false);
76                mForeground.setCallback(this);
77                if (mForeground.isStateful()) {
78                    mForeground.setState(getDrawableState());
79                }
80            }
81        }
82    }
83
84    public Drawable getForegroundCompat() {
85        if (VERSION.SDK_INT >= VERSION_M) {
86            return ForegroundHelper.getInstance().getForeground(this);
87        } else {
88            return mForeground;
89        }
90    }
91
92    @Override
93    public void draw(Canvas canvas) {
94        super.draw(canvas);
95        if (mForeground != null) {
96            final Drawable foreground = mForeground;
97            if (mForegroundBoundsChanged) {
98                mForegroundBoundsChanged = false;
99                final Rect selfBounds = mSelfBounds;
100                final int w = getRight() - getLeft();
101                final int h = getBottom() - getTop();
102                selfBounds.set(0, 0, w, h);
103                foreground.setBounds(selfBounds);
104            }
105            foreground.draw(canvas);
106        }
107    }
108
109    @Override
110    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
111        super.onLayout(changed, left, top, right, bottom);
112        mForegroundBoundsChanged |= changed;
113    }
114
115    @Override
116    protected boolean verifyDrawable(Drawable who) {
117        return super.verifyDrawable(who) || (who == mForeground);
118    }
119
120    @Override
121    public void jumpDrawablesToCurrentState() {
122        super.jumpDrawablesToCurrentState();
123        if (mForeground != null) {
124            mForeground.jumpToCurrentState();
125        }
126    }
127
128    @Override
129    protected void drawableStateChanged() {
130        super.drawableStateChanged();
131        if (mForeground != null && mForeground.isStateful()) {
132            mForeground.setState(getDrawableState());
133        }
134    }
135
136    /**
137     * Avoids creating a hardware layer when animating alpha.
138     */
139    @Override
140    public boolean hasOverlappingRendering() {
141        return false;
142    }
143}