这篇博客写得很纠结,因为本来快写好了,却在准备发表博客的时候发现两个bug,到处找人请教的过程中又把写了五个小时的博客内容给误删了,所以真可谓好事多磨!可幸的是两个bug被我解决了,所以我打算写出来,让大家看看我实现的思路。因为本人是实实在在的菜鸟,所以有的地方写得很零碎,因为这是我很吃力的地方,有理解不对的地方欢迎大家斧正,谢谢!
接前文(没有看的朋友可以看看我写的高德地图组件在Android的应用以及Android与JavaScript的交互(一),主要讲解Android与JavaScript的交互与高德地图组件的相关API),那么今天我们就接着将怎么把所有的功能全部实现到Android APP上面来呢?整个过程我们分五步来实现:
一、实现地图组件的相关布局;
二、fragment与activity的通信;
三、处理ViewPager预加载与fragment接口的矛盾;
四、重新修改html文件(因为我们需要新增加一个需求:一次传两个地址并返回两个经纬度);
五、解决大boss——bug。
那么接下来,我们首先来实现第一步,就是重新布局,效果如图:
我们用的是ViewPager与fragment,先贴代码,然后再填坑,代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.weixing.gaodemapcomponentdemo.MainActivity" >
<!-- 标题栏 -->
<RelativeLayout
android:id="@+id/tiele"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_alignParentTop="true"
android:background="#292A2D">
<!-- 标题文本 -->
<TextView
android:id="@+id/titleName"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="我的高德地图"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#00FF00"
android:gravity="center"
android:layout_centerInParent="true"/>
<!-- 搜索按钮 -->
<ImageButton
android:id="@+id/StartSerach"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_margin="4dp"
android:src="@drawable/android_search_icon"
android:background="#123456"/>
</RelativeLayout>
<!-- 地图区 -->
<android.support.v4.view.ViewPager
android:id="@+id/vp_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tiele"
android:layout_above="@+id/radiogroup"
android:background="@drawable/map">
</android.support.v4.view.ViewPager>
<!-- 地图组件功能区 -->
<RadioGroup
android:id="@+id/radiogroup"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:background="@drawable/img01_06"
android:orientation="horizontal" >
<!-- 位置标注 -->
<RadioButton
android:id="@+id/rb_pointmarker"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:text="位置标注"
android:button="@null"
android:gravity="center"
android:onClick="checkRadioButton"
/>
<!-- 路线规划 -->
<RadioButton
android:id="@+id/rb_waylayout"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:text="路线规划"
android:button="@null"
android:checked="true"
android:gravity="center"
android:onClick="checkRadioButton" />
<!-- 周边搜索 -->
<RadioButton
android:id="@+id/rb_perimetersearch"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:text="周边搜索"
android:button="@null"
android:gravity="center"
android:onClick="checkRadioButton"/>
<!-- 选址组件 -->
<RadioButton
android:id="@+id/rb_locationcomponent"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="1"
android:text="选址组件"
android:button="@null"
android:gravity="center"
android:onClick="checkRadioButton"/>
</RadioGroup>
<!-- 用于获取位置的webview
activity不会显示,但是一样的需要执行Android与JavaScript的交互 -->
<WebView
android:id="@+id/WebViewgetpoint"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentTop="true"
android:visibility="gone"/>
</RelativeLayout>
布局完成后,来讲一下我们的需求,就是不管我们在哪一个Fragment,只要点一下上方的搜索按钮,当前的fragment的webview就必须实现加载目标url,所以我们就来实现第二步Fragment与Activity的通信,先上代码:
package com.weixing.gaodemapcomponentdemo;
import java.util.ArrayList;
import java.util.List;
import com.weixing.gaodemapcomponentdemo.utils.MyJavaScript;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.RadioButton;
import android.widget.RadioGroup;
public class MainActivity extends FragmentActivity {
/**
* 加载显示地图信息控件的ViewPager
*/
public static ViewPager viewPager;
/**
* 底部功能按钮区
*/
private RadioGroup radioGroup;
/**
* 底部功能按钮
* 选址组件 LocationComponent
* 周边搜索 PerimetersSearch
* 位置标注 PositionMarker
* 路线规划 WayLayout
*/
private RadioButton LocationComponent,PerimetersSearch,PositionMarker,WayLayout;
/**
* ViewPager适配器
*/
private MyViewPagerAdapter pagerAdapter;
/**
* ViewPager数据
*/
private static List<Fragment> fragments;
/**
* 与fragment通信的接口
*/
private static StartSearchCall ClickStartSearchListener;
/**
* 搜索按钮
*/
private ImageButton btn_StartSearch;
/**
* 与底部按钮对应的fragment
* 选址组件fragment fragmentLocationComponent
* 周边搜索fragment fragmentPerimetersSearch
* 位置标注fragment fragmentPositionMarker
* 路线规划fragment fragmentWayLayout
*/
public Fragment fragmentLocationComponent,fragmentPerimetersSearch,
fragmentPositionMarker,fragmentWayLayout;
/**
* 用来获取目标经纬度的webview
* 不会显示出来,隐藏的运行
*/
private WebView webviewPoint;
/**
* 用来加载webview和处理fragment的消息
*/
private Handler handler;
/**
* 实现与fragment通信
* @author Administrator
*
*/
public interface StartSearchCall{
public void ClickListener();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化所有控件
initView();
//给ViewPager、radioButton、搜索按钮设置监听
setListener();
//对activity进行初始化
setWebView();
}
private void setListener() {
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int idx) {
radioGroup.clearCheck();
switch (idx) {
case 0:
radioGroup.check(R.id.rb_pointmarker);
break;
case 1:
radioGroup.check(R.id.rb_waylayout);
break;
case 2:
radioGroup.check(R.id.rb_perimetersearch);
break;
case 3:
radioGroup.check(R.id.rb_locationcomponent);
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
btn_StartSearch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ClickStartSearchListener.ClickListener();
}
});
//默认选择位置组件fragment
LocationComponent.setChecked(true);
}
/**
* RadioButton的响应事件处理
* @param v
*/
public void checkRadioButton(View v){
int id = v.getId();
radioGroup.clearCheck();
switch (id) {
case R.id.rb_pointmarker:
PositionMarker.setChecked(true);
viewPager.setCurrentItem(0,false);
break;
case R.id.rb_waylayout:
WayLayout.setChecked(true);
viewPager.setCurrentItem(1,false);
break;
case R.id.rb_perimetersearch:
PerimetersSearch.setChecked(true);
viewPager.setCurrentItem(2,false);
break;
case R.id.rb_locationcomponent:
LocationComponent.setChecked(true);
viewPager.setCurrentItem(3,false);
break;
}
}
/**
* 与fragment通信的方法
* 加载webview,获取目标地点的经纬度
*/
public void LoadWebViewPoint(){
webviewPoint.loadUrl("file:///android_asset/index.html");
}
/**
* 初始化所有控件以及fragment
*/
private void initView() {
viewPager = (ViewPager) findViewById(R.id.vp_container);
radioGroup = (RadioGroup) findViewById(R.id.radiogroup);
LocationComponent = (RadioButton) findViewById(com.weixing.gaodemapcomponentdemo.R.id.rb_locationcomponent);
PerimetersSearch = (RadioButton) findViewById(com.weixing.gaodemapcomponentdemo.R.id.rb_perimetersearch);
PositionMarker = (RadioButton) findViewById(com.weixing.gaodemapcomponentdemo.R.id.rb_pointmarker);
WayLayout = (RadioButton) findViewById(com.weixing.gaodemapcomponentdemo.R.id.rb_waylayout);
btn_StartSearch = (ImageButton) findViewById(R.id.StartSerach);
fragmentLocationComponent = new com.weixing.gaodemapcomponentdemo.
fragment.LocationComponent(handler);
fragmentPerimetersSearch = new com.weixing.gaodemapcomponentdemo.
fragment.PerimetersSearch(handler);
fragmentPositionMarker = new com.weixing.gaodemapcomponentdemo.
fragment.PositionMarker(handler);
fragmentWayLayout = new com.weixing.gaodemapcomponentdemo.
fragment.WayLayout(handler);
initViewPager();
}
/**
* 初始化ViewPager
*/
private void initViewPager() {
fragments = new ArrayList<Fragment>();
// fragments.add(new com.weixing.gaodemapcomponentdemo.fragment.WayLayout());
// fragments.add(new com.weixing.gaodemapcomponentdemo.fragment.PositionMarker());
// fragments.add(new com.weixing.gaodemapcomponentdemo.fragment.PerimetersSearch());
// fragments.add(new com.weixing.gaodemapcomponentdemo.fragment.LocationComponent());
fragments.add(fragmentPositionMarker);
fragments.add(fragmentWayLayout);
fragments.add(fragmentPerimetersSearch);
fragments.add(fragmentLocationComponent);
pagerAdapter = new MyViewPagerAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(pagerAdapter);
viewPager.setOffscreenPageLimit(0);
viewPager.setCurrentItem(3);
}
/**
* 初始化webview
*/
@SuppressLint("SetJavaScriptEnabled")
private void setWebView() {
Log.e("MyJavaScript", "开始加载webview");
handler = new Handler(getMainLooper());
webviewPoint = (WebView) findViewById(R.id.WebViewgetpoint);
//重要:让webview支持javascript
webviewPoint.getSettings().setJavaScriptEnabled(true);
//重要:添加可以供html中可供javascript调用的接口类
webviewPoint.addJavascriptInterface(new MyJavaScript(this, handler), "myjavascript");
webviewPoint.setWebViewClient(new WebViewClient());
}
/**
* 初始化接口监听,
* 提供给fragment调用
* @param Listener
*/
public static void setStartSearchCall(StartSearchCall Listener){
ClickStartSearchListener = Listener;
}
/**
* 对从JavaScript获取的经纬度进行分发给对应的fragment
* @param point
*/
public static void StartSearch(String point) {
Log.e("MyJavaScript", "MainActivity---》StartSearch");
switch (viewPager.getCurrentItem()) {
case 0:
com.weixing.gaodemapcomponentdemo.fragment.PositionMarker.StartSearch(point);
break;
case 1:
com.weixing.gaodemapcomponentdemo.fragment.WayLayout.StartSearch(point);
break;
case 2:
com.weixing.gaodemapcomponentdemo.fragment.PerimetersSearch.StartSearch(point);
break;
}
}
}
然后仔细分析一下我们实现Fragment与Activity通信的过程:分为四步,首先是写一个接口,然后在接口中定义一个抽象方法,接着第二步就是声明这个接口的对象,作为成员变量,接下来第三步写一个公共方法来初始化这个成员变量,最后第四步就是给标题搜索按钮的监听事件设置我们的接口方法。代码如下:
//第一步
public interface StartSearchCall{
public void ClickListener();
}
//第二步
private static StartSearchCall ClickStartSearchListener;
//第三步
public static void setStartSearchCall(StartSearchCall Listener){
ClickStartSearchListener = Listener;
}
//第四步
btn_StartSearch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ClickStartSearchListener.ClickListener();
}
});
最后,我们接口已经写好了,那么我们在fragment里来实现就OK了!这里我们在Fragment周边搜索里面来研究一下它是怎么实现这个接口,并实现Fragment与Activity的通信?代码如下:
package com.weixing.gaodemapcomponentdemo.fragment;
import com.weixing.gaodemapcomponentdemo.MainActivity;
import com.weixing.gaodemapcomponentdemo.MainActivity.StartSearchCall;
import com.weixing.gaodemapcomponentdemo.R;
import com.weixing.gaodemapcomponentdemo.utils.MyJavaScript;
import com.weixing.gaodemapcomponentdemo.utils.Util;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
/**
* 周边搜索
* @author Administrator
*
*/
public class PerimetersSearch extends Fragment
{
private EditText search_locations,search_searchRadius,search_keywords;
private static WebView WebView;
private static String keywords;
private static int searchRadius;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View layout = inflater.inflate(R.layout.activity_perimeter_search, container,false);
WebView = (android.webkit.WebView) layout.findViewById(R.id.search_WebView);
search_keywords = (EditText) layout.findViewById(R.id.search_keywords);
search_locations = (EditText) layout.findViewById(R.id.search_locations);
search_searchRadius = (EditText) layout.findViewById(R.id.search_searchRadius);
WebView.setBackgroundColor(Color.argb(00, 256, 256, 256));
MainActivity.setStartSearchCall(new StartSearchCall() {
@Override
public void ClickListener() {
try {
keywords = search_keywords.getText().toString();
keywords = Util.getkeywords(keywords);
if(keywords=="1")
{
Toast.makeText(getActivity(), "请检查关键字,支持1—5个关键词,用逗号分隔", Toast.LENGTH_LONG).show();
return;
}
String Radius = search_searchRadius.getText().toString();
if(Radius==null || Radius=="")
{
Toast.makeText(getActivity(), "请检查搜索半径,单位为:米,取值整数范围(1——50000)", Toast.LENGTH_LONG).show();
return;
}
searchRadius = Integer.valueOf(Radius);
if(searchRadius<1 || searchRadius > 50000)
{
Toast.makeText(getActivity(), "请检查搜索半径,单位为:米,取值整数范围(1——50000)", Toast.LENGTH_LONG).show();
return;
}
MyJavaScript.PlaceName = search_locations.getText().toString();
WebView.getSettings().setJavaScriptEnabled(true);
((MainActivity) getActivity()).LoadWebViewPoint();
} catch (Exception e) {
Toast.makeText(getActivity(), "请检查输入信息,错误提示:"+e.toString(),
Toast.LENGTH_LONG).show();
}
}
});
return layout;
}
/**
* 实现与activity通信的方法
* 并加载目标地图信息
* @param PlacePoint
*/
public static void StartSearch(String PlacePoint)
{
Log.e("MyJavaScript", "PerimetersSearch开始工作");
String url = "http://m.amap.com/around/?locations="+PlacePoint+"&keywords="+
keywords+"&defaultIndex=1&defaultView=&searchRadius="+
searchRadius+"&key=035a7298cddc6488961e9cba167c71c3";
// String url = "http://m.amap.com/around/?locations=121.429489,31.153304&keywords=美食,KTV,地铁站,公交站&defaultIndex=3&defaultView=&searchRadius=5000&key=035a7298cddc6488961e9cba167c71c3";
WebView.loadUrl(url);
Log.e("MyJavaScript", "PerimetersSearch--》StartSearch--》"+url);
WebView.setWebViewClient(new WebViewClient());
}
}
这时我们实现了接口,并写了在Fragment内写了对应的实现方法,但是这里有一个矛盾,就是我们第三步内容:处理ViewPager预加载与fragment接口的矛盾。再此之前插一个我遇到的难题,先看图:
首先说一说这个问题是怎么产生的呢?因为我当时把onCreateView()方法里的view是这样写的:View layout = inflater.inflate(R.layout.activity_perimeter_search, container);代码并没有任何异常,但是一直解决不了上面的异常,它也不会指向任何一句代码,后来我访遍高人,按自己的理解尝试修改了很多次都没有解决,反而花了我大半天时间,后来直接将异常的主要内容(the specified child already has a parent. y)拿去问度娘,结果几分钟就解决了,直接把Fragment里面的view修改为:View layout = inflater.inflate(R.layout.activity_perimeter_search, null);就搞定了。所以大家今后遇到问题能找度娘就找度娘,效率蛮高的。接着言归正传,这个问题是怎么产生的呢?通过询问度娘,得到的内容如下:
关于LayoutInflater类inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义
resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。
root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个参数attachToRoot为true,就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附加到resource对象的根布局对象上,也就是布局文件resource的最外层的View上,比如是一个LinearLayout或者其它的Layout对象。
attachToRoot:是否将root附加到布局文件的根视图上
这里我们将Fragment里面的view修改为:View layout = inflater.inflate(R.layout.activity_perimeter_search, null);或者View layout = inflater.inflate(R.layout.activity_perimeter_search, container,false);它返回的就不会是根布局对象了,即都可以解决上述问题了。
OK,接着实现我们的第三步方法处理ViewPager预加载与fragment接口的矛盾。说到这里我们先简单了解一下ViewPager的预加载机制,以及为什么Fragment里的这个接口实现起来有矛盾?
首先,ViewPager加载一个Fragment时,它会加载当前Fragment的左右两个Fragment,就算Fragment为第一个或者最后一个Fragment的时候,也意为着ViewPager至少同时存在两个Fragment,而我们对每个Fragment都写了不同的实现方法,那这里至少同时存在两个不同的实现方法,ViewPager应该采用哪一个方法?经过我的测试,它会选择最后面的一个,这里也就是说Fragment就算实现了接口的方法,但是仍然不受我们的控制!这里我们也有三种方法解决此问题:
1、更换控件,选择viewgroup等view容器控件;(如果添加左右滑动比较麻烦)
2、自定义ViewPager,重写它的预加载机制;(需要源码,而且左右滑动应该没有员ViewPager那么顺畅了)
3、修改我们的实现方法。(这个可以有,毕竟简单而且左右滑动的效果相对来讲也是加分项)
OK,这里我们声明一个Handler对象handler作为成员变量,并重写Fragment的构造方法,传入一个Handler作为参数,并将传入的参数对成员变量handler赋值。然后handler这里不写任何方法,它只用发消息给Activity。然后写一个当前Fragment需要实现的方法,供Activity调用,代码如下:
package com.weixing.gaodemapcomponentdemo.fragment;
import com.weixing.gaodemapcomponentdemo.MainActivity;
import com.weixing.gaodemapcomponentdemo.MainActivity.StartSearchCall;
import com.weixing.gaodemapcomponentdemo.R;
import com.weixing.gaodemapcomponentdemo.utils.MyJavaScript;
import com.weixing.gaodemapcomponentdemo.utils.Util;
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
/**
* 周边搜索
* @author Administrator
*
*/
public class PerimetersSearch extends Fragment
{
private EditText search_locations,search_searchRadius,search_keywords;
private static WebView WebView;
private static String keywords;
private static int searchRadius;
private Handler handler;
public PerimetersSearch (Handler handler)
{
this.handler = handler;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View layout = inflater.inflate(R.layout.activity_perimeter_search, container,false);
WebView = (android.webkit.WebView) layout.findViewById(R.id.search_WebView);
search_keywords = (EditText) layout.findViewById(R.id.search_keywords);
search_locations = (EditText) layout.findViewById(R.id.search_locations);
search_searchRadius = (EditText) layout.findViewById(R.id.search_searchRadius);
WebView.setBackgroundColor(Color.argb(00, 256, 256, 256));
MainActivity.setStartSearchCall(new StartSearchCall() {
@Override
public void ClickListener() {
Message msg = new Message();
msg.what = 250;
handler.handleMessage(msg);
}
});
return layout;
}
/**
* 实现与activity通信发方法
* 用于检查URL的参数并获取目标地的经纬度
*/
@SuppressLint("SetJavaScriptEnabled")
public void PerimetersSearchListener()
{
try {
keywords = search_keywords.getText().toString();
keywords = Util.getkeywords(keywords);
if(keywords=="1")
{
Toast.makeText(getActivity(), "请检查关键字,支持1—5个关键词,用逗号分隔", Toast.LENGTH_LONG).show();
return;
}
String Radius = search_searchRadius.getText().toString();
if(Radius==null || Radius=="")
{
Toast.makeText(getActivity(), "请检查搜索半径,单位为:米,取值整数范围(1——50000)", Toast.LENGTH_LONG).show();
return;
}
searchRadius = Integer.valueOf(Radius);
if(searchRadius<1 || searchRadius > 50000)
{
Toast.makeText(getActivity(), "请检查搜索半径,单位为:米,取值整数范围(1——50000)", Toast.LENGTH_LONG).show();
return;
}
MyJavaScript.PlaceName = search_locations.getText().toString();
WebView.getSettings().setJavaScriptEnabled(true);
((MainActivity) getActivity()).LoadWebViewPoint();
} catch (Exception e) {
Toast.makeText(getActivity(), "请检查输入信息,错误提示:"+e.toString(),
Toast.LENGTH_LONG).show();
}
}
/**
* 实现与activity通信的方法
* 并加载目标地图信息
* @param PlacePoint
*/
public static void StartSearch(String PlacePoint)
{
Log.e("MyJavaScript", "PerimetersSearch开始工作");
String url = "http://m.amap.com/around/?locations="+PlacePoint+"&keywords="+
keywords+"&defaultIndex=1&defaultView=&searchRadius="+
searchRadius+"&key=035a7298cddc6488961e9cba167c71c3";
// String url = "http://m.amap.com/around/?locations=121.429489,31.153304&keywords=美食,KTV,地铁站,公交站&defaultIndex=3&defaultView=&searchRadius=5000&key=035a7298cddc6488961e9cba167c71c3";
WebView.loadUrl(url);
Log.e("MyJavaScript", "PerimetersSearch--》StartSearch--》"+url);
WebView.setWebViewClient(new WebViewClient());
}
}
这里我们看到Fragment传回了一个msg.what = 250的消息给Activity,并写了一个公共方法PerimetersSearchListener()用来与Activity通信,那么我们回头看看Activity这里它如何处理的呢?
/**
* 用来加载webview和处理fragment的消息
*/
private Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg) {
if(msg.what==250)
{
switch (viewPager.getCurrentItem()) {
case 0:
((com.weixing.gaodemapcomponentdemo.fragment.PositionMarker)
fragmentPositionMarker).PositionMarkerListener();
break;
case 1:
((com.weixing.gaodemapcomponentdemo.fragment.WayLayout)
fragmentWayLayout).WayLayoutListener();
break;
case 2:
((com.weixing.gaodemapcomponentdemo.fragment.PerimetersSearch)
fragmentPerimetersSearch).PerimetersSearchListener();
break;
case 3:
((com.weixing.gaodemapcomponentdemo.fragment.LocationComponent)
fragmentLocationComponent).LocationComponentListener();
break;
}
}
super.handleMessage(msg);
}
};
最后我们总结一下处理ViewPager预加载与fragment接口的矛盾的主要思路,Fragment收到开始搜索的命令后不动,直接发消息给Fragment,问:“老大,你到底命令谁去搜索呢?”,然后悄悄的准备公共一个方法准备随时开始工作!然后Activity这边发了搜索的命令后,收到消息说没有指派明确的小弟,小弟懵了,于是马上再次指派对应的小弟开工。OK,这里我们就处理好ViewPager预加载与fragment接口的矛盾了!
这里我们再插一个我遇到的异常: java.lang.ClassCastException。
具体症状为上一次运行还没有任何问题,下一次运行的时候突然报这个异常,并指向代码为控件初始化的findViewById(),经检查也没有发现控件id填写错误,这个问题再次消耗了大半天时间,后来再次经网友点拨,重新对id命名,才解决掉此异常。
OK,接着继续实施我们的第四步:重新修改html文件。原因已经讲了,因为我们路线规划的Fragment里需要得到两个地点的经纬度值,请求两次的话太费时费力了,我们修改html文件后还是一样只用请求一次,提高效率。OK,所以我们这里先看看路线规划的Fragment传给html的参数是怎样构成的?
String textStartPlace = startPlace.getText().toString();
String textDestPlace = destPlace.getText().toString();
MyJavaScript.PlaceName = textStartPlace+","+textDestPlace;
这里我们把两个地点以“,”连接起来传给了MyJavaScript,接下来我们去看看MyJavaScript将这个地点传给html时我们怎么来处理?
var i = -1;
i=PlaceName.indexOf(",");
if(i!=-1)
{
startPlaace = PlaceName.substr(0,PlaceName.indexOf(","));
destPlace = PlaceName.substr(PlaceName.indexOf(",")+1,PlaceName.length);
}else
{
startPlaace = PlaceName;
destPlace = null;
}
我们拿到地点后先判断它是不是两个地点,是的话就将传来的值截取“,”前后的字符串分别给startPlaace和destPlace赋值,否则的话直接对startPlaace赋值,并将destPlace设置为null。接下来在placeSearch()里我们一样需要判断,还是先看代码:
if(startPlaace!=null)
{
MSearch.search(startPlaace); //关键字查询 第一个地点
startPlaace = null;
}else
{
MSearch.search(destPlace); //关键字查询 第二个地点
}
这里我们根据startPlaace是否为null来判断第一次查询还是第二次查询,不管有没有两个地点,startPlaace它都不会为null,于是第一次查询的时候进入方法,我们把startPlaace作为关键字查询MSearch.search(startPlaace),完成后将startPlaace设为null,所以第二次查询的话,它就会进入startPlaace等于null的方法,方法里把第二地点作为关键字查询,MSearch.search(destPlace)。
那我们查询结果这里这么处理这些关系呢?看代码:
if(destPlace==null)//只用拾取一次坐标
{
var point = marker[pointid].getLng()+","+marker[pointid].getLat();
myjavascript.call(point);
}else
{//需要拾取两次坐标
if(returnvalue==null)//第一次拾取坐标
{
var pointone = marker[pointid].getLng()+","+marker[pointid].getLat();
returnvalue = pointone;
marker = new Array();
placeSearch();
}else
{//第二次拾取坐标
var pointtwo = marker[pointid].getLng()+","+marker[pointid].getLat();
returnvalue = returnvalue +":"+ pointtwo;
myjavascript.call(returnvalue);
}
}
这里我们根据destPlace是否为空来判断当前html需要查询一个地点还是两个地点,如果是只用查询一个地点的话,直接将返回结果给myjavascript,否则的话需要查询两个地点,但是这里又有两种情况:查询第一次的结果和第二次的结果,于是我们先声明一个成员变量returnvalue,我们根据returnvalue的值来判断当前是否是第一次拿到查询结果。然后在第一次查询的时候将查询结果赋值给returnvalue,然后把数组marker初始化marker = new Array();,否则第二次查询结果拿的数据还是第一次的数据。并且再次调placeSearch(),这时候startPlaace已经被我们置为空了,所以它会直接执行MSearch.search(destPlace)。然后回到查询结果这里,我们returnvalue已经有内容了,所以到returnvalue不等于空的方法里,然后我们把第一次查询的结果和第二次的查询结果用“:”连接,并返回给myjavascript。
这里html的修改已经完成了,但是我们还是继续看看后面的过程是怎么实现的?
/**
* JS调用Android,传回坐标点
* @param point
*/
@JavascriptInterface
public void call(final String point){
handler.post(new Runnable() {
public void run() {
Log.e("MyJavaScript", point);
MainActivity.StartSearch(point);
}
});
}
myjavascript拿到结果没有直接传给Fragment,而是继续传回给Activity的StartSearch(String point),那么这个方法是干嘛的呢?
/**
* 对从JavaScript获取的经纬度进行分发给对应的fragment
* @param point
*/
public static void StartSearch(String point) {
Log.e("MyJavaScript", "MainActivity---》StartSearch");
switch (viewPager.getCurrentItem()) {
case 0:
com.weixing.gaodemapcomponentdemo.fragment.PositionMarker.StartSearch(point);
break;
case 1:
com.weixing.gaodemapcomponentdemo.fragment.WayLayout.StartSearch(point);
break;
case 2:
com.weixing.gaodemapcomponentdemo.fragment.PerimetersSearch.StartSearch(point);
break;
}
}
这里拿到结果就像快递中转站一样进行分发,但是为什么这里只有三个Fragment?答案还是自己看源码来解决吧!最后还是进入到路线规划的Fragment里面看看最后一步的实现吧!
/**
* 实现与activity通信的方法
* 该方法用于加载地图
* @param PlacePoint
*/
public static void StartSearch(String PlacePoint)
{
String[] point = PlacePoint.split(":");
String startPlace = point[0];
String destPlace = point[1];
String url = "http://m.amap.com/navi/?start="+startPlace+"&dest="
+destPlace +"&destName="+textDestName+"&key=035a7298cddc6488961e9cba167c71c3";
webview.loadUrl(url);
webview.setWebViewClient(new WebViewClient());
Log.e("MyJavaScript", "WayLayout-->StartSearch"+url);
}
接下来,我们进入第五步,解决大boss——bug们,因为这里有两个bug,第一个是位置标注的url显示不了地图,第二个是选址组件显示不了搜索结果。
这里我们讲一下“bug”们发现的过程,当我用代码“webview.loadUrl(url);
webview.setWebViewClient(new WebViewClient());”实现加载地图组件的路线规划、周边搜索、选址组件Fragment均能显示地图,且url用浏览器访问没有任何问题,但是加载位置标注的时候显示不了地图的具体内容(原因不明,高德官方没有邮箱,电话不予受理技术问题‘因为是免费的’,且QQ群回复需要自己去论坛写帖子‘已沉’,微信公众号未回复),所以我一度以为是高德的bug,现在才明白是自己的太low了,找不到足够多的招式来应付这些问题,所以大家今后遇到问题要多想想自己是否用了足够多的方法。第二个“bug”也是类似,我用浏览器访问url也能得到关键字查询结果,但是用webview加载url时显示不了查询结果,后来在知乎、cadn、开源中国、QQ群提问,均没有得到答复,后来有人建议我用ajax来查询是否能拿到查询结果?如果有结果的话,用http请求拿到返回的网页内容,将最后显示拿不到数据内容的部分做一个修改,最后就能实现浏览器访问的效果了,但是这不是我想要的效果,且我之前也没有接触ajax,所以就没有去实现,后来经微信公众号码农搬砖(账号manongbanzhuan)指点,才解决此“bug”。
现在来说一说第一个“bug”的解决,其实非常简单,就加一句代码(这个代码这辈子也要记住了,花了太多时间找它)“webview.setWebChromeClient(new WebChromeClient());”,那么为什么这一句代码能实现显示地图的效果呢?度娘回复是这样的:WebChromeClient会在一些影响浏览器ui交互动作发生时被调用,比如WebView关闭和隐藏、页面加载进展、js确认框和警告框、js加载前、js操作超时、webView获得焦点等等,简单理解就是WebChromeClient可以显示一些复杂内容。
第二个“bug”的解决稍微复杂一些,首先,需要增加两句代码进行设置:“webview.getSettings().setGeolocationEnabled(true);
webview.getSettings().setDomStorageEnabled(true);”第一句代码是启用地理定位;,如果需要存储一些简单的用key/value对即可解决的数据,DOM Storage是非常完美的方案所以第二句代码就是启用DOM存储API。接下来需要重写WebChromeClient类的onGeolocationPermissionsShowPrompt(String origin, Callback callback),设置默认处理获取位置请求。
webview.setWebChromeClient(new WebChromeClient(){//准确的显示弹出的地理位置权限的窗口
@Override
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
//默认处理获取位置请求
callback.invoke(origin, true, false);
super.onGeolocationPermissionsShowPrompt(origin, callback);
}
});
OK,这篇文章就完成了,最后非常遗憾的的又发现一个问题,就是用webview加载位置标注的时候,突然有一个新的问题!!(原本5月21日都还不存在的问题)
webview返回的结果说key值不对,并且将我们原本正确的key值增加了一个“:9”该问题已经在高德地图的服务与支持创建工单,预计礼拜二能将此问题解决,最后上源码,点击下载
最后一个“bug”解决了,代码没有做任何修改,直接显示了,感谢码农搬砖对我的支持,谢谢!本人如有理解不对之处,敬请斧正,谢谢!!
————————————————————————————————————*********************************************************************************************
优化源码后,主要是对之前的通信不够规范进行修改,如今这种方式更容易维护,易于理解。
点击下载