高德地圖和google地圖適配

Android組件化架構(gòu)

以下是我這個系列的相關(guān)文章,有興趣可以參考一下,可以給個喜歡或者關(guān)注我的文章。

[Android]如何做一個崩潰率少于千分之三噶應(yīng)用app--章節(jié)列表

今天介紹的是大型app必備模塊-地圖模塊。
當(dāng)今世界最大的地圖sdk應(yīng)該是google地圖,但是由于國內(nèi)墻掉了google play service,國內(nèi)是無法使用google地圖的,然而國內(nèi)比較熱門的地圖sdk是高德地圖和百度地圖。(如果你是IOS,還有自帶的地圖)

近來項目中需要世界地圖,所以特此做了一個高德地圖和google地圖兼容的模塊了。

Sdk接入

1.google地圖,接入相對比較簡單,當(dāng)然因為Android本身就是google親兒子的原因。
需要引入google service的sdk,以及google map的sdk
https://developers.google.com/places/android-api/start,獲取賬號需要gmail郵箱作為管理

2.高德地圖接入相對比較復(fù)雜一點,可以選擇2d,3d,定位,搜索多種模塊去接入地圖。
然后需要申請賬號,隨便郵箱手機(jī)號就可以了,通過keytools命令提出keystore的sha1值,包名和
sha1值相互綁定的,每次請求都會驗證。
然后配置AndroidManifest中的meta-data。

預(yù)覽模塊

地圖選擇UI

1.高德地圖是通過sdk提供的com.amap.api.maps2d.MapView自定義地圖View來實現(xiàn)的。
google地圖是通過sdk提供一個Fragment空間來實現(xiàn)地圖獲取

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/google_map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

mapFragment = childFragmentManager.findFragmentById(R.id.google_map) as SupportMapFragment

2.地圖預(yù)覽


地圖預(yù)覽(不小心透露了我的工作地址).png

Google地圖和高德地圖接口相關(guān)的名字都是差不多的,比較常用的接口
moveCamera 視窗轉(zhuǎn)移 縮放級別分為1~17級,數(shù)值越大地圖越精準(zhǔn)
addMarker 添加地圖標(biāo)簽

google地圖是使用getMapAysnc,會有onMapReady的接口回調(diào)

        mapFragment?.getMapAsync(this)

    /**
     * 地圖就緒
     */
    override fun onMapReady(googleMap: GoogleMap?) {
        googleMap ?: return
        with(googleMap) {
            val latLng = LatLng(latitude, longitude)
            //視覺轉(zhuǎn)移
            moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16f))
            //添加坐標(biāo)
            addMarker(MarkerOptions().position(latLng))
        }
    }

高德地圖使用setOnMapLoadedListener方法來設(shè)置回調(diào)

aMap?.setOnMapLoadedListener {
            //視覺移動
            aMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(latitude, longitude), 100f))
            //添加坐標(biāo)
            aMap?.addMarker(MarkerOptions().anchor(0.5f, 0.5f)
                    .icon(BitmapDescriptorFactory
                            .fromBitmap(BitmapFactory.decodeResource(
                                    resources, R.drawable.common_drag_location)))
                    .position(LatLng(latitude, longitude)))
        }

如果不想地圖的坐標(biāo)和視覺點顯示居中怎么辦?
需要將布局中margin上著手,如果想要往上移,就需要將marginTop設(shè)置為負(fù)值,這樣地圖中心點就會上移動,并且視覺點也會和中心點一樣上移。

定位模塊

定位預(yù)覽圖.png

