├── LICENSE.md ├── MapStateListener.java ├── README.md ├── TouchableMapFragment.java └── TouchableWrapper.java /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mads Bjerg Frandsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MapStateListener.java: -------------------------------------------------------------------------------- 1 | package dk.composed.mapstate; 2 | 3 | import android.app.Activity; 4 | 5 | import com.google.android.gms.maps.GoogleMap; 6 | import com.google.android.gms.maps.model.CameraPosition; 7 | 8 | import java.util.Timer; 9 | import java.util.TimerTask; 10 | 11 | public abstract class MapStateListener { 12 | 13 | private boolean mMapTouched = false; 14 | private boolean mMapSettled = false; 15 | private Timer mTimer; 16 | private static final int SETTLE_TIME = 500; 17 | 18 | private GoogleMap mMap; 19 | private CameraPosition mLastPosition; 20 | private Activity mActivity; 21 | 22 | public MapStateListener(GoogleMap map, TouchableMapFragment touchableMapFragment, Activity activity) { 23 | this.mMap = map; 24 | this.mActivity = activity; 25 | 26 | map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() { 27 | @Override 28 | public void onCameraChange(CameraPosition cameraPosition) { 29 | unsettleMap(); 30 | if(!mMapTouched) { 31 | runSettleTimer(); 32 | } 33 | } 34 | }); 35 | 36 | touchableMapFragment.setTouchListener(new TouchableWrapper.OnTouchListener() { 37 | @Override 38 | public void onTouch() { 39 | touchMap(); 40 | unsettleMap(); 41 | } 42 | 43 | @Override 44 | public void onRelease() { 45 | releaseMap(); 46 | runSettleTimer(); 47 | } 48 | }); 49 | } 50 | 51 | private void updateLastPosition() { 52 | mActivity.runOnUiThread(new Runnable() { 53 | @Override 54 | public void run() { 55 | mLastPosition = MapStateListener.this.mMap.getCameraPosition(); 56 | } 57 | }); 58 | } 59 | 60 | private void runSettleTimer() { 61 | updateLastPosition(); 62 | 63 | if(mTimer != null) { 64 | mTimer.cancel(); 65 | mTimer.purge(); 66 | } 67 | mTimer = new Timer(); 68 | mTimer.schedule(new TimerTask() { 69 | @Override 70 | public void run() { 71 | mActivity.runOnUiThread(new Runnable() { 72 | @Override 73 | public void run() { 74 | CameraPosition currentPosition = MapStateListener.this.mMap.getCameraPosition(); 75 | if (currentPosition.equals(mLastPosition)) { 76 | settleMap(); 77 | } 78 | } 79 | }); 80 | } 81 | }, SETTLE_TIME); 82 | } 83 | 84 | private synchronized void releaseMap() { 85 | if(mMapTouched) { 86 | mMapTouched = false; 87 | onMapReleased(); 88 | } 89 | } 90 | 91 | private void touchMap() { 92 | if(!mMapTouched) { 93 | if(mTimer != null) { 94 | mTimer.cancel(); 95 | mTimer.purge(); 96 | } 97 | mMapTouched = true; 98 | onMapTouched(); 99 | } 100 | } 101 | 102 | public void unsettleMap() { 103 | if(mMapSettled) { 104 | if(mTimer != null) { 105 | mTimer.cancel(); 106 | mTimer.purge(); 107 | } 108 | mMapSettled = false; 109 | mLastPosition = null; 110 | onMapUnsettled(); 111 | } 112 | } 113 | 114 | public void settleMap() { 115 | if(!mMapSettled) { 116 | mMapSettled = true; 117 | onMapSettled(); 118 | } 119 | } 120 | 121 | public abstract void onMapTouched(); 122 | public abstract void onMapReleased(); 123 | public abstract void onMapUnsettled(); 124 | public abstract void onMapSettled(); 125 | } 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Obsolete! Use the new Google Maps API instead: https://developers.google.com/maps/documentation/android-api/events#camera-change-events 2 | 3 | MapStateListener 4 | ================ 5 | 6 | The MapStateListener can be used to receive callbacks from GoogleMap on Android, when the map settles or unsettles, is touched or released. Settling the maps means, that the map is not scrolling, zooming or animating in any way and is not touched at the same time. 7 | 8 | How to use it 9 | ================ 10 | You can use the MapStateListener by replacing your MapFragment with the included TouchableMapFragment, which allows for receiving touch events from the Map. 11 | 12 | Add fragment in the activity layout. 13 | 14 | ```xml 15 | 22 | ``` 23 | 24 | Then in the onCreate() method of activity 25 | 26 | ```java 27 | // Obtain the TouchableMapFragment and get notified when the map is ready to be used. 28 | mapFragment = (TouchableMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); 29 | mapFragment.getMapAsync(this); 30 | ``` 31 | 32 | Then in onMapReady() method implement your MapStateListener 33 | 34 | With a reference to your TouchableMapFragment and the GoogleMap-object, in your Activity, you can simply use the MapStateListener like this: 35 | 36 | ```java 37 | new MapStateListener(mMap, mMapFragment, this) { 38 | @Override 39 | public void onMapTouched() { 40 | // Map touched 41 | } 42 | 43 | @Override 44 | public void onMapReleased() { 45 | // Map released 46 | } 47 | 48 | @Override 49 | public void onMapUnsettled() { 50 | // Map unsettled 51 | } 52 | 53 | @Override 54 | public void onMapSettled() { 55 | // Map settled 56 | } 57 | }; 58 | ``` 59 | -------------------------------------------------------------------------------- /TouchableMapFragment.java: -------------------------------------------------------------------------------- 1 | package dk.composed.mapstate; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import com.google.android.gms.maps.SupportMapFragment; 9 | 10 | public class TouchableMapFragment extends SupportMapFragment { 11 | 12 | private View mOriginalContentView; 13 | private TouchableWrapper mTouchView; 14 | 15 | public void setTouchListener(TouchableWrapper.OnTouchListener onTouchListener) { 16 | mTouchView.setTouchListener(onTouchListener); 17 | } 18 | 19 | @Override 20 | public View onCreateView(LayoutInflater inflater, ViewGroup parent, 21 | Bundle savedInstanceState) { 22 | mOriginalContentView = super.onCreateView(inflater, parent, 23 | savedInstanceState); 24 | 25 | mTouchView = new TouchableWrapper(getActivity()); 26 | mTouchView.addView(mOriginalContentView); 27 | 28 | return mTouchView; 29 | } 30 | 31 | @Override 32 | public View getView() { 33 | return mOriginalContentView; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /TouchableWrapper.java: -------------------------------------------------------------------------------- 1 | package dk.composed.mapstate; 2 | 3 | import android.content.Context; 4 | import android.view.MotionEvent; 5 | import android.widget.FrameLayout; 6 | 7 | public class TouchableWrapper extends FrameLayout { 8 | 9 | public TouchableWrapper(Context context) { 10 | super(context); 11 | } 12 | 13 | public void setTouchListener(OnTouchListener onTouchListener) { 14 | this.onTouchListener = onTouchListener; 15 | } 16 | 17 | private OnTouchListener onTouchListener; 18 | 19 | @Override 20 | public boolean dispatchTouchEvent(MotionEvent event) { 21 | 22 | switch (event.getAction()) { 23 | case MotionEvent.ACTION_DOWN: 24 | onTouchListener.onTouch(); 25 | break; 26 | case MotionEvent.ACTION_UP: 27 | onTouchListener.onRelease(); 28 | break; 29 | } 30 | 31 | return super.dispatchTouchEvent(event); 32 | } 33 | 34 | public interface OnTouchListener { 35 | public void onTouch(); 36 | public void onRelease(); 37 | } 38 | } 39 | --------------------------------------------------------------------------------