在viewPager裏使用高德地圖

懶加載

因爲viewpager的預加載機制,使得聯網應用會多出內存以及網絡的使用量,同時,在viewpager下使用高德地圖,也會因此出現各種莫名其妙的問題,因此,需要使用懶加載的手段。
實現懶加載,只需繼承fragment類然後重寫與界面顯示相關的方法即可。

public abstract class LazyFragment extends Fragment{
    protected boolean isVisible;

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {//frahment從不可見到完全可見的時候,會調用該方法
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint() && isInit){
            isVisible = true;
            onVisible();
        }else {
            isVisible = false;
            isInit=false;
            onInvisible();
        }
    }

    protected abstract void lazyLoad();//懶加載的方法,在這個方法裏面我們爲Fragment的各個組件去添加數據

    protected void onVisible(){
        lazyLoad();
    }

    protected void onInvisible(){

    }
}

isInit是一個在MapFragment裏定義的bool型變量,用來判斷高德地圖所在的fragment是否已經初始化,如果不添加,那麼點擊viewpager裏的tab跳轉可能會出現空指針錯誤。

高德地圖的具體實現

onCreateView()

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_map, container, false);
        isFirstLoc = 1;
        isInit = true;
        mapView = (MapView) v.findViewById(R.id.map);
        mapView.onCreate(savedInstanceState);// 此方法必須重寫
        screenWidth = ScreenUtils.getScreenWidth(getActivity());
        screenHeight = ScreenUtils.getScreenHeight(getActivity());
        setUpMapIfNeeded();
        return v;
    }

完成地圖空間的初始化以及定位標識符的初始化,防止多次重複定位。高度與寬度用來後面的地圖多標誌聚合。

setUpMapIfNeeded()

private void setUpMapIfNeeded() {
        ((MainActivity) getActivity()).setFabVisible(0);//業務要求,讀者可忽視
        aMap = mapView.getMap();
        uiSettings = aMap.getUiSettings();
        aMap.setLocationSource(this);//設置定位監聽,實現LocationSource的接口
        aMap.setOnMarkerClickListener(this);// 設置點擊marker事件監聽器
        aMap.setOnCameraChangeListener(this);// 對amap添加移動地圖事件監聽器
        //是否顯示定位按鈕
        uiSettings.setMyLocationButtonEnabled(true);
        //設置縮放控件
        uiSettings.setZoomGesturesEnabled(true);
        //顯示比例尺控件
        uiSettings.setScaleControlsEnabled(true);
        uiSettings.setZoomControlsEnabled(true);
        //顯示定位層並可以觸發定位事件
        aMap.setMyLocationEnabled(true);
        //開始定位
        location();
    }

完成地圖一些基本的參數配置。

location()

private void location() {
        Log.d("定位測試", "location方法已經執行");
        //初始化定位
        aMapLocationClient = new AMapLocationClient(getActivity());
        //設置定位回調監聽
        aMapLocationClient.setLocationListener(this);
        //初始化定位參數
        aMapLocationClientOption = new AMapLocationClientOption();
        //設置定位模式爲Hight_Accuracy高精度模式,Battery_Saving爲低功耗模式,Device_Sensors是僅設備模式
        aMapLocationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        //設置是否返回地址信息(默認返回地址信息)
        aMapLocationClientOption.setNeedAddress(true);
        //設置是否只定位一次,默認爲false
        aMapLocationClientOption.setOnceLocation(false);
        //設置是否強制刷新WIFI,默認爲強制刷新
        aMapLocationClientOption.setWifiActiveScan(true);
        //設置是否允許模擬位置,默認爲false,不允許模擬位置
        aMapLocationClientOption.setMockEnable(false);
        //設置定位間隔,單位毫秒,默認爲2000ms
        aMapLocationClientOption.setInterval(10000);
        //給定位客戶端對象設置定位參數
        aMapLocationClient.setLocationOption(aMapLocationClientOption);
        //啓動定位
        aMapLocationClient.startLocation();
    }

配置定位參數並開始定位。

onLocationChanged()