1.高德提供了AMapLocationListener作為專為提供高德坐標(biāo)的監(jiān)聽

      private fun setUpMap() {
        myLocationStyle = MyLocationStyle()
        myLocationStyle?.strokeColor(Color.argb(0, 0, 0, 0))// 設(shè)置圓形的邊框顏色
        myLocationStyle?.radiusFillColor(Color.argb(0, 0, 0, 0))// 設(shè)置圓形的填充顏色
        myLocationStyle?.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.common_self_location))  //顯示自身定位坐標(biāo)
        aMap?.setMyLocationStyle(myLocationStyle)
        aMap?.isMyLocationEnabled = true// 設(shè)置為true表示顯示定位層并可觸發(fā)定位,false表示隱藏定位層并不可觸發(fā)定位,默認(rèn)是false
        val uriSettings = aMap?.uiSettings
        uriSettings?.isZoomControlsEnabled = false //關(guān)掉縮放鍵
    }


    private fun initLoc() {
        //初始化定位
        mLocationClient = AMapLocationClient(context!!.applicationContext)
        //設(shè)置定位回調(diào)監(jiān)聽
        mLocationClient?.setLocationListener(this)
        //初始化定位參數(shù)
        mLocationOption = AMapLocationClientOption()
        //設(shè)置定位模式為高精度模式,Battery_Saving為低功耗模式,Device_Sensors是僅設(shè)備模式
        mLocationOption?.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
        //設(shè)置是否返回地址信息(默認(rèn)返回地址信息)
        mLocationOption?.isNeedAddress = true
        //設(shè)置是否只定位一次,默認(rèn)為false
        mLocationOption?.isOnceLocation = false
        //設(shè)置是否強(qiáng)制刷新WIFI,默認(rèn)為強(qiáng)制刷新
        mLocationOption?.isWifiActiveScan = false
        //設(shè)置是否允許模擬位置,默認(rèn)為false,不允許模擬位置
        mLocationOption?.isMockEnable = false
        //設(shè)置定位間隔,單位毫秒,默認(rèn)為3000ms
        mLocationOption?.interval = (3000)
        //給定位客戶端對象設(shè)置定位參數(shù)
        mLocationClient?.setLocationOption(mLocationOption)
        //啟動定位
        mLocationClient?.startLocation()
    }

 override fun onLocationChanged(amapLocation: AMapLocation?) {
       //監(jiān)聽實時定位
    }

google的定位是使用Android原生的Location定位

locationManager = context!!.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationManager?.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 10f, this)
            locationManager?.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 3000, 10f, this)
   
 /**
  *地圖就緒
   */
    override fun onMapReady(googleMap: GoogleMap?) {
        googleMap ?: return
        this.googleMap = googleMap
        with(googleMap.uiSettings) {
            isZoomGesturesEnabled = true
            isMyLocationButtonEnabled = true
            isScrollGesturesEnabled = true
        }
        try {
            googleMap.isMyLocationEnabled = true
        } catch (e: SecurityException) {
            ALog.e(TAG, e)
        }
   }

   /**
     * 定位更新
     */
    override fun onLocationChanged(location: Location?) {
      
    }

搜索模塊

搜索結(jié)果.png

1.google 搜索有兩種方式,一種是通過webapi來搜索出附近相關(guān)的地點(這里使用了RxVolley的框架),這個的好處關(guān)聯(lián)結(jié)果比較多。
這里不用Uri.Builder的拼接方式是因為其指定了Utf-8的格式轉(zhuǎn)換將會出現(xiàn)“,”強(qiáng)轉(zhuǎn)為“%”號

    val googlePlaceUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
    fun getGoogleNearByPlaces(latitude: Double, longitude: Double, radius: Int): Observable<GoogleLocation> {
        val builder = StringBuilder(googlePlaceUrl)
        builder.append("?location=").append(latitude.toString()).append(",").append(longitude.toString())
        builder.append("&radius=").append(radius.toString())
        builder.append("&key=").append(googlePlaceKey)
        return RxVolley.get<GoogleLocation>(builder.toString(), null, object : TypeToken<GoogleLocation>() {}.type)
    }

第二種是文字關(guān)聯(lián)搜索(地點自動完成),google提供了一個自定義的Fragment,但是如果你有高級定制,不用AutoCompleteTextView,那就需要通過定義一個Adapter來獲取相關(guān)內(nèi)容。(搜索結(jié)果比較少)
這邊是使用了需要高級定義搜索,所以使用了Adapter的形式。

    override fun doSearch(key: String, city: String?) {
        Observable.create(ObservableOnSubscribe<ArrayList<AutocompletePrediction>> {
            it.onNext(getAutocomplete(key)!!) //需要在次線程
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    searchAdpater?.clearData()
                    for (item in it) {
                        val placeResult = mGeoDataClient!!.getPlaceById(item.placeId) 
                        placeResult.addOnCompleteListener(mUpdatePlaceDetailsCallback)  //異步訪問單個placeId的詳細(xì)信息
                    }
                }, {
                    ALog.e(TAG, it)
                })
    }

    /**
     * 異步訪問單個placeId的詳細(xì)信息
     */
    val mUpdatePlaceDetailsCallback = object : OnCompleteListener<PlaceBufferResponse> {
        override fun onComplete(task: Task<PlaceBufferResponse>) {
            try {
                val place = task.result.get(0)
                searchAdpater?.addData(LocationItem(false, place.latLng.latitude, place.latLng.longitude, place.name.toString(), place.address.toString()))
                ALog.i(TAG, "Place details received: " + place.name)
                task.result.release()
            } catch (e: RuntimeRemoteException) {
                ALog.e(TAG, e)
            }
        }
    }

