高德地图组件在Android的应用以及Android与JavaScript的交互(二)

这篇博客写得很纠结,因为本来快写好了,却在准备发表博客的时候发现两个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日都还不存在的问题)key值被修改
webview返回的结果说key值不对,并且将我们原本正确的key值增加了一个“:9”该问题已经在高德地图的服务与支持创建工单,预计礼拜二能将此问题解决,最后上源码,点击下载
最后一个“bug”解决了,代码没有做任何修改,直接显示了,感谢码农搬砖对我的支持,谢谢!本人如有理解不对之处,敬请斧正,谢谢!!
位置标注
选址组件
周边搜索
路线规划

————————————————————————————————————*********************************************************************************************
优化源码后,主要是对之前的通信不够规范进行修改,如今这种方式更容易维护,易于理解。
点击下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值