public void onLocationChanged(AMapLocation aMapLocation) {
        //Log.d("定位回掉方法測試",aMapLocation.toString());
        if (aMapLocation != null) {
            if (aMapLocation.getErrorCode() == 0) {
                //可在其中解析amapLocation獲取相應內容。
                lat = aMapLocation.getLatitude();//獲取緯度
                lon = aMapLocation.getLongitude();//獲取經度
                //獲取定位時間
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = new Date(aMapLocation.getTime());
                df.format(date);

                Log.d("縮放標誌位", "定位次數" + isFirstLoc);
                // 如果不設置標誌位,此時再拖動地圖時,它會不斷將地圖移動到當前的位置
                if (isFirstLoc == 1) {
                    //設置縮放級別(縮放級別爲4-20級)
                    isFirstLoc = 0;
                    aMap.moveCamera(CameraUpdateFactory.zoomTo(15));
                    //將地圖移動到定位點
                    aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(lat, lon)));
                    //點擊定位按鈕 能夠將地圖的中心移動到定位點
                    mOnLocationChangedListener.onLocationChanged(aMapLocation);
                }

                if (null == markerLocal) {
                    //只有爲空時才重繪,防止內存泄漏
                    addMarkerToMap(lat, lon);
                }
            } else {
                //定位失敗時,可通過ErrCode(錯誤碼)信息來確定失敗的原因,errInfo是錯誤信息,詳見錯誤碼錶。
                Log.e("地圖錯誤", "定位失敗, 錯誤碼:" + aMapLocation.getErrorCode() + ", 錯誤信息:"
                        + aMapLocation.getErrorInfo());
            }
        }
    }

此方法是完成定位的回調方法,同時也是移動地圖的回調方法。再次方法裏獲取定位的結果並顯示在地圖上。

addMarkerToMap()

//添加標誌到地圖上
    private void addMarkerToMap(Double lat, Double lon) {
        NetWorks.getAroundPosts(lat, lon, new Observer<PostLab>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                Log.e("MapFragment",e.getMessage());
            }

            @Override
            public void onNext(PostLab postLab) {
                Log.e("地圖","成功獲取數據");
                if (markerOptionsListall != null) {
                    //每次重繪前清除數據
                    markerOptionsListall.clear();
                }
                posts = postLab.getmPosts();
                for (Post post :posts) {
                    double tempLat = post.getmLat();
                    double tempLon = post.getmLon();
                    final LatLng mLatLng = new LatLng(tempLat, tempLon);
                    final String mTitle = post.getmTitle();
                    MarkerOptions markerOptions = new MarkerOptions();
                    markerOptions.draggable(true);
                    markerOptions.icon(BitmapDescriptorFactory.defaultMarker());
                    View v = getActivity().getLayoutInflater()
                            .inflate(R.layout.custom_infowindow_content, null);
                    ImageView imageView = (ImageView) v.findViewById(R.id.custom_content_image);
                    TextView textView = (TextView) v.findViewById(R.id.custom_content_title);
                    imageLoader.displayImage(post.getmImageUrl(),
                            imageView, ImageLoaderOptionUtil.getOptions());
                    textView.setText(mTitle);
                    markerOptions.position(mLatLng);
                    //將這個view轉爲bitmap格式
                    markerOptions.icon(BitmapDescriptorFactory.fromView(v));
                    //markerOptions.title(mTitle);
                    markerOptionsListall.add(markerOptions);
                    markerLocal = aMap.addMarker(markerOptions);
                }
            }
        });
    }

本方法的目的是根據經緯度信息將標記添加到地圖上,我是業務需要,從網絡獲取多個經緯度信息然後添加到地圖上。

LazyLoad()

protected void lazyLoad() {
        if (null != aMap) {
            //每次重新加載地圖前,清除數據
            aMap.clear();
            markerLocal = null;
            markerOptionsListall.clear();
            markerOptionsListInView.clear();
        }
        setUpMapIfNeeded();
    }

整體代碼

MapFragment