private fun getAutocomplete(constraint: CharSequence): ArrayList<AutocompletePrediction>? {
        ALog.d(TAG, "Starting autocomplete query for: " + constraint)

        // Submit the query to the autocomplete API and retrieve a PendingResult that will
        // contain the results when the query completes.
        val results = mGeoDataClient?.getAutocompletePredictions(constraint.toString(), null,
                null)

        // This method should have been called off the main UI thread. Block and wait for at most
        // 60s for a result from the API.
        //收集文字關(guān)聯(lián)預(yù)測結(jié)果
        try {
            Tasks.await<AutocompletePredictionBufferResponse>(results!!, 2, TimeUnit.SECONDS)
        } catch (e: ExecutionException) {
            e.printStackTrace()
        } catch (e: InterruptedException) {
            e.printStackTrace()
        } catch (e: TimeoutException) {
            e.printStackTrace()
        }

        try {
            val autocompletePredictions = results!!.result

            ALog.d(TAG, "Query completed. Received " + autocompletePredictions.count
                    + " predictions.")

            // Freeze the results immutable representation that can be stored safely.
            return DataBufferUtils.freezeAndClose<AutocompletePrediction, AutocompletePrediction>(autocompletePredictions)
        } catch (e: RuntimeExecutionException) {
            // If the query did not complete successfully return null
            Toast.makeText(context, "Error contacting API: " + e.toString(),
                    Toast.LENGTH_SHORT).show()
            ALog.e(TAG, "Error getting autocomplete prediction API call", e)
            return null
        }

    }

2.高德地圖中的PoiSearch是支持通過關(guān)鍵字搜索和經(jīng)緯度地址附近搜索。

    /**
     * 經(jīng)緯度搜索
     */
    fun doSearchQuery(city: String, latitude: Double, longtitude: Double) {
        query = PoiSearch.Query("", "", city) // 第一個參數(shù)表示搜索字符串,第二個參數(shù)表示poi搜索類型,第三個參數(shù)表示poi搜索區(qū)域(空字符串代表全國)
        query?.pageSize = 20 // 設(shè)置每頁最多返回多少條poiitem
        query?.pageNum = 1  // 設(shè)置查第一頁
        val poiSearch = PoiSearch(context, query)
        poiSearch.setOnPoiSearchListener(onPoiSearchListener)
        // 設(shè)置搜索區(qū)域為以lp點為圓心,其周圍5000米范圍
        poiSearch.bound = PoiSearch.SearchBound(LatLonPoint(latitude, longtitude), 1000, true)
        poiSearch.searchPOIAsyn()  // 異步搜索
    }

    /**
     * 關(guān)鍵字搜索
     */
    fun doSearchQuery(keyWord: String, city: String?) {
        if (city != null)
            query = PoiSearch.Query(keyWord, "", city)
        else
            query = PoiSearch.Query(keyWord, "", "")
        query?.pageSize = 20 // 設(shè)置每頁最多返回多少條poiitem
        query?.pageNum = 1  // 設(shè)置查第一頁

        val poiSearch = PoiSearch(context!!, query)
        poiSearch.setOnPoiSearchListener(onSearchListener)
        poiSearch.searchPOIAsyn()  // 異步搜索
    }

當(dāng)然也支持異步返回

    /**
     * 搜索結(jié)果
     */
    val onSearchListener = object : PoiSearch.OnPoiSearchListener {
        override fun onPoiSearched(result: PoiResult?, rCode: Int) {
            if (rCode == 1000) {
                if (result?.query!! == query) {
                    //返回結(jié)果列表
                    result.pois 
                }
            }
        }

        override fun onPoiItemSearched(p0: PoiItem?, p1: Int) {
               //返回再搜索
        }
    }

地圖縮略圖獲取

地圖預(yù)覽

1.高德地圖和google地圖都需要使用web api來獲取縮略圖

        var builder: StringBuilder? = null
        if (type == GDMAP) {
            builder = StringBuilder(gdImgUrl)
            builder.append("?location=").append(longitude).append(",").append(latitude)
            builder.append("&zoom=").append(zoom)
            builder.append("&size=").append(dpToPx(context, width)).append("*").append(dpToPx(context, height))
            builder.append("&markers=").append("mid").append(",").append(",A:").append(longitude).append(",").append(latitude)
            builder.append("&key=").append(gdMapKey)
        } else if (type == GOOGLEMAP) {
            builder = StringBuilder(googleImgeUrl)
            builder.append("?center=").append(latitude).append(",").append(longitude)
            builder.append("&zoom=").append(zoom)
            builder.append("&size=").append(dpToPx(context, width)).append("x").append(dpToPx(context, height))
            builder.append("&markers=").append(latitude).append(",").append(longitude)
            builder.append("&key=").append(googlePlaceKey)
        }

        return builder.toString()

