ExpandableView.java revision c9c00ae2fa5fb787e9f12705f8cd8de445ecde4b
1/*
2 * Copyright (C) 2014 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
17package com.android.systemui.statusbar;
18
19import android.content.Context;
20import android.util.AttributeSet;
21import android.view.MotionEvent;
22import android.view.View;
23import android.view.ViewGroup;
24
25/**
26 * An abstract view for expandable views.
27 */
28public abstract class ExpandableView extends ViewGroup {
29
30    private OnHeightChangedListener mOnHeightChangedListener;
31    protected int mActualHeight;
32    protected int mClipTopAmount;
33    private boolean mActualHeightInitialized;
34
35    public ExpandableView(Context context, AttributeSet attrs) {
36        super(context, attrs);
37    }
38
39    @Override
40    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
41        for (int i = 0; i < getChildCount(); i++) {
42            View child = getChildAt(i);
43            int height = child.getMeasuredHeight();
44            int width = child.getMeasuredWidth();
45            int center = getWidth() / 2;
46            int childLeft = center - width / 2;
47            child.layout(childLeft,
48                    0,
49                    childLeft + width,
50                    height);
51        }
52        if (!mActualHeightInitialized && mActualHeight == 0) {
53            mActualHeight = getInitialHeight();
54        }
55        mActualHeightInitialized = true;
56    }
57
58    protected int getInitialHeight() {
59        return getHeight();
60    }
61
62    @Override
63    public boolean dispatchTouchEvent(MotionEvent ev) {
64        if (filterMotionEvent(ev)) {
65            return super.dispatchTouchEvent(ev);
66        }
67        return false;
68    }
69
70    private boolean filterMotionEvent(MotionEvent event) {
71        return event.getActionMasked() != MotionEvent.ACTION_DOWN
72                || event.getY() > mClipTopAmount && event.getY() < mActualHeight;
73    }
74
75    /**
76     * Sets the actual height of this notification. This is different than the laid out
77     * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
78     *
79     * @param actualHeight The height of this notification.
80     * @param notifyListeners Whether the listener should be informed about the change.
81     */
82    public void setActualHeight(int actualHeight, boolean notifyListeners) {
83        mActualHeight = actualHeight;
84        if (notifyListeners) {
85            notifyHeightChanged();
86        }
87    }
88
89    public void setActualHeight(int actualHeight) {
90        setActualHeight(actualHeight, true);
91    }
92
93    /**
94     * See {@link #setActualHeight}.
95     *
96     * @return The current actual height of this notification.
97     */
98    public int getActualHeight() {
99        return mActualHeight;
100    }
101
102    /**
103     * @return The maximum height of this notification.
104     */
105    public int getMaxHeight() {
106        return getHeight();
107    }
108
109    /**
110     * @return The minimum height of this notification.
111     */
112    public int getMinHeight() {
113        return getHeight();
114    }
115
116    /**
117     * Sets the notification as dimmed. The default implementation does nothing.
118     *
119     * @param dimmed Whether the notification should be dimmed.
120     * @param fade Whether an animation should be played to change the state.
121     */
122    public void setDimmed(boolean dimmed, boolean fade) {
123    }
124
125    /**
126     * @return The desired notification height.
127     */
128    public int getIntrinsicHeight() {
129        return getHeight();
130    }
131
132    /**
133     * Sets the amount this view should be clipped from the top. This is used when an expanded
134     * notification is scrolling in the top or bottom stack.
135     *
136     * @param clipTopAmount The amount of pixels this view should be clipped from top.
137     */
138    public void setClipTopAmount(int clipTopAmount) {
139        mClipTopAmount = clipTopAmount;
140    }
141
142    public int getClipTopAmount() {
143        return mClipTopAmount;
144    }
145
146    public void setOnHeightChangedListener(OnHeightChangedListener listener) {
147        mOnHeightChangedListener = listener;
148    }
149
150    /**
151     * @return Whether we can expand this views content.
152     */
153    public boolean isContentExpandable() {
154        return false;
155    }
156
157    public void notifyHeightChanged() {
158        if (mOnHeightChangedListener != null) {
159            mOnHeightChangedListener.onHeightChanged(this);
160        }
161    }
162
163    public boolean isTransparent() {
164        return false;
165    }
166
167    /**
168     * A listener notifying when {@link #getActualHeight} changes.
169     */
170    public interface OnHeightChangedListener {
171        void onHeightChanged(ExpandableView view);
172    }
173}
174