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.example.android.elevationdrag;
18
19import android.content.Context;
20import android.support.v4.widget.ViewDragHelper;
21import android.util.AttributeSet;
22import android.view.MotionEvent;
23import android.view.View;
24import android.widget.FrameLayout;
25
26import java.util.ArrayList;
27import java.util.List;
28
29/**
30 * A {@link FrameLayout} that allows the user to drag and reposition child views.
31 */
32public class DragFrameLayout extends FrameLayout {
33
34    /**
35     * The list of {@link View}s that will be draggable.
36     */
37    private List<View> mDragViews;
38
39    /**
40     * The {@link DragFrameLayoutController} that will be notify on drag.
41     */
42    private DragFrameLayoutController mDragFrameLayoutController;
43
44    private ViewDragHelper mDragHelper;
45
46    public DragFrameLayout(Context context) {
47        this(context, null, 0, 0);
48    }
49
50    public DragFrameLayout(Context context, AttributeSet attrs) {
51        this(context, attrs, 0, 0);
52    }
53
54    public DragFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
55        this(context, attrs, defStyleAttr, 0);
56    }
57
58    public DragFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
59        super(context, attrs, defStyleAttr, defStyleRes);
60        mDragViews = new ArrayList<View>();
61
62        /**
63         * Create the {@link ViewDragHelper} and set its callback.
64         */
65        mDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
66            @Override
67            public boolean tryCaptureView(View child, int pointerId) {
68                return mDragViews.contains(child);
69            }
70
71            @Override
72            public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
73                super.onViewPositionChanged(changedView, left, top, dx, dy);
74            }
75
76            @Override
77            public int clampViewPositionHorizontal(View child, int left, int dx) {
78                return left;
79            }
80
81            @Override
82            public int clampViewPositionVertical(View child, int top, int dy) {
83                return top;
84            }
85
86            @Override
87            public void onViewCaptured(View capturedChild, int activePointerId) {
88                super.onViewCaptured(capturedChild, activePointerId);
89                if (mDragFrameLayoutController != null) {
90                    mDragFrameLayoutController.onDragDrop(true);
91                }
92            }
93
94            @Override
95            public void onViewReleased(View releasedChild, float xvel, float yvel) {
96                super.onViewReleased(releasedChild, xvel, yvel);
97                if (mDragFrameLayoutController != null) {
98                    mDragFrameLayoutController.onDragDrop(false);
99                }
100            }
101        });
102    }
103
104    @Override
105    public boolean onInterceptTouchEvent(MotionEvent ev) {
106        final int action = ev.getActionMasked();
107        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
108            mDragHelper.cancel();
109            return false;
110        }
111        return mDragHelper.shouldInterceptTouchEvent(ev);
112    }
113
114    @Override
115    public boolean onTouchEvent(MotionEvent ev) {
116        mDragHelper.processTouchEvent(ev);
117        return true;
118    }
119
120    /**
121     * Adds a new {@link View} to the list of views that are draggable within the container.
122     * @param dragView the {@link View} to make draggable
123     */
124    public void addDragView(View dragView) {
125        mDragViews.add(dragView);
126    }
127
128    /**
129     * Sets the {@link DragFrameLayoutController} that will receive the drag events.
130     * @param dragFrameLayoutController a {@link DragFrameLayoutController}
131     */
132    public void setDragFrameController(DragFrameLayoutController dragFrameLayoutController) {
133        mDragFrameLayoutController = dragFrameLayoutController;
134    }
135
136    /**
137     * A controller that will receive the drag events.
138     */
139    public interface DragFrameLayoutController {
140
141        public void onDragDrop(boolean captured);
142    }
143}
144