Android学习记录——让应用通晓地理

一个可以把现在的心情记录到地图上的 APP ,基于高德 LBS 开放平台。

需要用到的工具

Android Studio 和 Genymotion 的基本使用方法可参考《Android学习记录——开发环境搭建》

创建应用——MoodMap

1. 使用 Android Studio 创建一个名为 MoodMap 的 Android 项目

配置保持默认参数即可:

Minimum SDK: API 16: Android 4.1 (Jelly Bean)
Activity Name: MainActivity
Layout Name: activity_main
Title: MoodMap
Menu Resource Name: menu_main

导入高德 LBS 开放平台相关 SDK

1. 注册高德 LBS 开放平台账号并获取相关 SDK 的 KEY

注册账号后在我的 KEY页面获取 Android 平台 SDK 和 Rest 服务接口的 KEY。

2. 下载 SDK

3. 配置 SDK 库文件

将 SDK 包中对应的 .jar 文件复制到当前 app 项目下的 libs 目录中;然后再 Android Studio 中依次右击 .jar文件,选择 Add as library ,将其导入当前 app 项目。

创建一张云图

通过以下命令请求 Rest 服务接口创建一张用于存储心情数据的云图:

curl -d "key=RestKey&name=mymap" "http://yuntuapi.amap.com/datamanage/table/create"

RestKey为刚才申请的 Rest 服务接口的 KEY,注意保存返回结果中的tableid,后面需要保存到 AndroidManifest.xml 文件中。

导入 Android Asynchronous Http Client

在项目 build.gradle 文件的 dependencies 节中添加一行compile 'com.loopj.android:android-async-http:1.4.5'

实现 APP 的功能

1. 界面

res/layout/activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <com.amap.api.maps2d.MapView        xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/map"        android:layout_width="fill_parent" android:layout_height="fill_parent" />

</RelativeLayout>

res/layout/choose_mood.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent" >

    <ImageButton        android:id="@+id/happyButton"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="wrap_content"        android:background="@null"        android:src="@drawable/face_happy"        android:contentDescription="@string/happy" />

    <ImageButton        android:id="@+id/smileButton"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="wrap_content"        android:background="@null"        android:src="@drawable/face_smile"        android:contentDescription="@string/smile" />

    <ImageButton        android:id="@+id/sadButton"        android:layout_width="0dp"        android:layout_weight="1"        android:layout_height="wrap_content"        android:background="@null"        android:src="@drawable/face_sad"        android:contentDescription="@string/sad" />

</LinearLayout>

res/values/strings.xml:

<resources>    <string name="app_name">MoodMap</string>

    <string name="action_settings">Settings</string>    <string name="your_mood">现在的心情</string>    <string name="happy">开心</string>    <string name="sad">伤心</string>    <string name="smile">微笑</string></resources>

复制以下3张图片到 res/drawable/ 目录下:

2. 逻辑

MainActivity.java:

package me.covertness.moodmap;

import java.util.ArrayList;import java.util.List;

import android.app.Dialog;import android.content.Context;import android.content.pm.ApplicationInfo;import android.content.pm.PackageManager;import android.location.Location;import android.os.Handler;import android.support.v7.app.ActionBarActivity;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.ImageButton;

import com.amap.api.cloud.model.AMapCloudException;import com.amap.api.cloud.model.CloudItem;import com.amap.api.cloud.model.CloudItemDetail;import com.amap.api.cloud.model.LatLonPoint;import com.amap.api.cloud.search.CloudResult;import com.amap.api.cloud.search.CloudSearch;import com.amap.api.location.AMapLocation;import com.amap.api.location.AMapLocationListener;import com.amap.api.location.LocationManagerProxy;import com.amap.api.location.LocationProviderProxy;import com.amap.api.maps2d.AMap;import com.amap.api.maps2d.CameraUpdateFactory;import com.amap.api.maps2d.LocationSource;import com.amap.api.maps2d.MapView;import com.amap.api.maps2d.model.BitmapDescriptor;import com.amap.api.maps2d.model.BitmapDescriptorFactory;import com.amap.api.maps2d.model.LatLng;import com.amap.api.maps2d.model.LatLngBounds;import com.amap.api.maps2d.model.Marker;import com.amap.api.maps2d.model.MarkerOptions;import com.loopj.android.http.*;import org.apache.http.Header;import org.json.JSONException;import org.json.JSONObject;