這里需要注意的是

1.高德size的拼接,是用*號,而google size的拼接是使用“x”。
2.google需要使用的是placeKey,不是mapkey。
3.兩種地圖的縮略圖都不支持自定義標(biāo)記(marker)
4.高德地圖無法顯示到國外google的地址的詳細(xì)信息
[高德縮略圖說明](http://lbs.amap.com/api/webservice/guide/api/staticmaps/
google縮略圖說明

特殊轉(zhuǎn)換
1.高德地圖使用的是高德坐標(biāo),并不是標(biāo)準(zhǔn)的的GPS坐標(biāo)。而高德只提供了其他地圖和GPS轉(zhuǎn)高德坐標(biāo),并不沒有提供高德轉(zhuǎn)為其他坐標(biāo)。Google使用的標(biāo)準(zhǔn)的GPS坐標(biāo),那么如果需要坐標(biāo)互通就需要相互轉(zhuǎn)換了,這里提供了坐標(biāo)轉(zhuǎn)換相互轉(zhuǎn)換的方式。

object GDConverter {
    fun fromGpsToLatLng(context: Context, latitude: Double, longitude: Double): LatLng? {

        val converter = CoordinateConverter(context)
        converter.from(CoordinateConverter.CoordType.GPS)
        try {
            converter.coord(DPoint(latitude, longitude))
            val desLatLng = converter.convert()
            return LatLng(desLatLng.latitude, desLatLng.longitude)
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return null
    }

    /**
     * GPS坐標(biāo)轉(zhuǎn)換成高德
     */
    fun toGDLatLng(latitude: Double, longitude: Double): LatLng {
        val latLng = LatLng(latitude, longitude)
        val converter = com.amap.api.maps2d.CoordinateConverter()
        converter.from(com.amap.api.maps2d.CoordinateConverter.CoordType.GPS)
        converter.coord(latLng)
        return converter.convert()
    }

    //圓周率 GCJ_02_To_WGS_84
    var PI = 3.14159265358979324

    /**
     * 方法描述:方法可以將高德地圖SDK獲取到的GPS經(jīng)緯度轉(zhuǎn)換為真實的經(jīng)緯度,可以用于解決安卓系統(tǒng)使用高德SDK獲取經(jīng)緯度的轉(zhuǎn)換問題。
     * @param 需要轉(zhuǎn)換的經(jīng)緯度
     * @return 轉(zhuǎn)換為真實GPS坐標(biāo)后的經(jīng)緯度
     * @throws <異常類型> {@inheritDoc} 異常描述
    </異常類型> */
    fun delta(lat: Double, lon: Double): HashMap<String, Double> {
        val a = 6378245.0//克拉索夫斯基橢球參數(shù)長半軸a
        val ee = 0.00669342162296594323//克拉索夫斯基橢球參數(shù)第一偏心率平方
        var dLat = this.transformLat(lon - 105.0, lat - 35.0)
        var dLon = this.transformLon(lon - 105.0, lat - 35.0)
        val radLat = lat / 180.0 * this.PI
        var magic = Math.sin(radLat)
        magic = 1 - ee * magic * magic
        val sqrtMagic = Math.sqrt(magic)
        dLat = dLat * 180.0 / (a * (1 - ee) / (magic * sqrtMagic) * this.PI)
        dLon = dLon * 180.0 / (a / sqrtMagic * Math.cos(radLat) * this.PI)

        val hm = HashMap<String, Double>()
        hm.put("lat", lat - dLat)
        hm.put("lon", lon - dLon)

        return hm
    }

    //轉(zhuǎn)換經(jīng)度
    fun transformLon(x: Double, y: Double): Double {
        var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
        ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0
        ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0
        ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0
        return ret
    }

    //轉(zhuǎn)換緯度
    fun transformLat(x: Double, y: Double): Double {
        var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
        ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0
        ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0
        ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0
        return ret
    }
}

高德支持Google瓦片
1.高德地圖如果想要顯示國外地圖,可以選擇使用google瓦片

 /**
     * 加載在線瓦片數(shù)據(jù)
     */
    private fun useOMCMap() {
//        val url = "http://www.google.cn/maps/vt?lyrs=y&gl=cn&x=%d&s=&y=%d&z=%d"
//        val url =  "http://mt1.google.cn/vt/lyrs=y&hl=zh-CN&gl=cn&x=%d&s=&y=%d&z=%d"   //3D衛(wèi)星地圖
//        val url = "http://mt0.google.cn/vt/lyrs=y@198&hl=zh-CN&gl=cn&src=app&x=%d&y=%d&z=%d&s="   //衛(wèi)星地圖
        val url = "http://mt2.google.cn/vt/lyrs=m@167000000&hl=zh-CN&gl=cn&src=app&x=%d&y=%d&z=%d&s=Galil"  //平面地圖
        if (tileOverlayOptions == null) {
            tileOverlayOptions = TileOverlayOptions().tileProvider(object : UrlTileProvider(256, 256) {
                override fun getTileUrl(x: Int, y: Int, zoom: Int): URL? {
                    try {
                        //return new URL(String.format(url, zoom + 1, TileXYToQuadKey(x, y, zoom)));
                        //return new URL(String.format(url, x, y, zoom));
                        val mFileDirName: String
                        val mFileName: String
                        mFileDirName = String.format("L%02d/", zoom + 1)
                        mFileName = String.format("%s", TileXYToQuadKey(x, y, zoom))//為了不在手機(jī)的圖片中顯示,下載的圖片取消jpg后綴,文件名自己定義,寫入和讀取一致即可,由于有自己的bingmap圖源服務(wù),所以此處我用的bingmap的文件名
                        val LJ = FileApi.MAP_DIRECTORY + mFileDirName + mFileName
                        if (MapImageCache.instance.isBitmapExit(mFileDirName + mFileName)) {//判斷本地是否有圖片文件,如果有返回本地url,如果沒有,緩存到本地并返回googleurl
                            return URL("file://" + LJ)
                        } else {
                            val filePath = String.format(url, x, y, zoom)
                            val mBitmap: Bitmap
                            //mBitmap = BitmapFactory.decodeStream(getImageStream(filePath));//不知什么原因?qū)е掠写罅康膱D片存在壞圖,所以重寫InputStream寫到byte數(shù)組方法
                            val stream = getImageStream(filePath)
                            if (stream != null) {
                                mBitmap = getImageBitmap(stream)
                                try {
                                    saveFile(mBitmap, mFileName, mFileDirName)
                                } catch (e: IOException) {
                                    e.printStackTrace()
                                }
                            }

                            return URL(filePath)
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }

                    return null
                }
            })
            tileOverlayOptions!!.diskCacheEnabled(false)   //由于高德自帶的瓦片緩存在關(guān)閉程序后會自動清空,所以無意義,關(guān)閉本地緩存
                    .diskCacheDir(FileApi.MAP_DIRECTORY)
                    .diskCacheSize(1024000)
                    .memoryCacheEnabled(true)
                    .memCacheSize(102400)
                    .zIndex(-9999f)
        }
        //增加瓦片貼圖
        mtileOverlay = aMap?.addTileOverlay(tileOverlayOptions)
        mtileOverlay?.isVisible = true
    }

如果需要在中國地域中停止添加瓦片,需要remove掉瓦片

fun stopUseOMCMap() {
        mtileOverlay?.remove()
        mtileOverlay?.clearTileCache()
        mtileOverlay?.isVisible = false
        aMap?.removecache()

    }

注意一點,請不要一直觸發(fā)重復(fù)addTileOverlay,調(diào)用remove的次數(shù)和add的次數(shù)是需要對應(yīng)的。
這里還有一部分的代碼沒有貼上,我將會開放一個module的demo供大家演示,有興趣的同學(xué)也可以在群內(nèi)聯(lián)系我。

特殊問題

1.高德只支持國內(nèi)坐標(biāo)詳情,如果在國外發(fā)送了國外地址到國內(nèi)手機(jī),國內(nèi)手機(jī)會使用高德地圖,將會無法顯示國外坐標(biāo)詳情。
2.google對國內(nèi)坐標(biāo)信息詳情也是比較有限的。
3.地圖搜索涉及到異步返回,onDestroy請去掉監(jiān)聽,不然會有內(nèi)存泄露
4.暫時發(fā)現(xiàn)高德的mapview會持有Activity context對象不釋放的情況,修復(fù)好會在這里更新
5.高德搜索的時候,如果不加入city的參數(shù),將是全國搜索,很可能搜索不到你所在城市的地點(獲取定位的時候可以順帶獲取所在城市名字),請謹(jǐn)慎處理。

對于一些更加深入的問題,例如交通地圖軌跡這樣的,暫時業(yè)務(wù)還沒涉及到,如果有更好的解決方案,會繼續(xù)上新篇,謝謝。

群1已滿,可以進(jìn)群2學(xué)習(xí)組件化

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容