public class MapFragment extends LazyFragment implements
        AMapLocationListener, LocationSource, AMap.OnMarkerClickListener,
        AMap.OnCameraChangeListener {
    private AMap aMap;
    private MapView mapView;
    private UiSettings uiSettings;
    private AMapLocationClient aMapLocationClient;
    private OnLocationChangedListener mOnLocationChangedListener = null;
    private AMapLocationClientOption aMapLocationClientOption;

    private Double lat; //經度
    private Double lon; //緯度
    private int isFirstLoc;//定位標誌位
    public static boolean isInit = false;//控件初始化標誌位
    private Marker markerLocal;
    private int screenHeight;// 屏幕高度(px)
    private int screenWidth;// 屏幕寬度(px)
    //視野內的marker
    private ArrayList<MarkerOptions> markerOptionsListInView = new ArrayList<>();
    //所有的marker
    private ArrayList<MarkerOptions> markerOptionsListall = new ArrayList<>();
    //markerOptionsListall初始化標誌位
    private int isListallInit = 0;
    //接收到的post集合
    private List<Post> posts;
    //獲取imageloader實例
    private final ImageLoader imageLoader = ImageLoader.getInstance();

    public MapFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_map, container, false);
        isFirstLoc = 1;
        isInit = true;
        mapView = (MapView) v.findViewById(R.id.map);
        mapView.onCreate(savedInstanceState);// 此方法必須重寫
        screenWidth = ScreenUtils.getScreenWidth(getActivity());
        screenHeight = ScreenUtils.getScreenHeight(getActivity());
        setUpMapIfNeeded();
        return v;
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
        deactivate();
    }


    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //mapView.onDestroy();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mapView.onDestroy();
    }

    private void setUpMapIfNeeded() {
        ((MainActivity) getActivity()).setFabVisible(0);
        aMap = mapView.getMap();
        uiSettings = aMap.getUiSettings();
        aMap.setLocationSource(this);//設置定位監聽,實現LocationSource的接口
        aMap.setOnMarkerClickListener(this);// 設置點擊marker事件監聽器
        aMap.setOnCameraChangeListener(this);// 對amap添加移動地圖事件監聽器
        //是否顯示定位按鈕
        uiSettings.setMyLocationButtonEnabled(true);
        //設置縮放控件
        uiSettings.setZoomGesturesEnabled(true);
        //顯示比例尺控件
        uiSettings.setScaleControlsEnabled(true);
        uiSettings.setZoomControlsEnabled(true);
        //顯示定位層並可以觸發定位事件
        aMap.setMyLocationEnabled(true);
        //開始定位
        location();
    }

    private void location() {
        Log.d("定位測試", "location方法已經執行");
        //初始化定位
        aMapLocationClient = new AMapLocationClient(getActivity());
        //設置定位回調監聽
        aMapLocationClient.setLocationListener(this);
        //初始化定位參數
        aMapLocationClientOption = new AMapLocationClientOption();
        //設置定位模式爲Hight_Accuracy高精度模式,Battery_Saving爲低功耗模式,Device_Sensors是僅設備模式
        aMapLocationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        //設置是否返回地址信息(默認返回地址信息)
        aMapLocationClientOption.setNeedAddress(true);
        //設置是否只定位一次,默認爲false
        aMapLocationClientOption.setOnceLocation(false);
        //設置是否強制刷新WIFI,默認爲強制刷新
        aMapLocationClientOption.setWifiActiveScan(true);
        //設置是否允許模擬位置,默認爲false,不允許模擬位置
        aMapLocationClientOption.setMockEnable(false);
        //設置定位間隔,單位毫秒,默認爲2000ms
        aMapLocationClientOption.setInterval(10000);
        //給定位客戶端對象設置定位參數
        aMapLocationClient.setLocationOption(aMapLocationClientOption);
        //啓動定位
        aMapLocationClient.startLocation();
    }


    @Override
    public void onLocationChanged(AMapLocation aMapLocation) {
        //Log.d("定位回掉方法測試",aMapLocation.toString());
        if (aMapLocation != null) {
            if (aMapLocation.getErrorCode() == 0) {
                //可在其中解析amapLocation獲取相應內容。
                lat = aMapLocation.getLatitude();//獲取緯度
                lon = aMapLocation.getLongitude();//獲取經度
                //獲取定位時間
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date date = new Date(aMapLocation.getTime());
                df.format(date);

                Log.d("縮放標誌位", "定位次數" + isFirstLoc);
                // 如果不設置標誌位,此時再拖動地圖時,它會不斷將地圖移動到當前的位置
                if (isFirstLoc == 1) {
                    //設置縮放級別(縮放級別爲4-20級)
                    isFirstLoc = 0;
                    aMap.moveCamera(CameraUpdateFactory.zoomTo(15));
                    //將地圖移動到定位點
                    aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(lat, lon)));
                    //點擊定位按鈕 能夠將地圖的中心移動到定位點
                    mOnLocationChangedListener.onLocationChanged(aMapLocation);
                }

                if (null == markerLocal) {
                    //只有爲空時才重繪,防止內存泄漏
                    addMarkerToMap(lat, lon);
                }
            } else {
                //定位失敗時,可通過ErrCode(錯誤碼)信息來確定失敗的原因,errInfo是錯誤信息,詳見錯誤碼錶。
                Log.e("地圖錯誤", "定位失敗, 錯誤碼:" + aMapLocation.getErrorCode() + ", 錯誤信息:"
                        + aMapLocation.getErrorInfo());
            }
        }
    }

    //添加標誌到地圖上
    private void addMarkerToMap(Double lat, Double lon) {
        NetWorks.getAroundPosts(lat, lon, new Observer<PostLab>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                Log.e("MapFragment",e.getMessage());
            }

            @Override
            public void onNext(PostLab postLab) {
                Log.e("地圖","成功獲取數據");
                if (markerOptionsListall != null) {
                    //每次重繪前清除數據
                    markerOptionsListall.clear();
                }
                posts = postLab.getmPosts();
                for (Post post :posts) {
                    double tempLat = post.getmLat();
                    double tempLon = post.getmLon();
                    final LatLng mLatLng = new LatLng(tempLat, tempLon);
                    final String mTitle = post.getmTitle();
                    MarkerOptions markerOptions = new MarkerOptions();
                    markerOptions.draggable(true);
                    markerOptions.icon(BitmapDescriptorFactory.defaultMarker());
                    View v = getActivity().getLayoutInflater()
                            .inflate(R.layout.custom_infowindow_content, null);
                    ImageView imageView = (ImageView) v.findViewById(R.id.custom_content_image);
                    TextView textView = (TextView) v.findViewById(R.id.custom_content_title);
                    imageLoader.displayImage(post.getmImageUrl(),
                            imageView, ImageLoaderOptionUtil.getOptions());
                    textView.setText(mTitle);
                    markerOptions.position(mLatLng);
                    markerOptions.icon(BitmapDescriptorFactory.fromView(v));
                    //markerOptions.title(mTitle);
                    markerOptionsListall.add(markerOptions);
                    markerLocal = aMap.addMarker(markerOptions);
                }
            }
        });
    }

    /**
     * 獲取視野內的marker 根據聚合算法合成自定義的marker 顯示視野內的marker
     */
    private void resetMarks() {

        // 開始刷新
        Projection projection = aMap.getProjection();
        Point p = null;
        markerOptionsListInView.clear();
        // 獲取在當前視野內的marker;提高效率
        for (MarkerOptions mp : markerOptionsListall) {
            p = projection.toScreenLocation(mp.getPosition());
            if (p.x < 0 || p.y < 0 || p.x > screenWidth || p.y > screenHeight) {
                // 不添加到計算的列表中
            } else {
                markerOptionsListInView.add(mp);
            }
        }
        // 自定義的聚合類MyMarkerCluster
        ArrayList<MarkerImageView> clustersMarker = new ArrayList<>();
        for (MarkerOptions mp : markerOptionsListInView) {
            if (clustersMarker.size() == 0) {
                // 添加一個新的自定義marker
                clustersMarker.add(new MarkerImageView(
                        getActivity(), mp, projection, 80));// 80=相距多少才聚合
            } else {
                boolean isIn = false;
                for (MarkerImageView cluster : clustersMarker) {
                    // 判斷當前的marker是否在前面marker的聚合範圍內 並且每個marker只會聚合一次。
                    if (cluster.getBounds().contains(mp.getPosition())) {
                        cluster.addMarker(mp);
                        isIn = true;
                        break;
                    }
                }
                // 如果沒在任何範圍內,自己單獨形成一個自定義marker。在和後面的marker進行比較
                if (!isIn) {
                    clustersMarker.add(new MarkerImageView(
                            getActivity(), mp, projection, 80));// 80=相距多少才聚合
                }
            }
        }
        // 設置聚合點的位置和icon
        for (MarkerImageView mmc : clustersMarker) {
            mmc.setpositionAndIcon();
        }
        aMap.clear();
        // 重新添加 marker
        for (MarkerImageView cluster : clustersMarker) {
            aMap.addMarker(cluster.getOptions());
        }
    }

    @Override
    protected void lazyLoad() {
        if (null != aMap) {
            //每次重新加載地圖前,清除數據
            aMap.clear();
            markerLocal = null;
            markerOptionsListall.clear();
            markerOptionsListInView.clear();
        }
        setUpMapIfNeeded();
    }


    @Override
    public void activate(OnLocationChangedListener onLocationChangedListener) {
        mOnLocationChangedListener = onLocationChangedListener;
    }

    @Override
    public void deactivate() {
        mOnLocationChangedListener = null;
    }

    //marker點擊事件
    @Override
    public boolean onMarkerClick(Marker marker) {
        marker.hideInfoWindow();
        LatLng latLng = marker.getPosition();
        return false;
    }


    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
    }

    @Override
    public void onCameraChangeFinish(CameraPosition cameraPosition) {
        if (markerOptionsListall.size() != 0) {
            resetMarks();
        }
    }

}

