⑤ 公交路线规划
还有一个公交路线规划,公交的规划其实还包括了步行,不过步行是一点点。下面首先是改变里面的值,增加一个出行方式。
然后找到startRouteSearch方法,增加如下方式:
case 3://公交
//构建驾车路线搜索对象 第三个参数表示公交查询城市区号,第四个参数表示是否计算夜班车,0表示不计算,1表示计算
RouteSearch.BusRouteQuery busQuery = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BusLeaseWalk, "0755",0);
//公交规划路径计算
routeSearch.calculateBusRouteAsyn(busQuery);
break;
这里注意一点,那就是城市的区号,这里我填的是0755,表示深圳。你可以通过城市区号查询去查看你所在地的城市区号,当然如果你不想去查询也可以,你可以直接用中文来解决,比如把0755改成深圳,也是可以的。如果你觉得这样也比较麻烦的话,那么你可以定义一个成员变量。
//城市
private String city;
然后在onLocationChanged中赋值。
city = aMapLocation.getCity();
最后在替换,这样的话就会以你当前所在城市为准。
RouteSearch.BusRouteQuery busQuery = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BusLeaseWalk, city, 0);
那么下面同样要绘制公交的图层,在overlay包下新建一个BusRouteOverlay类,代码如下:(来源于SDK)
package com.llw.mapdemo.overlay;
import android.content.Context;
import com.amap.api.maps.AMap;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.PolylineOptions;
import com.amap.api.services.busline.BusStationItem;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.route.BusPath;
import com.amap.api.services.route.BusStep;
import com.amap.api.services.route.Doorway;
import com.amap.api.services.route.RailwayStationItem;
import com.amap.api.services.route.RouteBusLineItem;
import com.amap.api.services.route.RouteBusWalkItem;
import com.amap.api.services.route.RouteRailwayItem;
import com.amap.api.services.route.TaxiItem;
import com.amap.api.services.route.WalkStep;
import com.llw.mapdemo.util.MapUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 公交路线图层类。在高德地图API里,如果需要显示公交路线,可以用此类来创建公交路线图层。如不满足需求,也可以自己创建自定义的公交路线图层。
* @since V2.1.0
*/
public class BusRouteOverlay extends RouteOverlay {
private BusPath busPath;
private LatLng latLng;
/**
* 通过此构造函数创建公交路线图层。
* @param context 当前activity。
* @param amap 地图对象。
* @param path 公交路径规划的一个路段。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类<strong> <a href="../../../../../../Search/com/amap/api/services/route/BusPath.html" title="com.amap.api.services.route中的类">BusPath</a></strong>。
* @param start 起点坐标。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类 <strong><a href="../../../../../../Search/com/amap/api/services/core/LatLonPoint.html" title="com.amap.api.services.core中的类">LatLonPoint</a></strong>。
* @param end 终点坐标。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类 <strong><a href="../../../../../../Search/com/amap/api/services/core/LatLonPoint.html" title="com.amap.api.services.core中的类">LatLonPoint</a></strong>。
* @since V2.1.0
*/
public BusRouteOverlay(Context context, AMap amap, BusPath path,
LatLonPoint start, LatLonPoint end) {
super(context);
this.busPath = path;
startPoint = MapUtil.convertToLatLng(start);
endPoint = MapUtil.convertToLatLng(end);
mAMap = amap;
}
/**
* 添加公交路线到地图上。
* @since V2.1.0
*/
public void addToMap() {
/**
* 绘制节点和线<br>
* 细节情况较多<br>
* 两个step之间,用step和step1区分<br>
* 1.一个step内可能有步行和公交,然后有可能他们之间连接有断开<br>
* 2.step的公交和step1的步行,有可能连接有断开<br>
* 3.step和step1之间是公交换乘,且没有步行,需要把step的终点和step1的起点连起来<br>
* 4.公交最后一站和终点间有步行,加入步行线路,还会有一些步行marker<br>
* 5.公交最后一站和终点间无步行,之间连起来<br>
*/
try {
List<BusStep> busSteps = busPath.getSteps();
for (int i = 0; i < busSteps.size(); i++) {
BusStep busStep = busSteps.get(i);
if (i < busSteps.size() - 1) {
BusStep busStep1 = busSteps.get(i + 1);// 取得当前下一个BusStep对象
// 假如步行和公交之间连接有断开,就把步行最后一个经纬度点和公交第一个经纬度点连接起来,避免断线问题
if (busStep.getWalk() != null
&& busStep.getBusLine() != null) {
checkWalkToBusline(busStep);
}
// 假如公交和步行之间连接有断开,就把上一公交经纬度点和下一步行第一个经纬度点连接起来,避免断线问题
if (busStep.getBusLine() != null
&& busStep1.getWalk() != null
&& busStep1.getWalk().getSteps().size() > 0) {
checkBusLineToNextWalk(busStep, busStep1);
}
// 假如两个公交换乘中间没有步行,就把上一公交经纬度点和下一步公交第一个经纬度点连接起来,避免断线问题
if (busStep.getBusLine() != null
&& busStep1.getWalk() == null
&& busStep1.getBusLine() != null) {
checkBusEndToNextBusStart(busStep, busStep1);
}
// 和上面的很类似
if (busStep.getBusLine() != null
&& busStep1.getWalk() == null
&& busStep1.getBusLine() != null) {
checkBusToNextBusNoWalk(busStep, busStep1);
}
if (busStep.getBusLine() != null
&& busStep1.getRailway() != null ) {
checkBusLineToNextRailway(busStep, busStep1);
}
if (busStep1.getWalk() != null &&
busStep1.getWalk().getSteps().size() > 0 &&
busStep.getRailway() != null) {
checkRailwayToNextWalk(busStep, busStep1);
}
if ( busStep1.getRailway() != null &&
busStep.getRailway() != null) {
checkRailwayToNextRailway(busStep, busStep1);
}
if (busStep.getRailway() != null &&
busStep1.getTaxi() != null ){
checkRailwayToNextTaxi(busStep, busStep1);
}
}
if (busStep.getWalk() != null
&& busStep.getWalk().getSteps().size() > 0) {
addWalkSteps(busStep);
} else {
if (busStep.getBusLine() == null && busStep.getRailway() == null && busStep.getTaxi() == null) {
addWalkPolyline(latLng, endPoint);
}
}
if (busStep.getBusLine() != null) {
RouteBusLineItem routeBusLineItem = busStep.getBusLine();
addBusLineSteps(routeBusLineItem);
addBusStationMarkers(routeBusLineItem);
if (i == busSteps.size() - 1) {
addWalkPolyline(MapUtil.convertToLatLng(getLastBuslinePoint(busStep)), endPoint);
}
}
if (busStep.getRailway() != null) {
addRailwayStep(busStep.getRailway());
addRailwayMarkers(busStep.getRailway());
if (i == busSteps.size() - 1) {
addWalkPolyline(MapUtil.convertToLatLng(busStep.getRailway().getArrivalstop().getLocation()), endPoint);
}
}
if (busStep.getTaxi() != null) {
addTaxiStep(busStep.getTaxi());
addTaxiMarkers(busStep.getTaxi());
}
}
addStartAndEndMarker();
} catch (Throwable e) {
e.printStackTrace();
}
}
private void checkRailwayToNextTaxi(BusStep busStep, BusStep busStep1) {
LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
LatLonPoint taxiFirstPoint = busStep1.getTaxi().getOrigin();
if (!railwayLastPoint.equals(taxiFirstPoint)) {
addWalkPolyLineByLatLonPoints(railwayLastPoint, taxiFirstPoint);
}
}
private void checkRailwayToNextRailway(BusStep busStep, BusStep busStep1) {
LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation();
if (!railwayLastPoint.equals(railwayFirstPoint)) {
addWalkPolyLineByLatLonPoints(railwayLastPoint, railwayFirstPoint);
}
}
private void checkBusLineToNextRailway(BusStep busStep, BusStep busStep1) {
LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation();
if (!busLastPoint.equals(railwayFirstPoint)) {
addWalkPolyLineByLatLonPoints(busLastPoint, railwayFirstPoint);
}
}
private void checkRailwayToNextWalk(BusStep busStep, BusStep busStep1) {
LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation();
LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1);
if (!railwayLastPoint.equals(walkFirstPoint)) {
addWalkPolyLineByLatLonPoints(railwayLastPoint, walkFirstPoint);
}
}
private void addRailwayStep(RouteRailwayItem railway) {
List<LatLng> railwaylistpoint = new ArrayList<LatLng>();
List<RailwayStationItem> railwayStationItems = new ArrayList<RailwayStationItem>();
railwayStationItems.add(railway.getDeparturestop());
railwayStationItems.addAll(railway.getViastops());
railwayStationItems.add(railway.getArrivalstop());
for (int i = 0; i < railwayStationItems.size(); i++) {
railwaylistpoint.add(MapUtil.convertToLatLng(railwayStationItems.get(i).getLocation()));
}
addRailwayPolyline(railwaylistpoint);
}
private void addTaxiStep(TaxiItem taxi){
addPolyLine(new PolylineOptions().width(getRouteWidth())
.color(getBusColor())
.add(MapUtil.convertToLatLng(taxi.getOrigin()))
.add(MapUtil.convertToLatLng(taxi.getDestination())));
}
/**
* @param busStep
*/
private void addWalkSteps(BusStep busStep) {
RouteBusWalkItem routeBusWalkItem = busStep.getWalk();
List<WalkStep> walkSteps = routeBusWalkItem.getSteps();
for (int j = 0; j < walkSteps.size(); j++) {
WalkStep walkStep = walkSteps.get(j);
if (j == 0) {
LatLng latLng = MapUtil.convertToLatLng(walkStep
.getPolyline().get(0));
String road = walkStep.getRoad();// 道路名字
String instruction = getWalkSnippet(walkSteps);// 步行导航信息
addWalkStationMarkers(latLng, road, instruction);
}
List<LatLng> listWalkPolyline = MapUtil
.convertArrList(walkStep.getPolyline());
this.latLng = listWalkPolyline.get(listWalkPolyline.size() - 1);
addWalkPolyline(listWalkPolyline);
// 假如步行前一段的终点和下的起点有断开,断画直线连接起来,避免断线问题
if (j < walkSteps.size() - 1) {
LatLng lastLatLng = listWalkPolyline.get(listWalkPolyline
.size() - 1);
LatLng firstlatLatLng = MapUtil
.convertToLatLng(walkSteps.get(j + 1).getPolyline()
.get(0));
if (!(lastLatLng.equals(firstlatLatLng))) {
addWalkPolyline(lastLatLng, firstlatLatLng);
}
}
}
}
/**
* 添加一系列的bus PolyLine
*
* @param routeBusLineItem
*/
private void addBusLineSteps(RouteBusLineItem routeBusLineItem) {
addBusLineSteps(routeBusLineItem.getPolyline());
}
private void addBusLineSteps(List<LatLonPoint> listPoints) {
if (listPoints.size() < 1) {
return;
}
addPolyLine(new PolylineOptions().width(getRouteWidth())
.color(getBusColor())
.addAll(MapUtil.convertArrList(listPoints)));
}
/**
* @param latLng
* marker
* @param title
* @param snippet
*/
private void addWalkStationMarkers(LatLng latLng, String title,
String snippet) {
addStationMarker(new MarkerOptions().position(latLng).title(title)
.snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
.icon(getWalkBitmapDescriptor()));
}
/**
* @param routeBusLineItem
*/
private void addBusStationMarkers(RouteBusLineItem routeBusLineItem) {
BusStationItem startBusStation = routeBusLineItem
.getDepartureBusStation();
LatLng position = MapUtil.convertToLatLng(startBusStation
.getLatLonPoint());
String title = routeBusLineItem.getBusLineName();
String snippet = getBusSnippet(routeBusLineItem);
addStationMarker(new MarkerOptions().position(position).title(title)
.snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
.icon(getBusBitmapDescriptor()));
}
private void addTaxiMarkers(TaxiItem taxiItem) {
LatLng position = MapUtil.convertToLatLng(taxiItem
.getOrigin());
String title = taxiItem.getmSname()+"打车";
String snippet = "到终点";
addStationMarker(new MarkerOptions().position(position).title(title)
.snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
.icon(getDriveBitmapDescriptor()));
}
private void addRailwayMarkers(RouteRailwayItem railway) {
LatLng Departureposition = MapUtil.convertToLatLng(railway
.getDeparturestop().getLocation());
String Departuretitle = railway.getDeparturestop().getName()+"上车";
String Departuresnippet = railway.getName();
addStationMarker(new MarkerOptions().position(Departureposition).title(Departuretitle)
.snippet(Departuresnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
.icon(getBusBitmapDescriptor()));
LatLng Arrivalposition = MapUtil.convertToLatLng(railway
.getArrivalstop().getLocation());
String Arrivaltitle = railway.getArrivalstop().getName()+"下车";
String Arrivalsnippet = railway.getName();
addStationMarker(new MarkerOptions().position(Arrivalposition).title(Arrivaltitle)
.snippet(Arrivalsnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible)
.icon(getBusBitmapDescriptor()));
}
/**
* 如果换乘没有步行 检查bus最后一点和下一个step的bus起点是否一致
*
* @param busStep
* @param busStep1
*/
private void checkBusToNextBusNoWalk(BusStep busStep, BusStep busStep1) {
LatLng endbusLatLng = MapUtil
.convertToLatLng(getLastBuslinePoint(busStep));
LatLng startbusLatLng = MapUtil
.convertToLatLng(getFirstBuslinePoint(busStep1));
if (startbusLatLng.latitude - endbusLatLng.latitude > 0.0001
|| startbusLatLng.longitude - endbusLatLng.longitude > 0.0001) {
drawLineArrow(endbusLatLng, startbusLatLng);// 断线用带箭头的直线连?
}
}
/**
*
* checkBusToNextBusNoWalk 和这个类似
*
* @param busStep
* @param busStep1
*/
private void checkBusEndToNextBusStart(BusStep busStep, BusStep busStep1) {
LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
LatLng endbusLatLng = MapUtil.convertToLatLng(busLastPoint);
LatLonPoint busFirstPoint = getFirstBuslinePoint(busStep1);
LatLng startbusLatLng = MapUtil.convertToLatLng(busFirstPoint);
if (!endbusLatLng.equals(startbusLatLng)) {
drawLineArrow(endbusLatLng, startbusLatLng);//
}
}
/**
* 检查bus最后一步和下一各step的步行起点是否一致
*
* @param busStep
* @param busStep1
*/
private void checkBusLineToNextWalk(BusStep busStep, BusStep busStep1) {
LatLonPoint busLastPoint = getLastBuslinePoint(busStep);
LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1);
if (!busLastPoint.equals(walkFirstPoint)) {
addWalkPolyLineByLatLonPoints(busLastPoint, walkFirstPoint);
}
}
/**
* 检查 步行最后一点 和 bus的起点 是否一致
*
* @param busStep
*/
private void checkWalkToBusline(BusStep busStep) {
LatLonPoint walkLastPoint = getLastWalkPoint(busStep);
LatLonPoint buslineFirstPoint = getFirstBuslinePoint(busStep);
if (!walkLastPoint.equals(buslineFirstPoint)) {
addWalkPolyLineByLatLonPoints(walkLastPoint, buslineFirstPoint);
}
}
/**
* @param busStep1
* @return
*/
private LatLonPoint getFirstWalkPoint(BusStep busStep1) {
return busStep1.getWalk().getSteps().get(0).getPolyline().get(0);
}
/**
*
*/
private void addWalkPolyLineByLatLonPoints(LatLonPoint pointFrom,
LatLonPoint pointTo) {
LatLng latLngFrom = MapUtil.convertToLatLng(pointFrom);
LatLng latLngTo = MapUtil.convertToLatLng(pointTo);
addWalkPolyline(latLngFrom, latLngTo);
}
/**
* @param latLngFrom
* @param latLngTo
* @return
*/
private void addWalkPolyline(LatLng latLngFrom, LatLng latLngTo) {
addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo)
.width(getRouteWidth()).color(getWalkColor()).setDottedLine(true));
}
/**
* @param listWalkPolyline
*/
private void addWalkPolyline(List<LatLng> listWalkPolyline) {
addPolyLine(new PolylineOptions().addAll(listWalkPolyline)
.color(getWalkColor()).width(getRouteWidth()).setDottedLine(true));
}
private void addRailwayPolyline(List<LatLng> listPolyline) {
addPolyLine(new PolylineOptions().addAll(listPolyline)
.color(getDriveColor()).width(getRouteWidth()));
}
private String getWalkSnippet(List<WalkStep> walkSteps) {
float disNum = 0;
for (WalkStep step : walkSteps) {
disNum += step.getDistance();
}
return "\u6B65\u884C" + disNum + "\u7C73";
}
public void drawLineArrow(LatLng latLngFrom, LatLng latLngTo) {
addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo).width(3)
.color(getBusColor()).width(getRouteWidth()));// 绘制直线
}
private String getBusSnippet(RouteBusLineItem routeBusLineItem) {
return "("
+ routeBusLineItem.getDepartureBusStation().getBusStationName()
+ "-->"
+ routeBusLineItem.getArrivalBusStation().getBusStationName()
+ ") \u7ECF\u8FC7" + (routeBusLineItem.getPassStationNum() + 1)
+ "\u7AD9";
}
/**
* @param busStep
* @return
*/
private LatLonPoint getLastWalkPoint(BusStep busStep) {
List<WalkStep> walkSteps = busStep.getWalk().getSteps();
WalkStep walkStep = walkSteps.get(walkSteps.size() - 1);
List<LatLonPoint> lonPoints = walkStep.getPolyline();
return lonPoints.get(lonPoints.size() - 1);
}
private LatLonPoint getExitPoint(BusStep busStep) {
Doorway doorway = busStep.getExit();
if (doorway == null) {
return null;
}
return doorway.getLatLonPoint();
}
private LatLonPoint getLastBuslinePoint(BusStep busStep) {
List<LatLonPoint> lonPoints = busStep.getBusLine().getPolyline();
return lonPoints.get(lonPoints.size() - 1);
}
private LatLonPoint getEntrancePoint(BusStep busStep) {
Doorway doorway = busStep.getEntrance();
if (doorway == null) {
return null;
}
return doorway.getLatLonPoint();
}
private LatLonPoint getFirstBuslinePoint(BusStep busStep) {
return busStep.getBusLine().getPolyline().get(0);
}
}
最后回到RouteActivity。
/**
* 公交规划路径结果
*
* @param busRouteResult 结果
* @param code 结果码
*/
@Override
public void onBusRouteSearched(BusRouteResult busRouteResult, int code) {
aMap.clear();// 清理地图上的所有覆盖物
if (code == AMapException.CODE_AMAP_SUCCESS) {
if (busRouteResult != null && busRouteResult.getPaths() != null) {
if (busRouteResult.getPaths().size() > 0) {
final BusPath busPath = busRouteResult.getPaths().get(0);
if (busPath == null) {
return;
}
BusRouteOverlay busRouteOverlay = new BusRouteOverlay(
this, aMap, busPath,
busRouteResult.getStartPos(),
busRouteResult.getTargetPos());
busRouteOverlay.removeFromMap();
busRouteOverlay.addToMap();
busRouteOverlay.zoomToSpan();
int dis = (int) busPath.getDistance();
int dur = (int) busPath.getDuration();
String des = MapUtil.getFriendlyTime(dur) + "(" + MapUtil.getFriendlyLength(dis) + ")";
Log.d(TAG, des);
} else if (busRouteResult.getPaths() == null) {
showMsg("对不起,没有搜索到相关数据!");
}
} else {
showMsg("对不起,没有搜索到相关数据!");
}
} else {
showMsg("错误码;" + code);
}
}
然后来看看运行的效果。
公交路线规划就写完了。
⑥ 步行路线详情
上面我们写好了四种出行方式的路线规划,并且一一做了测试,但是还有一个问题,就是不知道具体的路线信息,称之为路线规划详情。因此下面来介绍这个详情信息是怎么样显示的。因为这个路线详情涉及的内容比较多,所以我这里一个一个来写,首先是步行路线详情。
打开activity_route.xml,在里面增加如下布局代码:
<!--底部规划时间详情-->
<RelativeLayout
android:id="@+id/bottom_layout"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:background="#FFF"
android:orientation="horizontal"
android:padding="5dp"
android:visibility="gone">
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:paddingEnd="12dp"
android:singleLine="true"
android:textColor="#333333"
android:textSize="16sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginEnd="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="详情"
android:textColor="#4c90f9"
android:textSize="14sp" />
<Button
android:layout_width="7dp"
android:layout_height="13dp"
android:layout_marginStart="4dp"
android:background="@mipmap/arrow_right_blue"
android:gravity="center_vertical" />
</LinearLayout>
</RelativeLayout>
添加位置如下:
没有的图标去我的源码里面找,或者去高德的SDKdemo里面去找。下面进入到RouteActivity,
//路线规划详情
private RelativeLayout bottomLayout;
//花费时间
private TextView tvTime;
然后绑定控件iD,在initTravelMode方法中。
下面要显示步行花费的时间,找到onWalkRouteSearched方法,在里面增加如下代码:
//显示步行花费时间
tvTime.setText(des);
bottomLayout.setVisibility(View.VISIBLE);
//跳转到路线详情页面
bottomLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(RouteActivity.this,
RouteDetailActivity.class);
intent.putExtra("type",0);
intent.putExtra("path", walkPath);
startActivity(intent);
}
});
添加位置如下图:
这里我把之前的日志打印去掉了,换成文本显示,你会看到有一个RouteDetailActivity.class,这是路线详情页面,然后通过Intent给它传递数据,type用于区分当前的出行类型,path用于显示详情信息,目前还没有这个Activity,所以你需要创建一个。
创建好之后,在刚才报错的地方导一下包就可以了。那么我们先不去写这个详情页面,先运行一下看看。
嗯,还是很不错的,那么下面来写这个详情页面,我还是比较注重这个外观的,希望能有一个好的用户体验,因此我打算美化一下这个详情页面,首先打开styles.xml,增加如下代码:
<!--详情页面主题-->
<style name="DetailTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#FFF</item>
<item name="colorPrimaryDark">#FFF</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
如下图所示:
你可以看到这里面还有一个主题AppTheme,下面进入到AndroidManifest.xml中,配置这个刚才创建的DetailTheme样式。
从这个图你可以看到很多有用的信息,首先是程序默认使用的是AppTheme主题,当你创建的Activity没有指定主题样式时,则都是AppTheme,大部分人后面都会修改主题样式,因为原生的太丑了,而我给RouteDetailActivity指定了DetailTheme主题,那么就会使用指定的主题。
下面进入activity_route_detail.xml页面,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#efefef"
android:fitsSystemWindows="true"
android:orientation="vertical">
<!--标题-->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#FFF"
app:navigationIcon="@drawable/ic_black_24dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
</androidx.appcompat.widget.Toolbar>
<!--分隔线-->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#000" />
<!--路线详情-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_time"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#FFF"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="5dp"
android:paddingStart="12dp"
android:textColor="#333333"
android:textSize="16sp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#e0e0e0" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:divider="#00000000"
android:fadingEdge="none"
android:fadingEdgeLength="0dp"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false"
android:listSelector="#00000000"/>
</LinearLayout>
</LinearLayout>
下面进入到RouteDetailActivity,创建成员变量
private Toolbar toolbar;
private TextView tvTitle, tvTime;
private RecyclerView rv;//列表
然后新增一个initView方法,代码如下:
/**
* 初始化
*/
private void initView() {
toolbar = findViewById(R.id.toolbar);
tvTitle = findViewById(R.id.tv_title);
tvTime = findViewById(R.id.tv_time);
rv = findViewById(R.id.rv);
//高亮状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
Intent intent = getIntent();
if (intent == null) {
return;
}
switch (intent.getIntExtra("type", 0)) {
case 0://步行
break;
case 1://骑行
break;
case 2://驾车
break;
case 3://公交
break;
default:
break;
}
}
这里就是根绝传递进来的type做判断处理,下面就是显示详情数据了,这里我用的是RecyclerView,因此可以使用一个帮助框架。打开项目工程的build.gradle,添加如下库:
maven { url "https://jitpack.io" }
新版Android Studio在settings.gradle中添加,如下图所示:
然后打开app下的build.gradle,在dependencies闭包里面添加如下依赖:
//RecyclerView的好搭档
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
然后点击右上角的Sync Now进行同步,下面创建列表的item布局,在layout下新建一个item_segment.xml文件,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF" >
<ImageView
android:id="@+id/bus_seg_split_line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="50dp"
android:layout_marginStart="50dp"
android:background="#e0e0e0"/>
<RelativeLayout
android:id="@+id/bus_route_direction"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="11dp"
android:layout_marginStart="11dp" >
<ImageView
android:id="@+id/bus_dir_icon"
android:layout_width="22dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/dir_start" />
<ImageView
android:id="@+id/bus_dir_icon_up"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_above="@id/bus_dir_icon"
android:layout_centerHorizontal="true"
android:background="#b6b6b6"
android:visibility="gone"/>
<ImageView
android:id="@+id/bus_dir_icon_down"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_below="@id/bus_dir_icon"
android:layout_centerHorizontal="true"
android:background="#b6b6b6"
android:visibility="gone"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/bus_item"
android:layout_width="match_parent"
android:layout_height="44dp" >
<RelativeLayout
android:id="@+id/stationinfo"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true">
<ImageView
android:id="@+id/bus_expand_image"
android:layout_width="25dp"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"
android:clickable="true"
android:scaleType="centerInside"
android:src="@drawable/down"
android:visibility="gone" />
<TextView
android:id="@+id/bus_station_num"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/bus_expand_image"
android:layout_toStartOf="@id/bus_expand_image"
android:gravity="center_vertical"
android:textColor="#4c90f9"
android:textSize="12sp"
android:visibility="gone" >
</TextView>
</RelativeLayout>
<TextView
android:id="@+id/bus_line_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="50dp"
android:layout_marginStart="50dp"
android:layout_marginRight="70dp"
android:layout_marginEnd="70dp"
android:textColor="#333333"
android:text="出发"
android:textSize="14sp" />
</RelativeLayout>
<LinearLayout
android:id="@+id/expand_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/bus_item"
android:orientation="vertical">
</LinearLayout>
</RelativeLayout>
下面就来写列表适配器了,这里面会用到我刚才添加的那个依赖框架,它可以让你的RecycleView使用起来很简洁,在com.llw.mapdemo下新建一个adapter包,用于放置所有的适配器,在这个包下新建一个WalkSegmentListAdapter类,里面的代码如下:
package com.llw.mapdemo.adapter;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.amap.api.services.route.WalkStep;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.llw.mapdemo.R;
import com.llw.mapdemo.util.MapUtil;
import java.util.List;
/**
* 步行段列表适配器
*
* @author llw
* @date 2021/2/23 9:31
*/
public class WalkSegmentListAdapter extends BaseQuickAdapter<WalkStep, BaseViewHolder> {
private List<WalkStep> mItemList;
public WalkSegmentListAdapter(int layoutResId, @Nullable List<WalkStep> data) {
super(layoutResId, data);
mItemList = data;
}
@Override
protected void convert(BaseViewHolder helper, WalkStep item) {
TextView lineName = helper.getView(R.id.bus_line_name);
ImageView dirIcon = helper.getView(R.id.bus_dir_icon);
ImageView dirUp = helper.getView(R.id.bus_dir_icon_up);
ImageView dirDown = helper.getView(R.id.bus_dir_icon_down);
ImageView splitLine = helper.getView(R.id.bus_seg_split_line);
int position = getItemPosition(item);
if (position == 0) {
dirIcon.setImageResource(R.drawable.dir_start);
lineName.setText("出发");
dirUp.setVisibility(View.INVISIBLE);
dirDown.setVisibility(View.VISIBLE);
splitLine.setVisibility(View.INVISIBLE);
} else if (position == mItemList.size() - 1) {
dirIcon.setImageResource(R.drawable.dir_end);
lineName.setText("到达终点");
dirUp.setVisibility(View.VISIBLE);
dirDown.setVisibility(View.INVISIBLE);
} else {
splitLine.setVisibility(View.VISIBLE);
dirUp.setVisibility(View.VISIBLE);
dirDown.setVisibility(View.VISIBLE);
String actionName = item.getAction();
int resID = MapUtil.getWalkActionID(actionName);
dirIcon.setImageResource(resID);
lineName.setText(item.getInstruction());
}
}
}
当你改完包名,放置了图标之后,你会发现有一个地方报错。
这是因为还没有这个方法的,所以我们需要在MapUtil中添加这个方法。
public static int getWalkActionID(String actionName) {
if (actionName == null || actionName.equals("")) {
return R.drawable.dir13;
}
if ("左转".equals(actionName)) {
return R.drawable.dir2;
}
if ("右转".equals(actionName)) {
return R.drawable.dir1;
}
if ("向左前方".equals(actionName) || "靠左".equals(actionName) || actionName.contains("向左前方")) {
return R.drawable.dir6;
}
if ("向右前方".equals(actionName) || "靠右".equals(actionName) || actionName.contains("向右前方")) {
return R.drawable.dir5;
}
if ("向左后方".equals(actionName)|| actionName.contains("向左后方")) {
return R.drawable.dir7;
}
if ("向右后方".equals(actionName)|| actionName.contains("向右后方")) {
return R.drawable.dir8;
}
if ("直行".equals(actionName)) {
return R.drawable.dir3;
}
if ("通过人行横道".equals(actionName)) {
return R.drawable.dir9;
}
if ("通过过街天桥".equals(actionName)) {
return R.drawable.dir11;
}
if ("通过地下通道".equals(actionName)) {
return R.drawable.dir10;
}
return R.drawable.dir13;
}
就是通过你的行动ID来设置图标,很简单的方法。那么现在你的这个适配器就没有问题了,回到RouteDetailActivity。新写一个walkDetail方法。
/**
* 步行详情
* @param intent
*/
private void walkDetail(Intent intent) {
tvTitle.setText("步行路线规划");
WalkPath walkPath = intent.getParcelableExtra("path");
String dur = MapUtil.getFriendlyTime((int) walkPath.getDuration());
String dis = MapUtil.getFriendlyLength((int) walkPath.getDistance());
tvTime.setText(dur + "(" + dis + ")");
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new WalkSegmentListAdapter(R.layout.item_segment, walkPath.getSteps()));
}
那么现在要做的就是方法的调用了,在步行中调用walkDetail方法。
当然还要在onCreate中调用initView()。
下面运行一下:
⑦ 骑行路线详情
在RouteActivity中找到onRideRouteSearched方法,方法里面添加如下代码:
tvTime.setText(des);
bottomLayout.setVisibility(View.VISIBLE);
bottomLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(RouteActivity.this,
RouteDetailActivity.class);
intent.putExtra("type",1);
intent.putExtra("path", ridePath);
startActivity(intent);
}
});
然后在adapter包下新增一个RideSegmentListAdapter类,里面的代码如下;
package com.llw.mapdemo.adapter;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.amap.api.services.route.RideStep;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.llw.mapdemo.R;
import com.llw.mapdemo.util.MapUtil;
import java.util.List;
/**
* 骑行段列表适配器
*
* @author llw
* @date 2021/2/23 10:25
*/
public class RideSegmentListAdapter extends BaseQuickAdapter<RideStep, BaseViewHolder> {
private List<RideStep> mItemList;
public RideSegmentListAdapter(int layoutResId, @Nullable List<RideStep> data) {
super(layoutResId, data);
mItemList = data;
}
@Override
protected void convert(BaseViewHolder helper, RideStep item) {
TextView lineName = helper.getView(R.id.bus_line_name);
ImageView dirIcon = helper.getView(R.id.bus_dir_icon);
ImageView dirUp = helper.getView(R.id.bus_dir_icon_up);
ImageView dirDown = helper.getView(R.id.bus_dir_icon_down);
ImageView splitLine = helper.getView(R.id.bus_seg_split_line);
int position = getItemPosition(item);
if (position == 0) {
dirIcon.setImageResource(R.drawable.dir_start);
lineName.setText("出发");
dirUp.setVisibility(View.INVISIBLE);
dirDown.setVisibility(View.VISIBLE);
splitLine.setVisibility(View.INVISIBLE);
} else if (position == mItemList.size() - 1) {
dirIcon.setImageResource(R.drawable.dir_end);
lineName.setText("到达终点");
dirUp.setVisibility(View.VISIBLE);
dirDown.setVisibility(View.INVISIBLE);
} else {
splitLine.setVisibility(View.VISIBLE);
dirUp.setVisibility(View.VISIBLE);
dirDown.setVisibility(View.VISIBLE);
String actionName = item.getAction();
int resID = MapUtil.getWalkActionID(actionName);
dirIcon.setImageResource(resID);
lineName.setText(item.getInstruction());
}
}
}
下面再进入到RouteDetailActivity,新增如下方法:
/**
* 骑行详情
* @param intent
*/
private void rideDetail(Intent intent) {
tvTitle.setText("骑行路线规划");
RidePath ridePath = intent.getParcelableExtra("path");
String dur = MapUtil.getFriendlyTime((int) ridePath.getDuration());
String dis = MapUtil.getFriendlyLength((int) ridePath.getDistance());
tvTime.setText(dur + "(" + dis + ")");
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new RideSegmentListAdapter(R.layout.item_segment, ridePath.getSteps()));
}
然后在initView中调用。
然后你就可以运行了。
⑧ 驾车路线详情
在RouteActivity中找到onRideRouteSearched方法,方法里面添加如下代码:
tvTime.setText(des);
bottomLayout.setVisibility(View.VISIBLE);
bottomLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(RouteActivity.this,
RouteDetailActivity.class);
intent.putExtra("type",2);
intent.putExtra("path", drivePath);
startActivity(intent);
}
});
然后在adapter包下新增一个DriveSegmentListAdapter类,里面的代码如下;
package com.llw.mapdemo.adapter;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.amap.api.services.route.DriveStep;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.llw.mapdemo.R;
import com.llw.mapdemo.util.MapUtil;
import java.util.List;
/**
* 驾车段列表适配器
*
* @author llw
* @date 2021/2/23 11:18
*/
public class DriveSegmentListAdapter extends BaseQuickAdapter<DriveStep, BaseViewHolder> {
private List<DriveStep> mItemList;
public DriveSegmentListAdapter(int layoutResId, @Nullable List<DriveStep> data) {
super(layoutResId, data);
mItemList = data;
}
@Override
protected void convert(BaseViewHolder helper, DriveStep item) {
TextView lineName = helper.getView(R.id.bus_line_name);
ImageView dirIcon = helper.getView(R.id.bus_dir_icon);
ImageView dirUp = helper.getView(R.id.bus_dir_icon_up);
ImageView dirDown = helper.getView(R.id.bus_dir_icon_down);
ImageView splitLine = helper.getView(R.id.bus_seg_split_line);
int position = getItemPosition(item);
if (position == 0) {
dirIcon.setImageResource(R.drawable.dir_start);
lineName.setText("出发");
dirUp.setVisibility(View.INVISIBLE);
dirDown.setVisibility(View.VISIBLE);
splitLine.setVisibility(View.INVISIBLE);
} else if (position == mItemList.size() - 1) {
dirIcon.setImageResource(R.drawable.dir_end);
lineName.setText("到达终点");
dirUp.setVisibility(View.VISIBLE);
dirDown.setVisibility(View.INVISIBLE);
} else {
splitLine.setVisibility(View.VISIBLE);
dirUp.setVisibility(View.VISIBLE);
dirDown.setVisibility(View.VISIBLE);
String actionName = item.getAction();
int resID = MapUtil.getDriveActionID(actionName);
dirIcon.setImageResource(resID);
lineName.setText(item.getInstruction());
}
}
}
这里面会有一个方法报错,所以需要在MapUtil中新增如下方法:
public static int getDriveActionID(String actionName) {
if (actionName == null || actionName.equals("")) {
return R.drawable.dir3;
}
if ("左转".equals(actionName)) {
return R.drawable.dir2;
}
if ("右转".equals(actionName)) {
return R.drawable.dir1;
}
if ("向左前方行驶".equals(actionName) || "靠左".equals(actionName)) {
return R.drawable.dir6;
}
if ("向右前方行驶".equals(actionName) || "靠右".equals(actionName)) {
return R.drawable.dir5;
}
if ("向左后方行驶".equals(actionName) || "左转调头".equals(actionName)) {
return R.drawable.dir7;
}
if ("向右后方行驶".equals(actionName)) {
return R.drawable.dir8;
}
if ("直行".equals(actionName)) {
return R.drawable.dir3;
}
if ("减速行驶".equals(actionName)) {
return R.drawable.dir4;
}
return R.drawable.dir3;
}
适配器没有问题了,下面进入到RouteDetailActivity,新增如下方法:
/**
* 驾车详情
* @param intent
*/
private void driveDetail(Intent intent) {
tvTitle.setText("驾车路线规划");
DrivePath drivePath = intent.getParcelableExtra("path");
String dur = MapUtil.getFriendlyTime((int) drivePath.getDuration());
String dis = MapUtil.getFriendlyLength((int) drivePath.getDistance());
tvTime.setText(dur + "(" + dis + ")");
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new DriveSegmentListAdapter(R.layout.item_segment, drivePath.getSteps()));
}
然后在initView中调用。
然后运行一下:
⑨ 公交路线详情
最后这个公交路线详情是最麻烦的,前面三个其实都还蛮简单的,同样在RouteActivity中找到onBusRouteSearched方法,然后在里面添加如下代码:
tvTime.setText(des);
bottomLayout.setVisibility(View.VISIBLE);
bottomLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(RouteActivity.this,
RouteDetailActivity.class);
intent.putExtra("type",3);
intent.putExtra("path", busPath);
startActivity(intent);
}
});
下面这个适配器的代码就比较多了,为什么呢?因为公交的话它还有这个公交车的信息,比如你是要做几路车,然后经过几个站,之后再转几路车,最后到达终点。因此先增加一个站点的布局,在layout下创建一个item_segment_ex.xml文件,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="27dp"
android:layout_marginLeft="15.5dp"
android:layout_marginStart="15.5dp" >
<ImageView
android:id="@+id/bus_dir_icon"
android:layout_width="13dp"
android:layout_height="13dp"
android:layout_centerVertical="true"
android:src="@drawable/dir_station" />
<ImageView
android:id="@+id/bus_dir_icon_up"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_above="@id/bus_dir_icon"
android:layout_centerHorizontal="true"
android:background="#b6b6b6" />
<ImageView
android:id="@+id/bus_dir_icon_down"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_below="@id/bus_dir_icon"
android:layout_centerHorizontal="true"
android:background="#b6b6b6" />
</RelativeLayout>
<TextView
android:id="@+id/bus_line_station_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:textColor="#999999"
android:textSize="13sp" />
</LinearLayout>
这里还需要对原来的BusStep进行一次封装,而不是直接使用这个BusStep,在util包下新增一个SchemeBusStep类,代码如下:
package com.llw.mapdemo.util;
import com.amap.api.services.route.BusStep;
public class SchemeBusStep extends BusStep {
private boolean isWalk = false;
private boolean isBus = false;
private boolean israilway = false;
private boolean istaxi = false;
private boolean isStart = false;
private boolean isEnd = false;
private boolean arrowExpend = false;
public SchemeBusStep(BusStep step) {
if (step != null) {
this.setBusLine(step.getBusLine());
this.setWalk(step.getWalk());
this.setRailway(step.getRailway());
this.setTaxi(step.getTaxi());
}
}
public boolean isArrowExpend() {
return arrowExpend;
}
public void setArrowExpend(boolean arrowExpend) {
this.arrowExpend = arrowExpend;
}
public boolean isWalk() {
return isWalk;
}
public void setWalk(boolean isWalk) {
this.isWalk = isWalk;
}
public boolean isBus() {
return isBus;
}
public void setBus(boolean isBus) {
this.isBus = isBus;
}
public boolean isStart() {
return isStart;
}
public void setStart(boolean isStart) {
this.isStart = isStart;
}
public boolean isEnd() {
return isEnd;
}
public void setEnd(boolean isEnd) {
this.isEnd = isEnd;
}
public boolean isRailway() {
return israilway;
}
public boolean isTaxi() {
return istaxi;
}
public void setRailway(boolean israilway) {
this.israilway = israilway;
}
public void setTaxi(boolean istaxi) {
this.istaxi = istaxi;
}
}
这个里面主要就是对数据的控制,使你在适配器中可以更好处理显示的效果。下面来写这个适配器,在adapter包下新建一个BusSegmentListAdapter类,代码如下:
package com.llw.mapdemo.adapter;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.amap.api.services.busline.BusStationItem;
import com.amap.api.services.route.RailwayStationItem;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
import com.llw.mapdemo.R;
import com.llw.mapdemo.util.SchemeBusStep;
import java.util.List;
/**
* 公交段列表适配器
*
* @author llw
* @date 2021/2/23 14:37
*/
public class BusSegmentListAdapter extends BaseQuickAdapter<SchemeBusStep, BaseViewHolder> {
private List<SchemeBusStep> mBusStepList;
public BusSegmentListAdapter(int layoutResId, @Nullable List<SchemeBusStep> data) {
super(layoutResId, data);
mBusStepList = data;
}
@Override
protected void convert(BaseViewHolder helper, final SchemeBusStep item) {
RelativeLayout busItem = helper.getView(R.id.bus_item);
TextView busLineName = helper.getView(R.id.bus_line_name);
ImageView busDirIcon = helper.getView(R.id.bus_dir_icon);
TextView busStationNum = helper.getView(R.id.bus_station_num);
final ImageView busExpandImage = helper.getView(R.id.bus_expand_image);
ImageView busDirUp = helper.getView(R.id.bus_dir_icon_up);
ImageView busDirDown = helper.getView(R.id.bus_dir_icon_down);
ImageView splitLine = helper.getView(R.id.bus_seg_split_line);
final LinearLayout expandContent = helper.getView(R.id.expand_content);
int position = getItemPosition(item);
if (position == 0) {
busDirIcon.setImageResource(R.drawable.dir_start);
busLineName.setText("出发");
busDirUp.setVisibility(View.INVISIBLE);
busDirDown.setVisibility(View.VISIBLE);
splitLine.setVisibility(View.GONE);
busStationNum.setVisibility(View.GONE);
busExpandImage.setVisibility(View.GONE);
} else if (position == mBusStepList.size() - 1) {
busDirIcon.setImageResource(R.drawable.dir_end);
busLineName.setText("到达终点");
busDirUp.setVisibility(View.VISIBLE);
busDirDown.setVisibility(View.INVISIBLE);
busStationNum.setVisibility(View.INVISIBLE);
busExpandImage.setVisibility(View.INVISIBLE);
} else {
if (item.isWalk() && item.getWalk() != null && item.getWalk().getDistance() > 0) {
busDirIcon.setImageResource(R.drawable.dir13);
busDirUp.setVisibility(View.VISIBLE);
busDirDown.setVisibility(View.VISIBLE);
busLineName.setText("步行"
+ (int) item.getWalk().getDistance() + "米");
busStationNum.setVisibility(View.GONE);
busExpandImage.setVisibility(View.GONE);
} else if (item.isBus() && item.getBusLines().size() > 0) {
busDirIcon.setImageResource(R.drawable.dir14);
busDirUp.setVisibility(View.VISIBLE);
busDirDown.setVisibility(View.VISIBLE);
busLineName.setText(item.getBusLines().get(0).getBusLineName());
busStationNum.setVisibility(View.VISIBLE);
busStationNum
.setText((item.getBusLines().get(0).getPassStationNum() + 1) + "站");
busExpandImage.setVisibility(View.VISIBLE);
} else if (item.isRailway() && item.getRailway() != null) {
busDirIcon.setImageResource(R.drawable.dir16);
busDirUp.setVisibility(View.VISIBLE);
busDirDown.setVisibility(View.VISIBLE);
busLineName.setText(item.getRailway().getName());
busStationNum.setVisibility(View.VISIBLE);
busStationNum
.setText((item.getRailway().getViastops().size() + 1) + "站");
busExpandImage.setVisibility(View.VISIBLE);
} else if (item.isTaxi() && item.getTaxi() != null) {
busDirIcon.setImageResource(R.drawable.dir14);
busDirUp.setVisibility(View.VISIBLE);
busDirDown.setVisibility(View.VISIBLE);
busLineName.setText("打车到终点");
busStationNum.setVisibility(View.GONE);
busExpandImage.setVisibility(View.GONE);
}
}
busItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (item.isBus()) {//公交
if (item.isArrowExpend() == false) {
item.setArrowExpend(true);
busExpandImage.setImageResource(R.drawable.up);
addBusStation(item.getBusLine().getDepartureBusStation(), expandContent);
for (BusStationItem station : item.getBusLine()
.getPassStations()) {
addBusStation(station, expandContent);
}
addBusStation(item.getBusLine().getArrivalBusStation(), expandContent);
} else {
item.setArrowExpend(false);
busExpandImage.setImageResource(R.drawable.down);
expandContent.removeAllViews();
}
} else if (item.isRailway()) {//火车
if (item.isArrowExpend() == false) {
item.setArrowExpend(true);
busExpandImage.setImageResource(R.drawable.up);
addRailwayStation(item.getRailway().getDeparturestop(), expandContent);
for (RailwayStationItem station : item.getRailway().getViastops()) {
addRailwayStation(station, expandContent);
}
addRailwayStation(item.getRailway().getArrivalstop(), expandContent);
} else {
item.setArrowExpend(false);
busExpandImage.setImageResource(R.drawable.down);
expandContent.removeAllViews();
}
}
}
});
}
/**
* 添加公交车站
* @param station
* @param expandContent
*/
private void addBusStation(BusStationItem station, LinearLayout expandContent) {
LinearLayout ll = (LinearLayout) View.inflate(getContext(),
R.layout.item_segment_ex, null);
TextView tv = ll.findViewById(R.id.bus_line_station_name);
tv.setText(station.getBusStationName());
expandContent.addView(ll);
}
/**
* 添加火车站
* @param station
* @param expandContent
*/
private void addRailwayStation(RailwayStationItem station, LinearLayout expandContent) {
LinearLayout ll = (LinearLayout) View.inflate(getContext(),
R.layout.item_segment_ex, null);
TextView tv = ll
.findViewById(R.id.bus_line_station_name);
tv.setText(station.getName() + " " + getRailwayTime(station.getTime()));
expandContent.addView(ll);
}
/**
* 获取铁路时间
* @param time
* @return
*/
public static String getRailwayTime(String time) {
return time.substring(0, 2) + ":" + time.substring(2, time.length());
}
}
里面也是比较简单的代码,不清楚的话和我说一声,我再给你解释一下,那么下面进入RouteDetailActivity,先增加一个getBusSteps方法,对BusStep数据进行组装。
/**
* 公交方案数据组装
* @param list
* @return
*/
private List<SchemeBusStep> getBusSteps(List<BusStep> list) {
List<SchemeBusStep> busStepList = new ArrayList<>();
SchemeBusStep start = new SchemeBusStep(null);
start.setStart(true);
busStepList.add(start);
for (BusStep busStep : list) {
if (busStep.getWalk() != null && busStep.getWalk().getDistance() > 0) {
SchemeBusStep walk = new SchemeBusStep(busStep);
walk.setWalk(true);
busStepList.add(walk);
}
if (busStep.getBusLine() != null) {
SchemeBusStep bus = new SchemeBusStep(busStep);
bus.setBus(true);
busStepList.add(bus);
}
if (busStep.getRailway() != null) {
SchemeBusStep railway = new SchemeBusStep(busStep);
railway.setRailway(true);
busStepList.add(railway);
}
if (busStep.getTaxi() != null) {
SchemeBusStep taxi = new SchemeBusStep(busStep);
taxi.setTaxi(true);
busStepList.add(taxi);
}
}
SchemeBusStep end = new SchemeBusStep(null);
end.setEnd(true);
busStepList.add(end);
return busStepList;
}
然后添加一个busDetail方法,里面的代码如下:
/**
* 公交详情
* @param intent
*/
private void busDetail(Intent intent) {
tvTitle.setText("公交路线规划");
BusPath busPath = intent.getParcelableExtra("path");
String dur = MapUtil.getFriendlyTime((int) busPath.getDuration());
String dis = MapUtil.getFriendlyLength((int) busPath.getDistance());
tvTime.setText(dur + "(" + dis + ")");
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new BusSegmentListAdapter(R.layout.item_segment, getBusSteps(busPath.getSteps())));
}
最后在initView中调用。
然后运行一下:
⑩ 手动输入目的地和出发地
在实际使用中,通常都是定位到当前所在地,然后用户再输入一个目的地,以此来计算这个两点之间的路线规划,手动点地图这种方式并不常用,因为你很难点到足够精确的位置,其次就是你要在地图上寻找这个地方所在,这样用户花费的时间就会更多,因此日常使用都是手动输入,输入方式有多种,常规的键盘输入、声音输入、扫码输入等。下面我们使用键盘输入。
这里就需要修改activity_route.xml布局了,将之前的出行方式布局改为如下代码:
<!--设置出行方式-->
<LinearLayout
android:id="@+id/top_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="6dp"
android:paddingEnd="6dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="出行方式:"
android:textColor="#000"
android:textSize="16sp" />
<Spinner
android:id="@+id/spinner"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="6dp"
android:paddingEnd="6dp">
<TextView
android:id="@+id/tv_start"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="当前所在地"
android:textColor="#000"
android:textSize="@dimen/sp_14" />
<EditText
android:id="@+id/et_start_address"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="12dp"
android:layout_toRightOf="@+id/tv_start"
android:background="@null"
android:textColor="#000"
android:textSize="@dimen/sp_12" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_below="@+id/et_start_address"
android:background="#000" />
<TextView
android:id="@+id/tv_end"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_below="@+id/et_start_address"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:text="前往目的地"
android:textColor="#000"
android:textSize="@dimen/sp_14" />
<EditText
android:id="@+id/et_end_address"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@+id/et_start_address"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_toRightOf="@+id/tv_end"
android:background="@null"
android:hint="请输入目的地"
android:imeOptions="actionSearch"
android:singleLine="true"
android:textColor="#000"
android:textSize="14sp" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_below="@+id/et_end_address"
android:background="#000" />
</RelativeLayout>
</LinearLayout>
预览效果如下:
然后进入到RouteActivity,创建变量。
//起点、终点
private EditText etStartAddress, etEndAddress;
然后在initTravelMode方法中绑定ID,同时给目的地输入框添加一个软键盘按键监听。
etStartAddress = findViewById(R.id.et_start_address);
etEndAddress = findViewById(R.id.et_end_address);
//键盘按键监听
etEndAddress.setOnKeyListener(this);
这里的当前地我还是使用定位所在地,在onLocationChanged方法中增加如下代码。
//设置当前所在地
etStartAddress.setText(address);
etStartAddress.setEnabled(false);//禁用输入
我在布局中设置了软键盘变成搜索功能按钮,此时etEndAddress.setOnKeyListener(this);
的这个this应该下方有一条红线,因此你当前的Activity需要实现按键监听。
然后重写onKey方法。
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
//获取输入框的值
String endAddress = etEndAddress.getText().toString().trim();
if (endAddress.isEmpty()) {
showMsg("请输入要前往的目的地");
} else {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
//隐藏软键盘
imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
//通过输入的目的地转为经纬度,然后进行地图上添加标点,最后计算出行路线规划
}
return true;
}
return false;
}
我相信这个代码你应该见过一次了,只不过现在还差一个地址转换的处理,通过地理编码就可以把地址转化成坐标。
//地理编码搜索
private GeocodeSearch geocodeSearch;
//解析成功标识码
private static final int PARSE_SUCCESS_CODE = 1000;
然后在initMap中
//构造 GeocodeSearch 对象
geocodeSearch = new GeocodeSearch(this);
//设置监听
geocodeSearch.setOnGeocodeSearchListener(this);
同样Activity依然需要实现GeocodeSearch.OnGeocodeSearchListener。
然后重写如下方法:
@Override
public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
}
/**
* 地址转坐标
*
* @param geocodeResult
* @param rCode
*/
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) {
if (rCode == PARSE_SUCCESS_CODE) {
List<GeocodeAddress> geocodeAddressList = geocodeResult.getGeocodeAddressList();
if (geocodeAddressList != null && geocodeAddressList.size() > 0) {
//终点
mEndPoint = geocodeAddressList.get(0).getLatLonPoint();
//开始路线搜索
startRouteSearch();
}
} else {
showMsg("获取坐标失败");
}
}
在onGeocodeSearched方法中通过坐标确定终点,然后进行路线规划,最后一步就是在onKey方法的else中触发这个地理编码搜索。
// name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode
GeocodeQuery query = new GeocodeQuery(endAddress, city);
geocodeSearch.getFromLocationNameAsyn(query);
这样就OK了,运行一下。
现在这个当前所在地是通过定位获取的,那么怎么样改成可以手动输入的呢?其实很简单,一开始我写这个页面时,因为是通过定位获取到这个所在地的地址信息然后设置到控件上,所以控件的字体我做了一些调整,如果说要想设置为可输入的话,那么两个输入框的字体应该一致才行,下面先更改这一个,打开activity_route.xml。
之前在页面中我是对这个输入框拿到定位地址后就做了禁用,因此你无法再去手动输入了,现在去掉它。
这行代码我注释掉了,之前设置这个出发地也就是所在地的时候通过定位的方式可以拿到这个经纬度和地址信息,因此很容易就可以构建出mStartPoint(起点),而现在你要做到不光是可以定位,也可以通过手动输入的方式。那么业务逻辑上肯定要有改变了。
首先将定位的地址设置为成员变量。
现在你看到的这个address,是一个局部变量,只会在定位获得结果的时候赋值,因此需要改变一下。
定义变量,我把原来的变量名从address改成了locationAddress,表明这是一个通过定位获得的地址。
//定位地址
private String locationAddress;
对于路线的规划是在输入完目的地之后,点击软键盘上的搜索按钮之后执行的。还记得之前的目的地输入后,有一个地址转坐标的操作,在这里进行了目的地的构建,然后才是路线的规划。
那么你现在的出发地也是手动输入的话,那就同样要转换成坐标,然后去构建这个出发地,也就是起点左标,而解析只有一个返回,那么你就需要区分这个解析是起点还是终点了。然后来看这一块的代码,这里就是对这个目的地输入框进行了检查,这是之前的代码,现在这一块的代码就要做更改了。
首先定义变量。
//起点地址转坐标标识 1
private int tag = -1;
然后设置起点这个输入框的软键盘搜索按键监听。
然后修改onKey方法。如下所示:
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
//获取输入框的值 出发地(起点)
String startAddress = etStartAddress.getText().toString().trim();
//获取输入框的值 目的地(终点)
String endAddress = etEndAddress.getText().toString().trim();
//判断出发地是否有值 不管这个值是定位还是手动输入
if (startAddress.isEmpty()) {
showMsg("请输入当前的出发地");
return false;
}
//判断目的地是否有值
if (endAddress.isEmpty()) {
showMsg("请输入要前往的目的地");
return false;
}
//当出发地输入框有值的时候,判断这个值是否是定位的地址,是则说明你没有更改过,则不需要进行地址转坐标,不是则需要转换。
if (!locationAddress.equals(startAddress)) {
tag = 1;
GeocodeQuery startQuery = new GeocodeQuery(startAddress, city);
geocodeSearch.getFromLocationNameAsyn(startQuery);
} else {
tag = -1;
}
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
//隐藏软键盘
imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
//通过输入的目的地转为经纬度,然后进行地图上添加标点,最后计算出行路线规划
// name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode
GeocodeQuery endQuery = new GeocodeQuery(endAddress, city);
geocodeSearch.getFromLocationNameAsyn(endQuery);
return true;
}
return false;
}
然后再去修改onGeocodeSearched方法的代码:
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) {
if (rCode == PARSE_SUCCESS_CODE) {
List<GeocodeAddress> geocodeAddressList = geocodeResult.getGeocodeAddressList();
if (geocodeAddressList != null && geocodeAddressList.size() > 0) {
//判断是不是起点的搜索
if (tag == 1) {
//起点
mStartPoint = geocodeAddressList.get(0).getLatLonPoint();
} else {
//终点
mEndPoint = geocodeAddressList.get(0).getLatLonPoint();
}
if (mStartPoint != null && mEndPoint != null) {
//开始路线搜索
startRouteSearch();
}
}
} else {
showMsg("获取坐标失败");
}
}
下面就可以运行了。
十、源码
如果对你有所帮助的话,不妨欢迎Star和Fork。
源码地址:GaodeMapDemo
尾声
掘金在文章字数限制这一块做的太严格了,不得已一篇文章要分成四篇来发布。说实话写这篇文章真的很不容易,花费了我很多精力,当然首先是对得起看这篇文章的人吧,山高水长,后会有期~