1a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/*
2a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Copyright (C) 2015 The Android Open Source Project
3a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
4a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * you may not use this file except in compliance with the License.
6a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * You may obtain a copy of the License at
7a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
8a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes *
10a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Unless required by applicable law or agreed to in writing, software
11a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * See the License for the specific language governing permissions and
14a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * limitations under the License.
15a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
16a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
17a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banespackage android.support.design.widget;
18a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
1944e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banesimport android.os.Build;
20a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.support.v4.view.ViewCompat;
21a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport android.view.View;
22b1a51676163f8db131e6d97970459346735253beChris Banesimport android.view.ViewParent;
23a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
24a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes/**
25a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Utility helper for moving a {@link android.view.View} around using
26a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link android.view.View#offsetLeftAndRight(int)} and
27a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * {@link android.view.View#offsetTopAndBottom(int)}.
28a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * <p>
29a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * Also the setting of absolute offsets (similar to translationX/Y), rather than additive
30a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes * offsets.
31a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes */
32a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesclass ViewOffsetHelper {
33a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private final View mView;
35a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mLayoutTop;
37a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mLayoutLeft;
38a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mOffsetTop;
39a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    private int mOffsetLeft;
40a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
41a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public ViewOffsetHelper(View view) {
42a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mView = view;
43a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
44a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
45a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public void onViewLayout() {
46a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        // Now grab the intended top
47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mLayoutTop = mView.getTop();
48a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        mLayoutLeft = mView.getLeft();
49a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
50a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        // And offset it as needed
51a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        updateOffsets();
52a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
53a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
54ecd4b988781330e346244be73dca39cd04c71391Chris Banes    private void updateOffsets() {
55ecd4b988781330e346244be73dca39cd04c71391Chris Banes        ViewCompat.offsetTopAndBottom(mView, mOffsetTop - (mView.getTop() - mLayoutTop));
56ecd4b988781330e346244be73dca39cd04c71391Chris Banes        ViewCompat.offsetLeftAndRight(mView, mOffsetLeft - (mView.getLeft() - mLayoutLeft));
57b1a51676163f8db131e6d97970459346735253beChris Banes
5844e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes        // Manually invalidate the view and parent to make sure we get drawn pre-M
5944e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes        if (Build.VERSION.SDK_INT < 23) {
6044e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes            tickleInvalidationFlag(mView);
6144e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes            final ViewParent vp = mView.getParent();
6244e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes            if (vp instanceof View) {
6344e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes                tickleInvalidationFlag((View) vp);
6444e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes            }
65a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
66a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
67a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
6844e00ab1860a78d91ad6f586f41eaae0c108a3bcChris Banes    private static void tickleInvalidationFlag(View view) {
69a59da11da29af8bc9d8e07a21d68e04d6de3df92Chris Banes        final float x = ViewCompat.getTranslationX(view);
70a59da11da29af8bc9d8e07a21d68e04d6de3df92Chris Banes        ViewCompat.setTranslationX(view, x + 1);
71a59da11da29af8bc9d8e07a21d68e04d6de3df92Chris Banes        ViewCompat.setTranslationX(view, x);
72a59da11da29af8bc9d8e07a21d68e04d6de3df92Chris Banes    }
73a59da11da29af8bc9d8e07a21d68e04d6de3df92Chris Banes
74a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
75a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Set the top and bottom offset for this {@link ViewOffsetHelper}'s view.
76a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
77a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @param offset the offset in px.
78a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return true if the offset has changed
79a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
80a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public boolean setTopAndBottomOffset(int offset) {
81a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mOffsetTop != offset) {
82a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOffsetTop = offset;
83a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            updateOffsets();
84a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return true;
85a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
86a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return false;
87a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
88a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
89a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    /**
90a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * Set the left and right offset for this {@link ViewOffsetHelper}'s view.
91a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     *
92a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @param offset the offset in px.
93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     * @return true if the offset has changed
94a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes     */
95a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public boolean setLeftAndRightOffset(int offset) {
96a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        if (mOffsetLeft != offset) {
97a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            mOffsetLeft = offset;
98a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            updateOffsets();
99a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return true;
100a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        }
101a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return false;
102a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
103a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
104a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public int getTopAndBottomOffset() {
105a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mOffsetTop;
106a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
107a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
108a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    public int getLeftAndRightOffset() {
109a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        return mOffsetLeft;
110a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes    }
111a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes}