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 
17 package com.example.android.elevationdrag;
18 
19 import android.content.Context;
20 import android.support.v4.widget.ViewDragHelper;
21 import android.util.AttributeSet;
22 import android.view.MotionEvent;
23 import android.view.View;
24 import android.widget.FrameLayout;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * A {@link FrameLayout} that allows the user to drag and reposition child views.
31  */
32 public 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 
DragFrameLayout(Context context)46     public DragFrameLayout(Context context) {
47         this(context, null, 0, 0);
48     }
49 
DragFrameLayout(Context context, AttributeSet attrs)50     public DragFrameLayout(Context context, AttributeSet attrs) {
51         this(context, attrs, 0, 0);
52     }
53 
DragFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)54     public DragFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
55         this(context, attrs, defStyleAttr, 0);
56     }
57 
DragFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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
onInterceptTouchEvent(MotionEvent ev)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
onTouchEvent(MotionEvent ev)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      */
addDragView(View dragView)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      */
setDragFrameController(DragFrameLayoutController dragFrameLayoutController)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 
onDragDrop(boolean captured)141         public void onDragDrop(boolean captured);
142     }
143 }
144