[go: nahoru, domu]

1/*
2 * Copyright (C) 2016 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.setupwizardlib.view;
18
19import android.annotation.TargetApi;
20import android.content.Context;
21import android.graphics.Canvas;
22import android.graphics.RectF;
23import android.os.Build;
24import android.util.AttributeSet;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.WindowInsets;
28
29/**
30 * This class provides sticky header functionality in a recycler view, to use with
31 * SetupWizardIllustration. To use this, add a header tagged with "sticky". The header will continue
32 * to be drawn when the sticky element hits the top of the view.
33 *
34 * <p>There are a few things to note:
35 * <ol>
36 *   <li>The view does not work well with padding. b/16190933
37 *   <li>If fitsSystemWindows is true, then this will offset the sticking position by the height of
38 *   the system decorations at the top of the screen.
39 * </ol>
40 */
41public class StickyHeaderRecyclerView extends HeaderRecyclerView {
42
43    private View mSticky;
44    private int mStatusBarInset = 0;
45    private RectF mStickyRect = new RectF();
46
47    public StickyHeaderRecyclerView(Context context) {
48        super(context);
49    }
50
51    public StickyHeaderRecyclerView(Context context, AttributeSet attrs) {
52        super(context, attrs);
53    }
54
55    public StickyHeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
56        super(context, attrs, defStyleAttr);
57    }
58
59    @Override
60    protected void onLayout(boolean changed, int l, int t, int r, int b) {
61        super.onLayout(changed, l, t, r, b);
62        if (mSticky == null) {
63            updateStickyView();
64        }
65        if (mSticky != null) {
66            final View headerView = getHeader();
67            if (headerView != null && headerView.getHeight() == 0) {
68                headerView.layout(0, -headerView.getMeasuredHeight(),
69                        headerView.getMeasuredWidth(), 0);
70            }
71        }
72    }
73
74    @Override
75    protected void onMeasure(int widthSpec, int heightSpec) {
76        super.onMeasure(widthSpec, heightSpec);
77        if (mSticky != null) {
78            measureChild(getHeader(), widthSpec, heightSpec);
79        }
80    }
81
82    public void updateStickyView() {
83        final View header = getHeader();
84        if (header != null) {
85            mSticky = header.findViewWithTag("sticky");
86        }
87    }
88
89    @Override
90    public void draw(Canvas canvas) {
91        super.draw(canvas);
92        if (mSticky != null) {
93            final View headerView = getHeader();
94            final int saveCount = canvas.save();
95            // The view to draw when sticking to the top
96            final View drawTarget = headerView != null ? headerView : mSticky;
97            // The offset to draw the view at when sticky
98            final int drawOffset = headerView != null ? mSticky.getTop() : 0;
99            // Position of the draw target, relative to the outside of the scrollView
100            final int drawTop = drawTarget.getTop();
101            if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) {
102                // RecyclerView does not translate the canvas, so we can simply draw at the top
103                mStickyRect.set(0, -drawOffset + mStatusBarInset, drawTarget.getWidth(),
104                        drawTarget.getHeight() - drawOffset + mStatusBarInset);
105                canvas.translate(0, mStickyRect.top);
106                canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight());
107                drawTarget.draw(canvas);
108            } else {
109                mStickyRect.setEmpty();
110            }
111            canvas.restoreToCount(saveCount);
112        }
113    }
114
115    @Override
116    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
117    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
118        if (getFitsSystemWindows()) {
119            mStatusBarInset = insets.getSystemWindowInsetTop();
120            insets.replaceSystemWindowInsets(
121                    insets.getSystemWindowInsetLeft(),
122                    0, /* top */
123                    insets.getSystemWindowInsetRight(),
124                    insets.getSystemWindowInsetBottom()
125            );
126        }
127        return insets;
128    }
129
130    @Override
131    public boolean dispatchTouchEvent(MotionEvent ev) {
132        if (mStickyRect.contains(ev.getX(), ev.getY())) {
133            ev.offsetLocation(-mStickyRect.left, -mStickyRect.top);
134            return getHeader().dispatchTouchEvent(ev);
135        } else {
136            return super.dispatchTouchEvent(ev);
137        }
138    }
139}
140