MarkerImageView

聚合標誌工具類

public class MarkerImageView {
    //上下文
    private Context context;
    //marker類
    private MarkerOptions options;
    //當前可觀區域裏的 聚合過之後的集合
    private ArrayList<MarkerOptions> includeMarkers = new ArrayList<>();
    // 創建區域
    private LatLngBounds bounds;

    private int count = 0;
    /**
     * 頭像加載完監聽
     */
    public MarkerImageView(Context context, MarkerOptions firstMarkers,
                           Projection projection, int gridSize) {
        this.context = context;
        options = new MarkerOptions();

        Point point = projection.toScreenLocation(firstMarkers.getPosition());
        //範圍類
        Point southwestPoint = new Point(point.x - gridSize, point.y + gridSize);
        //範圍類
        Point northeastPoint = new Point(point.x + gridSize, point.y - gridSize);

        bounds = new LatLngBounds(projection.fromScreenLocation(southwestPoint),
                projection.fromScreenLocation(northeastPoint));
        //設置初始化marker屬性
        options.anchor(0.5f, 0.5f).title(firstMarkers.getTitle()).position(firstMarkers.getPosition())
                .icon(firstMarkers.getIcon())
        .snippet(firstMarkers.getSnippet());
        includeMarkers.add(firstMarkers);
        Log.d("計數",""+count++);


    }


