安卓编程 为什么第一次进入地图时ongetgeocoderesultt返回为空,第二次就好了

--旗下,中国最好的系统开发学习平台!
热门推荐:
当前位置: >
Android 百度地图定位(手动+自动) 安卓开发教程
发布时间: 14:21 | 作者:采集侠 | 来源:网络整理 | 浏览:
近由于项目需要,研究了下百度地图定位,他们提供的实例基本都是用监听器实现自动定位的。我想实现一种效果:当用户进入UI时,不定位,用户需要定位的时候,自己手动点击按钮,再去定位当前位置。& 经过2天研究和咨询,找到了解决方案,在此备忘一下。
注意:定位使用真机才能够真正定位;模拟器的话,在DDMS中的Emulator Control中,选择Manual,下面单选按钮选择Decimal,然后填写经纬度,send后,再点击定位我的位置按钮,就能定位了(这应该算是固定定位,哈哈。。。)、
1、第一步当然是获取一个针对自己项目的key值。
2、使用百度API是有前提的,摘自百度:首先将API包括的两个文件baidumapapi.jar和 libBMapApiEngine.so拷贝到工程根目录及libs\armeabi目录下,并在工程属性-&Java Build Path-&Libraries中选择&Add JARs&,选定baidumapapi.jar,确定后返回,这样您就可以在您的程序中使用API了。(这两个文件见附件)。
3、按照自己的需求写一个layout,我的如下:
&?xml version=&1.0& encoding=&utf-8&?&
&LinearLayout&
xmlns:android=&&&
android:orientation=&vertical&&
android:layout_width=&fill_parent&&
android:layout_height=&fill_parent&&
&TextView&&
android:id=&@+id/myLocation_id&&&
android:layout_width=&fill_parent&&&
android:layout_height=&wrap_content&&
android:textSize=&15dp&&
android:gravity=&center_horizontal&&
android:textColor=&@drawable/black&&
android:background=&@drawable/gary&&
&com.baidu.mapapi.MapView android:id=&@+id/bmapsView&&
android:layout_width=&fill_parent& android:layout_height=&fill_parent&&&
android:clickable=&true&& android:layout_weight=&1&&&&&
android:layout_width=&wrap_content&&&
android:layout_height=&wrap_content&&&
android:id=&@+id/location_button_id&&&
android:text=&@string/location_button_text&&
&/LinearLayout&&
需要特别注意的是:&com.baidu.mapapi.MapView& /& 这玩意。
4、写一个MapApplication实现application,提供全局的BMapManager,以及其初始化。
public BMapManager mapManager =&
static MapA&
public String mStrKey = &你申请的key值&;&
@Override&
public void onCreate() {&
mapManager = new BMapManager(this);&
mapManager.init(mStrKey, new MyGeneralListener());&
@Override&
//建议在您app的退出之前调用mapadpi的destroy()函数,避免重复初始化带来的时间消耗&
public void onTerminate() {&
// TODO Auto-generated method stub&
if(mapManager != null)&
mapManager.destroy();&
mapManager =&
super.onTerminate();&
static class MyGeneralListener implements MKGeneralListener{&
@Override&
public void onGetNetworkState(int arg0) {&
Toast.makeText(MapApplication.app.getApplicationContext(), &您的网络出错啦!&,&
Toast.LENGTH_LONG).show();&
@Override&
public void onGetPermissionState(int iError) {&
if (iError ==& MKEvent.ERROR_PERMISSION_DENIED) {&
// 授权Key错误:&
Toast.makeText(MapApplication.app.getApplicationContext(),&您的授权Key不正确!&,&
Toast.LENGTH_LONG).show();&
5、接下来就是按照百度api写定位代码了,使用handler机制去添加定位图层,需要说明的都在注释上了。&
private BMapManager mBMapMan =&
private MapView mMapView =&
private MapController bMapC&
private MKLocationManager mkLocationM&
private MKSearch mkS&
private TextView address_&& //定位到的位置信息&
private ProgressD&
private List&HotelInfo& hotelL&
private int distance = 1000;& //查询的范围(单位:m)&
Handler handler = new Handler(){&
@Override&
public void handleMessage(Message msg) {&
double lat = msg.getData().getDouble(&lat&);&
double lon = msg.getData().getDouble(&lon&);&
if(lat!=0&&lon!=0){&
GeoPoint point = new GeoPoint(&
(int) (lat * 1E6),&
(int) (lon * 1E6));&
bMapController.animateTo(point);& //设置地图中心点&
bMapController.setZoom(15);&
mkSearch.reverseGeocode(point);&& //解析地址(异步方法)&
MyLocationOverlay myLoc = new MyLocationOverlayFromMap(ShowMapAct.this,mMapView);&
myLoc.enableMyLocation();&& // 启用定位&
myLoc.enableCompass();&&&&& // 启用指南针&
mMapView.getOverlays().add(myLoc);&
Toast.makeText(ShowMapAct.this, &没有加载到您的位置&, Toast.LENGTH_LONG).show();&
if(hotelList!=null){&
Drawable marker = getResources().getDrawable(R.drawable.iconmarka);& //设置marker&
marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight());&& //为maker定义位置和边界&
mMapView.getOverlays().add(new OverItemList(marker,hotelList,ShowMapAct.this,bMapController));&
}else if(hotelList==null&&lat!=0&&lon!=0){&
Toast.makeText(ShowMapAct.this, &网络异常,没有获取到酒店信息。&, Toast.LENGTH_LONG).show();&
if(dialog!=null)& dialog.dismiss();&
@Override&
protected void onCreate(Bundle savedInstanceState) {&
distance = getIntent().getExtras().getInt(&distance&);&& //获取查询范围&
super.onCreate(savedInstanceState);&
setContentView(R.layout.location);&
mMapView = (MapView)findViewById(R.id.bmapsView);&& //初始化一个mapView& 存放Map&
init();& //初始化地图管理器&
super.initMapActivity(mBMapMan);&
address_view = (TextView)findViewById(R.id.myLocation_id);&
SpannableStringBuilder style = new SpannableStringBuilder(String.format(getResources().getString(R.string.location_text),&位置不详&));&
style.setSpan(new ForegroundColorSpan(Color.RED), 5, style.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);&
address_view.setText(style);&
Button location_button = (Button)findViewById(R.id.location_button_id);&
location_button.setOnClickListener(new View.OnClickListener(){&
@Override&
public void onClick(View v) {&
dialog = ProgressDialog.show(ShowMapAct.this, &&, &数据加载中,请稍后.....&);&
new Thread(new MyThread()).start();&
mkSearch = new MKSearch();&& //初始化一个MKSearch,根据location解析详细地址&
mkSearch.init(mBMapMan, this);&
mMapView.setBuiltInZoomControls(true);&& //启用内置的缩放控件&
bMapController = mMapView.getController();&
GeoPoint defaultPoint = new GeoPoint((int) (39.920934 * 1E6),(int) (116.412817 * 1E6));& //用给定的经纬度构造一个GeoPoint,单位是微度 (度 * 1E6)&
bMapController.setCenter(defaultPoint);& //设置地图中心点&
bMapController.setZoom(12);& //设置地图zoom级别&
mkLocationManager = mBMapMan.getLocationManager();&
* 初始化地图管理器BMapManager
public void init(){&
MapApplication app = (MapApplication)getApplication();&
if (app.mapManager == null) {&
app.mapManager = new BMapManager(getApplication());&
app.mapManager.init(app.mStrKey, new MapApplication.MyGeneralListener());&
mBMapMan = app.mapM&
@Override&
protected void onDestroy() {&
MapApplication app = (MapApplication)getApplication();&
if (mBMapMan != null) {&
mBMapMan.destroy();&
app.mapManager.destroy();&
app.mapManager =&
mBMapMan =&
super.onDestroy();&
@Override&&&
protected void onPause() {&&&
if (mBMapMan != null) {&&&
// 终止百度地图API& &
mBMapMan.stop();&&&
super.onPause();&&&
@Override&&&
protected void onResume() {&
if (mBMapMan != null) {&&&
// 开启百度地图API& &
mBMapMan.start();&&&
super.onResume();&&&
@Override&
protected boolean isRouteDisplayed() {&
@Override&
public void onGetAddrResult(MKAddrInfo result, int iError) {&
if(result==null)&
SpannableStringBuilder style = new SpannableStringBuilder(String.format(getResources().getString(R.string.location_text),result.strAddr));&
style.setSpan(new ForegroundColorSpan(Color.RED), 5, style.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);&
address_view.setText(style);&
if(dialog!=null) dialog.dismiss();&
@Override&
public void onGetDrivingRouteResult(MKDrivingRouteResult arg0, int arg1) {}&
@Override&
public void onGetPoiResult(MKPoiResult arg0, int arg1, int arg2) {}&
@Override&
public void onGetTransitRouteResult(MKTransitRouteResult arg0, int arg1) {}&
@Override&
public void onGetWalkingRouteResult(MKWalkingRouteResult arg0, int arg1) {}&
* 重新定位,加载数据
* @author Administrator
class MyThread implements Runnable{&
@Override&
public void run() {&
* 最重要的就是这个玩意
* 由于LocationListener获取第一个位置修正的时间会很长,为了避免用户等待,
* 在LocationListener获取第一个更精确的位置之前,应当使用getLocationInfo() 获取一个缓存的位置
Location location = mkLocationManager.getLocationInfo();&
double lat = 0d,lon = 0d;&
if(location!=null){&& //定位到位置&
String coordinate = location.getLatitude()+&,&+location.getLongitude();&
HotelRemoteData hotelData = new HotelRemoteData();&
* 远程获取酒店列表数据
hotelList = hotelData.getHotelToMap(coordinate,distance);&
lat = location.getLatitude();&
lon = location.getLongitude();&
Message msg = new Message();&
Bundle data = new Bundle();&
data.putDouble(&lat&, lat);&
data.putDouble(&lon&, lon);&
msg.setData(data);&
handler.sendMessage(msg);&Android事件散发机制完全解析,带你从源码的角度彻底理解(上)_Android深入显出之Surface_iphone:GPS位置跟地图开发(CLLocation and MKMapView)__脚本百事通
稍等,加载中……
^_^请注意,有可能下面的2篇文章才是您想要的内容:
Android事件散发机制完全解析,带你从源码的角度彻底理解(上)
Android深入显出之Surface
iphone:GPS位置跟地图开发(CLLocation and MKMapView)
Android事件散发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图片使用Button而不用ImageView?等等……如果想要彻底搞明白这些问题,掌握Android事件分发机制是必不可少的,而Android事件分发机制绝对不是三言两语就能说得清的。阅读源码讲究由浅入深,循序渐进,因此我们也从简单的开始,本篇先带大家探究View的事件分发,下篇再去探究难度更高的ViewGroup的事件分发。那我们现在就开始吧!比如说你当前有一个非常简单的项目,只有一个Activity,并且Activity中只有一个按钮。你可能已经知道,如果想要给这个按钮注册一个点击事件,只需要调用:
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Log.d("TAG", "onClick execute");
这样在onClick方法里面写实现,就可以在按钮被点击的时候执行。你可能也已经知道,如果想给这个按钮再添加一个touch事件,只需要调用:
button.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "onTouch execute, action " + event.getAction());
onTouch方法里能做的事情比onClick要多一些,比如判断手指按下、抬起、移动等事件。那么如果我两个事件都注册了,哪一个会先执行呢?我们来试一下就知道了,运行程序点击按钮,打印结果如下:可以看到,onTouch是优先于onClick执行的,并且onTouch执行了两次,一次是ACTION_DOWN,一次是ACTION_UP(你还可能会有多次ACTION_MOVE的执行,如果你手抖了一下)。因此事件传递的顺序是先经过onTouch,再传递到onClick。细心的朋友应该可以注意到,onTouch方法是有返回值的,这里我们返回的是false,如果我们尝试把onTouch方法里的返回值改成true,再运行一次,结果如下:我们发现,onClick方法不再执行了!为什么会这样呢?你可以先理解成onTouch方法返回true就认为这个事件被onTouch消费掉了,因而不会再继续向下传递。如果到现在为止,以上的所有知识点你都是清楚的,那么说明你对Android事件传递的基本用法应该是掌握了。不过别满足于现状,让我们从源码的角度分析一下,出现上述现象的原理是什么。首先你需要知道一点,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类里的dispatchTouchEvent方法,可是你会发现Button类里并没有这个方法,那么就到它的父类TextView里去找一找,你会发现TextView里也没有这个方法,那没办法了,只好继续在TextView的父类View里找一找,这个时候你终于在View里找到了这个方法,示意图如下:然后我们来看一下View中dispatchTouchEvent方法的源码:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return onTouchEvent(event);
这个方法非常的简洁,只有短短几行代码!我们可以看到,在这个方法内,首先是进行了一个判断,如果mOnTouchListener != null,(mViewFlags & ENABLED_MASK) == ENABLED和mOnTouchListener.onTouch(this, event)这三个条件都为真,就返回true,否则就去执行onTouchEvent(event)方法并返回。先看一下第一个条件,mOnTouchListener这个变量是在哪里赋值的呢?我们寻找之后在View里发现了如下方法:
public void setOnTouchListener(OnTouchListener l) {
mOnTouchListener =
Bingo!找到了,mOnTouchListener正是在setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。现在我们可以结合前面的例子来分析一下了,首先在dispatchTouchEvent中最先执行的就是onTouch方法,因此onTouch肯定是要优先于onClick执行的,也是印证了刚刚的打印结果。而如果在onTouch方法里返回了true,就会让dispatchTouchEvent方法直接返回true,不会再继续往下执行。而打印结果也证实了如果onTouch返回true,onClick就不会再执行了。根据以上源码的分析,从原理上解释了我们前面例子的运行结果。而上面的分析还透漏出了一个重要的信息,那就是onClick的调用肯定是在onTouchEvent(event)方法中的!那我们马上来看下onTouchEvent的源码,如下所示:
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewF
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken =
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
if (!post(mPerformClick)) {
performClick();
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
removeTapCallback();
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress =
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
int slop = mTouchS
if ((x & 0 - slop) || (x &= getWidth() + slop) ||
(y & 0 - slop) || (y &= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
相较于刚才的dispatchTouchEvent方法,onTouchEvent方法复杂了很多,不过没关系,我们只挑重点看就可以了。首先在第14行我们可以看出,如果该控件是可以点击的就会进入到第16行的switch判断中去,而如果当前的事件是抬起手指,则会进入到MotionEvent.ACTION_UP这个case当中。在经过种种判断之后,会执行到第38行的performClick()方法,那我们进入到这个方法里瞧一瞧:
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
可以看到,只要mOnClickListener不是null,就会去调用它的onClick方法,那mOnClickListener又是在哪里赋值的呢?经过寻找后找到如下方法:
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
mOnClickListener =
一切都是那么清楚了!当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值。然后每当控件被点击时,都会在performClick()方法里回调被点击控件的onClick方法。这样View的整个事件分发的流程就让我们搞清楚了!不过别高兴的太早,现在还没结束,还有一个很重要的知识点需要说明,就是touch事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。说到这里,很多的朋友肯定要有巨大的疑问了。这不是在自相矛盾吗?前面的例子中,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到执行了吗?其实你只是被假象所迷惑了,让我们仔细分析一下,在前面的例子当中,我们到底返回的是什么。参考着我们前面分析的源码,首先在onTouch事件里返回了false,就一定会进入到onTouchEvent方法中,然后我们来看一下onTouchEvent方法的细节。由于我们点击了按钮,就会进入到第14行这个if判断的内部,然后你会发现,不管当前的action是什么,最终都一定会走到第89行,返回一个true。是不是有一种被欺骗的感觉?明明在onTouch事件里返回了false,系统还是在onTouchEvent方法中帮你返回了true。就因为这个原因,才使得前面的例子中ACTION_UP可以得到执行。那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。如下所示:
imageView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "onTouch execute, action " + event.getAction());
运行一下程序,点击ImageView,你会发现结果如下:在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。好了,关于View的事件分发,我想讲的东西全都在这里了。
Android深入显出之Surface
Android深入浅出之Surface一
本节的目的就是为了讲清楚
系统,大家耳熟能详的
SurfaceFlinger
到底是个什么东西,它的工作流程又是怎样的。当然,鉴于
SurfaceFlinger
的复杂性,我们依然将采用情景分析的办法,找到合适的切入点。
是怎么在屏幕上显示出来的呢?我将首先把这个说清楚。
接着我们把其中的关键调用抽象在
层,以这些函数调用为切入点来研究
SurfaceFlinger
。好了,开始我们的征途吧。
是如何显示的
最初的想法就是,
获得一块显存,然后在上面绘图,最后交给设备去显示。这个道理是没错,但是
SurfaceFlinger
System Server
进程中创建的,
一般另有线程,这之间是如何
如何挂上关系的呢?我可以先提前告诉大家,这个过程还比较复杂。呵呵。
好吧,我们从
最初的启动开始。代码在
framework/base/core/java/android/app/ActivityThread.java
中,这里有个函数叫
handleLaunchActivity
[----&ActivityThread:: handleLaunchActivity()]
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.
handleResumeActivity(r.token, false, r.isForward);
调用handleResumeActivity
handleLaunchActivity
handleResumeActivity
[---&ActivityThread:: handleResumeActivity]
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
boolean willBeVisible = !a.mStartedA
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor =
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
a.mWindowAdded =
wm.addView(decor, l); //
这个很关键。
那几行非常关键,它关系到咱们在
setContentView
到底都包含了些什么。我先告诉大家。所有你创建的
之上,还有一个
,这是一个
FrameLayout
,另外还有一个
PhoneWindow
。上面这些东西的代码在
framework/Policies/Base/Phone/com/android/Internal/policy/impl
。这些隐藏的
的创建都是由你在
setContentView
[----&PhoneWindow:: addContentView]
public void addContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
刚创建的时候mContentParent
installDecor();
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
installDecor
将创建mDecor
和mContentParent
是DecorView
mContentParent
是ViewGroup
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
ViewManager wm = a.getWindowManager()
又返回什么呢?
PhoneWindow
创建的时候会调用它的
setWindowManager
。而这个函数由
framework/base/core/java/android/view/Window.java
public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {
mAppToken = appT
mAppName = appN
if (wm == null) {
wm = WindowManagerImpl.getDefault();
mWindowManager = new LocalWindowManager(wm);
你看见没,分析
代码这个东西真的很复杂。
mWindowManager
LocalWindowManager
,但由通过
模式把功能交给
WindowManagerImpl
去实现了。
真的很复杂!
好了,罗里罗嗦的,我们回到
wm.addView(decor, l)
。最终会由
WindowManagerImpl
操作,我们直接看它的实现好了。
framework/base/core/java/android/view/WindowManagerImpl.java
[----&addView]
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
ViewR //ViewRoot
,我们的主人公终于登场!
synchronized (this) {
root = new ViewRoot(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
mViews[index] =
mRoots[index] =
mParams[index] =
root.setView(view, wparams, panelParentView);
是整个显示系统中最为关键的东西,看起来这个东西好像和
有那么点关系,其实它根本和
关系不大,它不过是一个
罢了,唯一有关系的就是它其中有一个变量为
类型。我们看看它的定义。
framework/base/core/java/android/view/ViewRoot.java
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks
private final Surface mSurface = new Surface();
它竟然从handler
派生,而ViewParent
不过定义了一些接口函数罢了。
直觉上感到它和
SurfaceFlinger
有点关系。要不先去看看?
framework/base/core/java/android/view/Surface.java
中,我们调用的是无参构造函数。
public Surface() {
mCanvas = new CompatibleCanvas(); //
就是创建一个Canvas
如果你有兴趣的话,看看
其他构造函数,最终都会调用
的实现,而这些
的实现将和
SurfaceFlinger
建立关系,但我们这里
显然还没有到这一步。那它到底是怎么和
SurfaceFlinger
搞上的呢?这一切待会就会水落石出的。
另外,为什么
是主人公呢?因为
建立了客户端和
SystemServer
的关系。我们看看它的构造函数。
public ViewRoot(Context context) {
getWindowSession(context.getMainLooper());
getWindowsession
将建立和WindowManagerService
ublic static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
//sWindowSession
是通过Binder
机制创建的。终于让我们看到点希望了
InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
sWindowSession = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"))
.openSession(imm.getClient(), imm.getInputContext());
mInitialized =
} catch (RemoteException e) {
return sWindowS
的进程调用另一端是
WindowManagerService
framework/base/services/java/com/android/server/WindowManagerService.java
中。我们先不说这个。
回过头来看看
接下来的调用。
[--&ViewRoot::setView()]
,这个函数很复杂,我们看其中关键几句。
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
requestLayout();
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
requestLayout
实现很简单,就是往
中发送了一个消息。
public void requestLayout() {
checkThread();
mLayoutRequested =
scheduleTraversals(); //
发送DO_TRAVERSAL
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled =
sendEmptyMessage(DO_TRAVERSAL);
我们看看跨进程的那个调用。
sWindowSession.add
。它的最终实现在
WindowManagerService
[---&WindowSession::add()]
public int add(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
return addWindow(this, window, attrs, viewVisibility, outContentInsets);
WindowSession
是个内部类,会调用外部类的
这个函数巨复杂无比,但是我们的核心目标是找到创建显示相关的部分。所以,最后精简的话就简单了。
[---&WindowManagerService:: addWindow]
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
创建一个WindowState
,这个又是什么玩意儿呢?
win = new WindowState(session, client, token,
attachedWindow, attrs, viewVisibility);
win.attach();
WindowState
类中有一个和
相关的成员变量,叫
SurfaceSession
函数中被创建。
SurfaceSession
SurfaceFlinger
有关系了。我们待会看。
好,我们知道
创建及调用
后,我们客户端的
WindowManagerService
建立了牢不可破的关系。
另外,我们知道
,而且刚才我们调用了
requestLayout
,所以接下来消息循环下一个将调用的就是
handleMessage
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL:
performTraversals();
performTraversals
更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到
performTraversals
,不过我们现在更感兴趣的是
是如何创建的。
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mV
boolean initialized =
boolean contentInsetsChanged =
boolean visibleInsetsC
//ViewRoot
也有一个Surface
成员变量,叫mSurface
,这个就是代表SurfaceFlinger
//ViewRoot
在这个Surface
上作画,最后将由SurfaceFlinger
来合成显示。刚才说了mSurface
还没有什么内容。
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
[----&ViewRoot:: relayoutWindow()]
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
//relayOut
是跨进程调用,mSurface
做为参数传进去了,看来离真相越来越近了呀!
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface); mSurface
做为参数传进去了。
我们赶紧转到
WindowManagerService
去看看吧。、
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
Configuration outConfig, Surface outSurface){
看到这里,我内心一阵狂喜,有戏,太有戏了!
是我们最初创建的WindowState
Surface surface = win.createSurfaceLocked();
if (surface != null) {
先创建一个本地surface
,然后把传入的参数outSurface copyFrom
outSurface.copyFrom(surface);
win.mReportDestroySurface =
win.mSurfacePendingDestroy =
outSurface.release();
[---&WindowState::createSurfaceLocked]
Surface createSurfaceLocked() {
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
mAttrs.getTitle().toString(),
0, w, h, mAttrs.format, flags);
Surface.openTransaction();
这里使用了
的另外一个构造函数。
public Surface(SurfaceSession s,
int pid, String name, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
mCanvas = new CompatibleCanvas();
init(s,pid,name,display,w,h,format,flags); ----&
调用了native
到这里,不进入
是不可能说清楚了。不过我们要先回顾下之前的关键步骤。
SurfaceSession
outSurface
上面两个类的
framework/base/core/jni/android_view_Surface.cpp
[----&SurfaceSession:: SurfaceSession()]
public class SurfaceSession {
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
函数对应为:
[---&SurfaceSession_init]
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
//SurfaceSession
对应为SurfaceComposerClient
sp&SurfaceComposerClient& client = new SurfaceComposerC
client-&incStrong(clazz);
常用做法,在JAVA
对象中保存C++
对象的指针。
env-&SetIntField(clazz, sso.client, (int)client.get());
[---&Surface_init]
static void Surface_init(
JNIEnv* env, jobject clazz,
jobject session,
jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
SurfaceComposerClient* client =
(SurfaceComposerClient*)env-&GetIntField(session, sso.client);
sp&SurfaceControl&
if (jname == NULL) {
相关解决方案
Android深入显出之Zygote(转)
Android深入显出视频中的总结
Android深入显出之Binder机制
深入显出Android makefile(1)-初探
Jsch 深入显出
Android-热门Android-最新Android-其它
android错误——aapt.exe已停止工作
华为G610(Android 4.2)恒久关闭键盘灯的方法
最新基于adt-bundle-windows-x86的android开发环境筹建
R cannot be resolved to a variable解决办法
Qt5.2中的android环境筹建
使用HAXM加速的Android x86模拟器(和一些有关问题)
仿微信聊天界面,以及话音录制功能
怎么修改安卓手机的wifi mac地址
初学者有关问题 This TableRow layout or its TableLayout parent is useless
Android SDK更新 Connection to http://dl- refused 解决办法
利用计算机玩Android版“天天连萌”刷高分(四)——模拟按键及程序优化
Android 实现读物翻页效果TXT阅读器源码
安卓找不到一般的versionCode和versionName
创办安卓项目没有自动成生R.Java文件
大家好 点击按钮下载 apk的例子
android4.0下, 怎么将Launcher2 上应用的图标放大
百度mapandroid sdk设置缩放级别后不能马上取得
怎么高速显示高清图片
要疯了,为什么在res停新建一个文件夹就出错
关于Service中的startService可能bindService的区别
安卓找不到一般的versionCode和versionName
android4.0下, 怎么将Launcher2 上应用的图标放大
安卓中点击返回键提醒用户是不是真的返回
大家好 点击按钮下载 apk的例子
百度mapandroid sdk设置缩放级别后不能马上取得
创办安卓项目没有自动成生R.Java文件
Android 实现读物翻页效果TXT阅读器源码
利用计算机玩Android版“天天连萌”刷高分(四)——模拟按键及程序优化
关于近段时间Android学习的一些感触
怎么学习android
上一篇: android OpenGL学习札记(一)
下一篇: android回调函数小结
iphone:GPS位置跟地图开发(CLLocation and MKMapView)
iphone:GPS位置和地图开发(CLLocation and MKMapView)From:http://www.iblue.cc/2011/05/gps%E4%BD%8D%E7%BD%AE%E5%92%8C%E5%9C%B0%E5%9B%BE%E5%BC%80%E5%8F%91/
有了iPhone,意味着我们不会再迷路了。有了iPhone内建的全球定位系统(Global Positioning System,GPS)硬件,以及一些创新的位置和地图软件,iPhone不仅随时知道你位于何处,而且还能显示给你看。
iPhone使用所谓的辅助GPS技术来搞清楚你位于何处。有了内建的GPS接收器,iPhone可以利用通信塔台和Wi-Fi热点之间的三角信息,来增加它发送给应用程序的位置数据的准确性。
在本章中,我们首先介绍Core Location,这是让我们快速且轻松地找到当前位置的框架。然后,我们将介绍Map Kit框架,它使我们能够把流行的Google Maps引擎所支持的地图添加到自己的应用中。我们还将看看如何轻松地执行反向地理编码来获取一个位置的地址,最后,我们把所有这些概念组合到一个地图应用程序中,它能够再现本地Maps应用程序的很多功能。
Map Kit是基于Google Maps引擎的地图框架,允许我们能够向应用程序中添加交互式的地图。地图可以滚动或缩放到地球上的任何区域,并且可以有标签或注释,以便向地图添加需要额外显示的信息。
向应用程序中添加地图:
1)创建一个新的基于视图的应用程序,将其保存为MappingExample。
2)在Groups & Files面板中,展开Targets区域,用鼠标右键点击应用程序目标,并选择Get Info。
3)确保选中General标签,点击Linked Libraries列表底部的Add (+),并且添加MapKit.framework(如图8-6所示)。
4)打开MappingExampleViewController.h文件,导入MapKit框架,并且创建一个实例变量来保存位置管理器(见程序清单8-6)。
向项目中添加Map Kit框架
程序清单8-6
地图应用程序的头文件
5)切换到MappingExampleViewController.m文件,取消对viewDidLoad方法的注释,并且添加如下代码:
map = [[MKMapView alloc]
→initWithFrame:[self.view bounds]];
[self.view addSubview:map];
程序清单8-7给出了更新后的代码。
6)构建并运行应用程序(如图8-7所示)。
这就搞定了,只需要两行代码,就可以有一个地图。你可以在地图上导航,并且使用“收放”手势来实现放大/缩小。
带有全屏MKMapView的应用程序
程序清单8-7
基本的地图应用程序的完整代码
我们现在更新代码,使其变得更有趣些。
在地图上显示你的位置:
1)打开MappingExampleViewController.m,并且更新viewDidLoad方法以便把地图类型设置为卫星视图,并告诉它显示当前的位置(用一个动画的蓝色标记表示):
map.mapType = MKMapTypeS
map.showsUserLocation = YES;
这个标记上外围的圆圈表示位置数据的精度,圆圈越粗,精确度越低:
CLLocationCoordinate2D
coords.latitude = 37.331689;
coords.longitude = -122.030731;
2)接下来,创建一个变量来保存地图中心坐标,在这个例子中,就是Apple公司的总部:
float zoomLevel = 0.018;
MKCoordinateRegion region =
→MKCoordinateRegionMake(coords,
→MKCoordinateSpanMake(zoomLevel,
→zoomLevel));
[map setRegion:[map regionThatFits:
→region] animated:YES];
3)要缩放地图,我们需要创建一个MKCoordinateRegion。
这个结构不仅包含了地图应该居中的坐标,而且还有一个范围,后者由确定地图应该显示多少(以度为单位)的水平距离和垂直距离组成。较大的范围创建一个缩小的视图,较小的范围创建一个放大的视图(如图8-8所示)。程序清单8-8给出了更新后的viewDidLoad方法。
就像iPhone的本地Maps应用程序一样,我们可以使用mapType属性来显示3种可能的地图:
·MKMapTypeStandard—显示一个常规地图,包含街道和路名。如果没有指定的话,这是默认的地图类型。
·MKMapTypeSatellite—显示卫星视图。
·MKMapTypeHybrid—显示两种地图的组合,换句话说,卫星视图上覆盖有路和街道的信息。
·在设置范围值的时候,根据所使用的地图类型,我们可以放大或缩小到一定的程度。例如,MKMapTypeSatellite的地图通常包含更多的细节,并且允许我们比MKTypeStandard放大更多。
在地图上显示当前位置
程序清单8-8
更新设置和显示位置的代码
为了让地图应用更加丰富且有趣,我们通常在地图上附加信息,这也是注释的作用。
Map Kit包含了添加注释的支持,不仅可以是简单的“标签”注释(正如在本地的iPhone Maps应用程序中所见到的),而且可以是包含自己的感受的定制的注释。为地图添加注释,只需要在我们已经完成的基础上再略作改进。
为地图添加注释:
1)打开MappingExampleViewController.h,并向@interface:添加MKMapViewDelegate协议声明:
@interface MappingExampleView
→Controller : UIViewController
→&MKMapViewDelegate&
2)在MappingExampleViewController.m文件中,我们需要创建实现MKAnnotation协议的自己的定制类。至少,这个类必须实现coordinate属性。
3)在viewDidLoad中,设置委托,并且通过添加如下代码创建自己的注释:
customAnnotation *annotation =
→[[customAnnotation alloc]
→initWithCoordinate:coords];
annotation.title = @”The Title”;
annotation.subtitle = @”Subtitle”;
[map addAnnotation:annotation];
[annotation release];
我们已经删除setUserLocation:行,以便可以看到这些注释(否则,位置标记和注释将会出现在地图上的同一位置)。
4)最后,实现mapView:viewForAnnotation:委托方法,把注释显示为一个别针:
MKPinAnnotationView *pinView =
→(MKPinAnnotationView *) [map
→dequeueReusableAnnotationViewWith
→Identifier:annotation.title];
if (pinView == nil)
pinView = [[[MKPinAnnotationView
→alloc] initWithAnnotation:
→annotation reuseIdentifier:
→annotation.title] autorelease];
pinView.annotation =
程序清单8-9给出更新后的代码。
程序清单8-9
添加定制的注释的代码
程序清单8-9
mapView:viewForAnnotation:委托返回了一个MKAnnotationView对象。在这个例子中(如图8-9所示),我们使用MKPinAnnotationView子类,正如其名字所示,它把注释显示为别针。然而,如果希望注释有不同的外观,我们可以返回自己的子类。
我们可以在程序清单8-10中看到这个最简单的示例。我们在MKAnnotationView基类上设置image:属性。这将产生如图8-10所示的地图。
·创建MKAnnotationView对象,以便当注释移动到屏幕外的时候(例如用户缩放或滚动地图)允许地图引擎删除注释,此时会指定一个复用的队列。当注释移动出屏幕的时候,从队列中取出它(也叫做出列)。
·我们也可以通过在定制的MKAnnotation View类中覆盖viewForCalloutAccessory Position:来改变注释的弹出视图(当用户触摸注释的时候出现的视图)的外观。
在地图上显示一个注释
显示图形的一个定制注释
程序清单8-10
把一幅图像显示为注释
添加反向地理编码
Map Kit提供了一种工具,通过查找纬度和经度坐标来获取地址信息,这个过程叫做反向地理编码(reverse geocoding)。我们通过MKReverseGeocoder类及其委托方法来实现这一点。
在前面的例子中,我们手动创建自己的注释,在viewDidLoad中指定坐标标题和子标题。现在,我们将更改自己的代码,以对位置执行反向查找,创建一个注释,在弹出视图中显示地址。
添加反向地理编码:
1)打开MappingExampleViewController.h,并且向@interface添加MKReverseGeocode Delegate协议:
@interface MappingExampleView
→Controller : UIViewController
→&MKMapViewDelegate,
→MKReverseGeocoderDelegate&
我们还需要创建一个实例变量来保存反向地理编码器:
MKReverseGeocoder *
程序清单8-11给出了更新后的代码。
程序清单8-11
更新后添加了反向地址编码的头文件
2)回到MappingExampleViewController.m中,更新viewDidLoad方法:
geo = [[MKReverseGeocoder alloc]
甶nitWithCoordinate:coords];
geo.delegate =
[geo start];
这里,我们创建地理编码器实例,设置委托,并且告诉它开始查找。
3)最后,我们需要实现MKReverseGeocode委托方法,如果找到地址的话,添加一个注释:
[map addAnnotation:placemark];
[geo cancel];
如果失败的话,在控制台上显示一条消息:
NSLog(@”geo error: %@”,error);
[geo cancel];
注意,我们在两种情况下都取消地理编码器。程序清单8-12给出了完整的代码。图8-11显示带有地址的应用程序。
·在reverseGeocoder:didFindPlacemark:委托方法中,我们添加一个MKPlacemark作为注释。这个类包含用来保存位置信息(如城市或州)的属性。和前面我们自己定制的注释类相似,这个类支持MKAnnotation协议,因此它可以显示在地图上。
在注释中显示地址
程序清单8-12
更新后的代码执行一次反向地理查找,并且把地址作为注释添加到地图上
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:&&,&&2号QQ群:,在群里结识技术精英和交流技术^_^
本站联系邮箱:

我要回帖

更多关于 geocode 的文章

 

随机推荐