public class  extends ActionBarActivity implements LocationSource, AMapLocationListener,        CloudSearch.OnCloudSearchListener {    private String amapRestfulKey;    private String cloudTableId;    private MapView mapView;    private AMap aMap;    private LocationManagerProxy locationManagerProxy;    private CloudSearch cloudSearch;    private CloudSearch.Query currentQuery;    private OnLocationChangedListener locationChangedListener;    private AMapLocation currentLocation = null;    private static AsyncHttpClient httpClient = new AsyncHttpClient();

    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);

        try {            ApplicationInfo app = this.getPackageManager().getApplicationInfo(this.getPackageName(),PackageManager.GET_META_DATA);            amapRestfulKey = app.metaData.getString("me.covertness.moodmap.restful.apikey");            cloudTableId = app.metaData.getString("me.covertness.moodmap.cloud.tableid");        } catch (PackageManager.NameNotFoundException e) {            Log.e("metadata", "Failed to load meta-data, NameNotFound: " + e.getMessage());        } catch (NullPointerException e) {            Log.e("metadata", "Failed to load meta-data, NullPointer: " + e.getMessage());        }

        mapView = (MapView) findViewById(R.id.map);        mapView.onCreate(savedInstanceState);

        if (aMap == null) {            aMap = mapView.getMap();            aMap.setLocationSource(this);               // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false            aMap.setMyLocationEnabled(true);        }

        cloudSearch = new CloudSearch(this);        cloudSearch.setOnCloudSearchListener(this);    }

    protected void onDestroy() {        super.onDestroy();

        mapView.onDestroy();    }

    protected void onPause() {        super.onPause();

        mapView.onPause();    }

    protected void onResume() {        super.onResume();

        mapView.onResume();    }

    protected void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);

        mapView.onSaveInstanceState(outState);    }

    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.menu_main, menu);        return true;    }

    @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();

        //noinspection SimplifiableIfStatement        if (id == R.id.action_settings) {            return true;        }

        return super.onOptionsItemSelected(item);    }

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

        if (locationManagerProxy == null) {            locationManagerProxy = LocationManagerProxy.getInstance(this);        }

        //此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗,        //注意设置合适的定位时间的间隔,并且在合适时间调用removeUpdates()方法来取消定位请求        //在定位结束后,在合适的生命周期调用destroy()方法        //其中如果间隔时间为-1,则定位只定一次        locationManagerProxy.requestLocationData(                LocationProviderProxy.AMapNetwork, -1, 10, this);    }

    @Override    public void deactivate() {        locationChangedListener = null;

        if (locationManagerProxy != null) {            locationManagerProxy.removeUpdates(this);            locationManagerProxy.destroy();        }        locationManagerProxy = null;    }

    @Override    public void onLocationChanged(AMapLocation aMapLocation) {        if (locationChangedListener != null && aMapLocation != null) {            int retCode = aMapLocation.getAMapException().getErrorCode();            if (retCode == 0) {                currentLocation = aMapLocation;                locationChangedListener.onLocationChanged(currentLocation);// 显示系统小蓝点                updateMapData();                showMoodDialog();            } else {                Log.e("locate", aMapLocation.getAMapException().getErrorMessage());            }        }    }

    /**     * 此方法已经废弃     */    @Override    public void onLocationChanged(Location location) {

    }

    @Override    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override    public void onProviderEnabled(String provider) {

    }

    @Override    public void onProviderDisabled(String provider) {    }

    @Override    public void onCloudSearched(CloudResult result, int rCode) {        if (rCode == 0) {            if (result != null && result.getQuery() != null) {                if (result.getQuery().equals(currentQuery)) {                    //获取云数据                    List<CloudItem> mCloudItems = result.getClouds();                    if (mCloudItems != null && mCloudItems.size() > 0) {                        aMap.clear();                        PoiOverlay mPoiCloudOverlay = new PoiOverlay(this, aMap, mCloudItems);                        mPoiCloudOverlay.removeFromMap();                        mPoiCloudOverlay.addToMap();                        mPoiCloudOverlay.zoomToSpan();                    } else {                        Log.d("search", "result is empty");                    }                }            } else {                Log.d("search", "result is empty");            }        } else {            Log.e("search", "error code: " + rCode);        }    }

    @Override    public void onCloudItemDetailSearched(CloudItemDetail cloudItemDetail, int i) {

    }

    private void showMoodDialog() {        final Dialog moodDialog = new Dialog(this);        moodDialog.setContentView(R.layout.choose_mood);        moodDialog.setTitle(R.string.your_mood);

        final JsonHttpResponseHandler publishMoodHandler = new JsonHttpResponseHandler() {            @Override            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {                try {                    if (response.getInt("status") == 1) {                        final Handler handler = new Handler();                        handler.postDelayed(new Runnable() {                            @Override                            public void run() {                                updateMapData();                            }                        }, 5000);                    } else {                        Log.e("publishMood", "failed: " + response.getString("info"));                    }                } catch (JSONException e) {                    e.printStackTrace();                }            }        };

        final ImageButton happyButton = (ImageButton) moodDialog.findViewById(R.id.happyButton);        // if button is clicked, close the custom dialog        happyButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                moodDialog.dismiss();                publishMood(happyButton.getContentDescription().toString(), publishMoodHandler);            }        });

        final ImageButton smileButton = (ImageButton) moodDialog.findViewById(R.id.smileButton);        // if button is clicked, close the custom dialog        smileButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                moodDialog.dismiss();                publishMood(smileButton.getContentDescription().toString(), publishMoodHandler);            }        });

        final ImageButton sadButton = (ImageButton) moodDialog.findViewById(R.id.sadButton);        // if button is clicked, close the custom dialog        sadButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                moodDialog.dismiss();                publishMood(sadButton.getContentDescription().toString(), publishMoodHandler);            }        });

        moodDialog.show();    }

    private void publishMood(String mood, AsyncHttpResponseHandler responseHandler) {        RequestParams params = new RequestParams();        params.put("key", amapRestfulKey);        params.put("tableid", cloudTableId);        params.put("loctype", "1");

        JSONObject jsonObject = new JSONObject();        try {            jsonObject.put("_name", mood);            jsonObject.put("_location",                    currentLocation.getLongitude() + "," + currentLocation.getLatitude());        } catch (JSONException e) {            e.printStackTrace();        }        params.put("data", jsonObject.toString());        httpClient.post("http://yuntuapi.amap.com/datamanage/data/create", params, responseHandler);    }

    private void updateMapData() {        //圆形查询范围        CloudSearch.SearchBound bound = new CloudSearch.SearchBound(new LatLonPoint(                currentLocation.getLatitude(), currentLocation.getLongitude()), 50000);        try {            //构造查询对象            currentQuery = new CloudSearch.Query(cloudTableId, "", bound);

            //异步搜索            cloudSearch.searchCloudAsyn(currentQuery);        } catch (AMapCloudException e) {            e.printStackTrace();        }    }}

class PoiOverlay {    private final List<CloudItem> mPois;    private final AMap mAMap;    private final Context mainContext;    private ArrayList<Marker> mPoiMarks = new ArrayList<>();

    public PoiOverlay(Context context, AMap amap, List<CloudItem> pois) {        mainContext = context;        mAMap = amap;        mPois = pois;    }

    public void addToMap() {        for (int i = 0; i < mPois.size(); i++) {            Marker marker = mAMap.addMarker(getMarkerOptions(i));            marker.setObject(i);            mPoiMarks.add(marker);        }    }

    public void removeFromMap() {        for (Marker mark : mPoiMarks) {            mark.remove();        }    }

    public void zoomToSpan() {        if (mPois != null && mPois.size() > 0) {            if (mAMap == null)                return;            LatLngBounds bounds = getLatLngBounds();            mAMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 20));        }    }

    private LatLngBounds getLatLngBounds() {        LatLngBounds.Builder b = LatLngBounds.builder();        for (int i = 0; i < mPois.size(); i++) {            b.include(new LatLng(mPois.get(i).getLatLonPoint().getLatitude(),                    mPois.get(i).getLatLonPoint().getLongitude()));        }        return b.build();    }

    private MarkerOptions getMarkerOptions(int index) {        return new MarkerOptions()                .position(                        new LatLng(mPois.get(index).getLatLonPoint()                                .getLatitude(), mPois.get(index)                                .getLatLonPoint().getLongitude()))                .title(getTitle(index)).snippet(getSnippet(index))                .icon(getBitmapDescriptor(index));    }

    protected BitmapDescriptor getBitmapDescriptor(int index) {        String title = mPois.get(index).getTitle();        if (title.equals(mainContext.getString(R.string.happy))) {            return BitmapDescriptorFactory.fromResource(R.drawable.face_happy);        } else if (title.equals(mainContext.getString(R.string.smile))) {            return BitmapDescriptorFactory.fromResource(R.drawable.face_smile);        } else if (title.equals(mainContext.getString(R.string.sad))) {            return BitmapDescriptorFactory.fromResource(R.drawable.face_sad);        } else {            return null;        }    }

    protected String getTitle(int index) {        return mPois.get(index).getTitle();    }

    protected String getSnippet(int index) {        return mPois.get(index).getSnippet();    }}

3. 修改AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="me.covertness.moodmap" >

    <uses-permission android:name="android.permission.INTERNET" />    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />

    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <meta-data            android:name="com.amap.api.v2.apikey" android:value="AndroidKey"/>        <meta-data            android:name="me.covertness.moodmap.restful.apikey" android:value="RestKey"/>        <meta-data            android:name="me.covertness.moodmap.cloud.tableid" android:value="CloudTableId"/>

        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application>

</manifest>

注意将代码中的AndroidKeyRestKeyCloudTableId替换成自己应用的。

测试应用

使用 Android 4.1 或以上版本的手机打开 MoodMap (Genymotion模拟器需要先打开GPS并设置当前的位置),选择一个表情,等待片刻便能在地图上看到自己所在的位置多了一个表情,如下图所示。

要点总结

1. 界面

应用初始化完成后会创建一个自定义的对话框,其布局文件位于 choose_mood.xml 文件中,当用户做出选择后该对话框即关闭。

2. 逻辑

此应用启动后首先初始化 2D 地图 SDK,然后发起定位请求,获得位置信息后显示对话框让用户选择当前的心情,之后保存用户的心情到云图,最后更新云图数据显示到地图上。期间使用到 2D 地图 SDK 、 定位 SDK 和 云图 SDK ,具体使用方法可参看实现代码及文档。

3. AndroidManifest.xml

应用将用到第三方服务的 KEY 保存到了 meta-data 字段,可在 Java 代码中通过如下方式获取:

try {    ApplicationInfo app = this.getPackageManager().getApplicationInfo(this.getPackageName(),PackageManager.GET_META_DATA);    amapRestfulKey = app.metaData.getString("me.covertness.moodmap.restful.apikey");    cloudTableId = app.metaData.getString("me.covertness.moodmap.cloud.tableid");} catch (PackageManager.NameNotFoundException e) {    Log.e("metadata", "Failed to load meta-data, NameNotFound: " + e.getMessage());} catch (NullPointerException e) {    Log.e("metadata", "Failed to load meta-data, NullPointer: " + e.getMessage());}

原文:大专栏  Android学习记录——让应用通晓地理

原文地址:https://www.cnblogs.com/chinatrump/p/11584773.html

时间: 2024-10-10 15:36:19

Android学习记录——让应用通晓地理的相关文章

Android学习记录一——安装环境

一直想接触这块,但是却一直耽搁到现在.找过几回资料,找过几回安装包,这两天受了些刺(gong)激(zi),决定静下心来,一点点开始吃. 接触c#都是从门外汉开始,谈不上任何编程基础,所以接触android也很慢. 1.首先需要找到开发工具.就像c#winform用VisualStudio,android的基础语言是java,开发工具自然首选eclipse.这个环节走了一些弯路,最后找到了android官网上的ADT(Android Development Tools). 官网介绍: It's a

android学习记录(十七)---Notification 精要解析

notification,通知,显示在状态栏那里的信息.它看起来是这样的: 如果想设计更为人性化的notification,可参考android官方的design文档----notification 创建notification 类似于AlertDialog的创建,notification的创建同样也是通过NotificationCompat.Builder来设置ui界面然后调用Builder.build()方法创建.当你想展示你的notification时,通过调用NotificationMa

Android学习记录(一)

其实以前我培训的时候就是学Android,只不过当时只是和它有一面之缘,却有缘无份.在学习完java的基础知识之内,就悲催地去公司实习了,从此和Android形同路人.这些天不知道抽了啥风,越来越想将Android重新揽回怀抱,重拾旧爱啊. 接下来就附上我的学习起步以及碰到的一些问题和解决方式: 1.下载ADT(Android Development Tools).就在官方下载最新的.我下载的版本是:adt-bundle-windows-x86_64-20140702.zip.ADT中已经包含e

Android学习记录(一)res中AndroidManifest文件说明

<?xml version="1.0" encoding="utf-8"?> <!-- xmlns:android 约束规则,参考schemas package:此包表示整个java应用程序的主要包名,而且是一个默认的程序名称. android:versionCode="1":表示工程所生成的apk的版本号,1开始,2,3,4不断升级(软件升级时用的) android:versionName="1.0":表示

Android学习记录 - Activity

1,Activity是什么? Activity是一个用户界面的窗口,在这个窗口中摆放了各种与用户交互的UI组件,如文本.输入框和按钮等 2,为什么需要Activity?(意义.作用等) 在一个应用程序中,有一个或者多个与用户交互的界面,在Android中,这个界面叫做Activity,Activity的主要功能就是负责UI部分,它是各种UI组件的容器和载体,可以控制和管理布局在其中的UI组件 3,Activity的生命周期

android学习记录(四)管理里中基本组键

1.线性布局管理器<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android> </ LinearLayout >   Android:orientation 用于设置布局管理器内组件的排列方式(ertical(垂直).horizontal(水平)) Android:gravity 用于设置管理器内组件的对齐方式(top.bottom.left.right等) Android:layout_width

android学习记录(三)百度地图错误---只有一个电话显示帧,没有地图内容。

安卓开发新手百度地图,刚开始碰到一个问题,没有地图信息,还有就是它只有一帧. 如图所示: 上网寻找说是key的问题,然后又一次申请.还是不行. 最后再次看了自己的Manifest文件,发现自己的<MataData>有问题.放在了新写的Application标签里.应该放在自带的Application里面.然后,就攻克了. 部分的Manifest文件: <application android:allowBackup="true" android:icon="

Android学习记录04——绑定形式

1 编写HelloBindService方法, 2 编写BindServiceActivity方法, 3 然后运行,结果如图-- a.单击"绑定Service" b.单击"取消绑定Service" c.取消绑定后,单击"获取Service数据",提示先绑定服务 d.单击"绑定Service"后,再点击"获取Service数据" 总的来说,这次实验就是绑定形式的Service,一些注意的点都在代码注释中,也就

android学习记录 生命周期介绍和页面跳转

Activity生命周期(7个方法和3个阶段) 7个方法: Void onCreate(Bundle savedlnstanceState) Void onStart()    //开始 Void onRestart()  //重置 Void onResume()  //回复 Void onPause()   //暂停 Void onStop()    //停止 Void onDestroy()  //销毁 3个阶段: 开始Activity,在这个阶段以此执行3个生命周期,分别是onCreate