    public MarkerImageView(Context context) {
        this.context = context;
    }

    public LatLngBounds getBounds() {
        return bounds;
    }

    public MarkerOptions getOptions() {
        return options;
    }

    public void setOptions(MarkerOptions options) {
        this.options = options;
    }



    /**
     * 添加marker
     */
    public void addMarker(MarkerOptions markerOptions) {
        includeMarkers.add(markerOptions);// 添加到列表中
    }

    /**
     * 設置聚合點的中心位置以及圖標
     */
    public void setpositionAndIcon() {
        int size = includeMarkers.size();
        double lat = 0.0;
        double lng = 0.0;
        // 一個的時候
        if (size == 1) {//設置marker單個屬性
            // 設置marker位置
            Log.d("單個marker", "被使用");
            options.position(new LatLng(
                    includeMarkers.get(0).getPosition().latitude,
                    includeMarkers.get(0).getPosition().longitude));
            options.title("聚合點");
            options.icon(BitmapDescriptorFactory
                    .fromBitmap(getViewBitmap(getView(size))));

        } else {// 聚合的時候
            //設置marker聚合屬性
            Log.d("多個marker", includeMarkers.size()+"個被使用");
            for (MarkerOptions op : includeMarkers) {
                lat += op.getPosition().latitude;
                lng += op.getPosition().longitude;
            }
            // 設置marker的位置爲中心位置爲聚集點的平均位置
            options.position(new LatLng(lat / size, lng / size));
            options.title("聚合點");
            options.icon(BitmapDescriptorFactory
                    .fromBitmap(getViewBitmap(getView(size))));
        }
    }

    /**
     * marker視圖
     */
    public View getView(int num) {
        View view = LayoutInflater.from(context).inflate(
                R.layout.view_gaode_img, null);
        /** 數量 */
        TextView txt_num = (TextView) view
                .findViewById(R.id.view_gaode_txt_num);
        /** 頭像 */
        ImageView img_portrait = (ImageView) view
                .findViewById(R.id.view_gaode_img_portrait);

        img_portrait.setPadding(8, 8, 8, 12);
        if (num > 1) {
            txt_num.setText(num + "");
        } else if (num == 1) {
            //在此處修改未聚合時候的顯示方式
            txt_num.setText(num + "");
            img_portrait.setBackgroundResource(R.drawable.green_pin);
            //overDraw();
        }
        return view;
    }

    /**
     * 把一個view轉化成bitmap對象
     */
    public static Bitmap getViewBitmap(View view) {
        Bitmap bitmap = null;
        try {
            if (view != null) {
                view.measure(
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                view.layout(0, 0, view.getMeasuredWidth(),
                        view.getMeasuredHeight());
                view.buildDrawingCache();
                bitmap = view.getDrawingCache();
            }
        } catch (Exception e) {
        }

        return bitmap;
    }
}

值得注意的一點是,在for循環中向markerOptionsListall中添加markerOptions時,一定要每次循環都要聲明並定義markOptions,因爲類當參數傳遞時是直接使用的引用,如果不這樣做,每次循環都會重用markOptions對象,導致一些莫名其妙的錯誤。
多點聚合主要參考這位大神的博客:這兒

發佈了40 篇原創文章 · 獲贊 10 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章