一、这篇文章包括公交路线的显示,积极站名通过横线时间轴的显示,先看效果图吧!
这个是西安市公交800路的运行图,以及经过的站名!
二、项目布局文件
1、主布局文件
这里其实就是一个横向的listview,但是又不是那个listview,还是看源码吧!
package com.bm.futuretechcity.view;
import java.util.LinkedList;
import java.util.Queue;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;
public class HorizontalListView extends AdapterView {
public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mLeftViewIndex = -1;
private int mRightViewIndex = 0;
protected int mCurrentX;
protected int mNextX;
private int mMaxX = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private GestureDetector mGesture;
private Queue mRemovedViewQueue = new LinkedList();
private OnItemSelectedListener mOnItemSelected;
private OnItemClickListener mOnItemClicked;
private OnItemLongClickListener mOnItemLongClicked;
private boolean mDataChanged = false;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private synchronized void initView() {
mLeftViewIndex = -1;
mRightViewIndex = 0;
mDisplayOffset = 0;
mCurrentX = 0;
mNextX = 0;
mMaxX = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
mGesture = new GestureDetector(getContext(), mOnGesture);
}
@Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
}
@Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
mOnItemClicked = listener;
}
@Override
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
mOnItemLongClicked = listener;
}
private DataSetObserver mDataObserver = new DataSetObserver() {
@Override
public void onChanged() {
synchronized(HorizontalListView.this){
mDataChanged = true;
}
invalidate();
requestLayout();
}
@Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}
};
@Override
public ListAdapter getAdapter() {
return mAdapter;
}
@Override
public View getSelectedView() {
//TODO: implement
return null;
}
@Override
public void setAdapter(ListAdapter adapter) {
if(mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
}
private synchronized void reset(){
initView();
removeAllViewsInLayout();
requestLayout();
}
@Override
public void setSelection(int position) {
//TODO: implement
}
private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
@Override
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(mAdapter == null){
return;
}
if(mDataChanged){
int oldCurrentX = mCurrentX;
initView();
removeAllViewsInLayout();
mNextX = oldCurrentX;
mDataChanged = false;
}
if(mScroller.computeScrollOffset()){
int scrollx = mScroller.getCurrX();
mNextX = scrollx;
}
if(mNextX <= 0){
mNextX = 0;
mScroller.forceFinished(true);
}
if(mNextX >= mMaxX) {
mNextX = mMaxX;
mScroller.forceFinished(true);
}
int dx = mCurrentX - mNextX;
removeNonVisibleItems(dx);
fillList(dx);
positionItems(dx);
mCurrentX = mNextX;
if(!mScroller.isFinished()){
post(new Runnable(){
@Override
public void run() {
requestLayout();
}
});
}
}
private void fillList(final int dx) {
int edge = 0;
View child = getChildAt(getChildCount()-1);
if(child != null) {
edge = child.getRight();
}
fillListRight(edge, dx);
edge = 0;
child = getChildAt(0);
if(child != null) {
edge = child.getLeft();
}
fillListLeft(edge, dx);
}
private void fillListRight(int rightEdge, final int dx) {
while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {
View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
rightEdge += child.getMeasuredWidth();
if(mRightViewIndex == mAdapter.getCount()-1) {
mMaxX = mCurrentX + rightEdge - getWidth();
}
if (mMaxX < 0) {
mMaxX = 0;
}
mRightViewIndex++;
}
}
private void fillListLeft(int leftEdge, final int dx) {
while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
leftEdge -= child.getMeasuredWidth();
mLeftViewIndex--;
mDisplayOffset -= child.getMeasuredWidth();
}
}
private void removeNonVisibleItems(final int dx) {
View child = getChildAt(0);
while(child != null && child.getRight() + dx <= 0) {
mDisplayOffset += child.getMeasuredWidth();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mLeftViewIndex++;
child = getChildAt(0);
}
child = getChildAt(getChildCount()-1);
while(child != null && child.getLeft() + dx >= getWidth()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mRightViewIndex--;
child = getChildAt(getChildCount()-1);
}
}
private void positionItems(final int dx) {
if(getChildCount() > 0){
mDisplayOffset += dx;
int left = mDisplayOffset;
for(int i=0;i
3、在一个这里使用到了竖向的文字布局,这个事自定义了一个view实现的;看源码!
package com.bm.futuretechcity.view;
/**************************
* 说明:本类实现了文字的竖直排版显示(中国古时的行文形式),
* 虽然仍有许多特效及功能仍未实现,但基本的使用已经能满足。
* 版权:尽管放心用吧,可以自行随意改进转载和使用,转载时请保留这段文字即可
* 另特别感谢 老僧xp 提出的修改意见
**************************/
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.Paint.FontMetrics;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.View.MeasureSpec;
public class TextViewVertical extends View {
public static final int LAYOUT_CHANGED = 1;
private Paint paint;
private int mTextPosx = 0;// x坐标
private int mTextPosy = 0;// y坐标
private int mTextWidth = 0;// 绘制宽度
private int mTextHeight = 0;// 绘制高度
private int mFontHeight = 0;// 绘制字体高度
private float mFontSize = 24;// 字体大小
private int mRealLine = 0;// 字符串真实的行数
private int mLineWidth = 0;//列宽度
private int TextLength = 0;//字符串长度
private int oldwidth = 0;//存储久的width
private String text = "";//待显示的文字
private Handler mHandler = null;
private Matrix matrix;
private Align textStartAlign = Align.RIGHT;//draw start left or right.//default right
BitmapDrawable drawable = (BitmapDrawable) getBackground();
public TextViewVertical(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public TextViewVertical(Context context, AttributeSet attrs) {
super(context, attrs);
matrix = new Matrix();
paint = new Paint();//新建画笔
paint.setTextAlign(Align.CENTER);//文字居中
paint.setAntiAlias(true);//平滑处理
paint.setColor(Color.BLACK);//默认文字颜色
try {
mFontSize = Float.parseFloat(attrs.getAttributeValue(null, "textSize"));//获取字体大小属性
} catch (Exception e) {
}
}
/*
//获取整数值
private final int getAttributeIntValue(AttributeSet attrs,String field) {
int intVal = 0;
//TODO
//应该可以直接用attrs.getAttributeIntValue()获取对应的数值的,
//但不知道为什么一直无法获得只好临时写个函数凑合着用,没有写完整,暂时只支持px作为单位,其它单位的转换有空再写
String tempText=attrs.getAttributeValue(androidns, field);
intVal = (int)Math.ceil(Float.parseFloat(tempText.replaceAll("px","")));
return intVal;
}*/
//设置文字
public final void setText(String text) {
this.text = text;
this.TextLength = text.length();
if (mTextHeight > 0) GetTextInfo();
}
//设置字体大小
public final void setTextSize(float size) {
if (size != paint.getTextSize()) {
mFontSize = size;
if (mTextHeight > 0) GetTextInfo();
}
}
//设置字体颜色
public final void setTextColor(int color) {
paint.setColor(color);
}
//设置字体颜色
public final void setTextARGB(int a, int r, int g, int b) {
paint.setARGB(a, r, g, b);
}
//设置字体
public void setTypeface(Typeface tf) {
if (this.paint.getTypeface() != tf) {
this.paint.setTypeface(tf);
}
}
//设置行宽
public void setLineWidth(int LineWidth) {
mLineWidth = LineWidth;
}
//获取实际宽度
public int getTextWidth() {
return mTextWidth;
}
//设置Handler,用以发送事件
public void setHandler(Handler handler) {
mHandler = handler;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.v("TextViewVertical", "onDraw");
if (drawable != null) {
//画背景
Bitmap b = Bitmap.createBitmap(drawable.getBitmap(), 0, 0, mTextWidth, mTextHeight);
canvas.drawBitmap(b, matrix, paint);
}
//画字
draw(canvas, this.text);
}
private void draw(Canvas canvas, String thetext) {
char ch;
mTextPosy = 0;//初始化y坐标
mTextPosx = textStartAlign == Align.LEFT ? mLineWidth : mTextWidth - mLineWidth;//初始化x坐标
for (int i = 0; i < this.TextLength; i++) {
ch = thetext.charAt(i);
if (ch == '\n') {
if (textStartAlign == Align.LEFT) {
mTextPosx += mLineWidth;// 换列
} else {
mTextPosx -= mLineWidth;// 换列
}
mTextPosy = 0;
} else {
mTextPosy += mFontHeight;
if (mTextPosy > this.mTextHeight) {
if (textStartAlign == Align.LEFT) {
mTextPosx += mLineWidth;// 换列
} else {
mTextPosx -= mLineWidth;// 换列
}
i--;
mTextPosy = 0;
} else {
canvas.drawText(String.valueOf(ch), mTextPosx, mTextPosy, paint);
}
}
}
//调用接口方法
//activity.getHandler().sendEmptyMessage(TestFontActivity.UPDATE);
}
//计算文字行数和总宽
private void GetTextInfo() {
Log.v("TextViewVertical", "GetTextInfo");
char ch;
int h = 0;
paint.setTextSize(mFontSize);
//获得字宽
if (mLineWidth == 0) {
float[] widths = new float[1];
paint.getTextWidths("正", widths);//获取单个汉字的宽度
mLineWidth = (int) Math.ceil(widths[0] * 1.1 + 2);
}
FontMetrics fm = paint.getFontMetrics();
mFontHeight = (int) (Math.ceil(fm.descent - fm.top) * 0.9);// 获得字体高度
//计算文字行数
mRealLine = 0;
for (int i = 0; i < this.TextLength; i++) {
ch = this.text.charAt(i);
if (ch == '\n') {
mRealLine++;// 真实的行数加一
h = 0;
} else {
h += mFontHeight;
if (h > this.mTextHeight) {
mRealLine++;// 真实的行数加一
i--;
h = 0;
} else {
if (i == this.TextLength - 1) {
mRealLine++;// 真实的行数加一
}
}
}
}
mRealLine++;//额外增加一行
mTextWidth = mLineWidth * mRealLine;//计算文字总宽度
measure(mTextWidth, getHeight());//重新调整大小
layout(getLeft(), getTop(), getLeft() + mTextWidth, getBottom());//重新绘制容器
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredHeight = measureHeight(heightMeasureSpec);
//int measuredWidth = measureWidth(widthMeasureSpec);
if (mTextWidth == 0) GetTextInfo();
setMeasuredDimension(mTextWidth, measuredHeight);
if (oldwidth != getWidth()) {//
oldwidth = getWidth();
if (mHandler != null) mHandler.sendEmptyMessage(LAYOUT_CHANGED);
}
}
private int measureHeight(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
int result = 500;
if (specMode == MeasureSpec.AT_MOST) {
result = specSize;
} else if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}
mTextHeight = result;//设置文本高度
return result;
}
/*
private int measureWidth(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
int result = 500;
if (specMode == MeasureSpec.AT_MOST){
result = specSize;
}else if (specMode == MeasureSpec.EXACTLY){
result = specSize;
}
return result;
} */
}
三、地图显示的java文件
package com.bm.futuretechcity;
import java.util.List;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.AMap.InfoWindowAdapter;
import com.amap.api.maps.AMap.OnMarkerClickListener;
import com.amap.api.maps.MapView;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.overlay.BusLineOverlay;
import com.amap.api.services.busline.BusLineItem;
import com.amap.api.services.busline.BusLineQuery;
import com.amap.api.services.busline.BusLineQuery.SearchType;
import com.amap.api.services.busline.BusLineResult;
import com.amap.api.services.busline.BusLineSearch;
import com.amap.api.services.busline.BusLineSearch.OnBusLineSearchListener;
import com.amap.api.services.busline.BusStationItem;
import com.amap.api.services.busline.BusStationQuery;
import com.amap.api.services.busline.BusStationResult;
import com.amap.api.services.busline.BusStationSearch;
import com.amap.api.services.busline.BusStationSearch.OnBusStationSearchListener;
import com.bm.futuretechcity.adapter.ShowStationNameAdapter;
import com.bm.futuretechcity.base.BaseActivity;
import com.bm.futuretechcity.utils.ToastUtil;
import com.bm.futuretechcity.view.HorizontalListView;
public class TraBuslineActivity extends BaseActivity implements OnMarkerClickListener, InfoWindowAdapter, OnItemSelectedListener,
OnBusLineSearchListener, OnBusStationSearchListener, OnClickListener {
/**
* 页面标题
*/
private TextView titleTv;
private AMap aMap;
private MapView mapView;
private ProgressDialog progDialog = null;// 进度框
private String cityCode = "";// 城市区号
private int currentpage = 0;// 公交搜索当前页,第一页从0开始
private BusLineResult busLineResult;// 公交线路搜索返回的结果
private List lineItems = null;// 公交线路搜索返回的busline
private BusLineQuery busLineQuery;// 公交线路查询的查询类
private BusStationResult busStationResult;// 公交站点搜索返回的结果
private List stationItems;// 公交站点搜索返回的busStation
private HorizontalListView show_name_station;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_tra_busline);
/*
* 设置离线地图存储目录,在下载离线地图或初始化地图设置; 使用过程中可自行设置, 若自行设置了离线地图存储的路径,
* 则需要在离线地图下载和使用地图页面都进行路径设置
*/
// Demo中为了其他界面可以使用下载的离线地图,使用默认位置存储,屏蔽了自定义设置
// MapsInitializer.sdcardDir =OffLineMapUtils.getSdCacheDir(this);
show_name_station = (HorizontalListView) this.findViewById(R.id.show_name_station);
mapView = (MapView) findViewById(R.id.map);
mapView.onCreate(bundle);// 此方法必须重写
init();
// String lineId = item.getBusLineId();// 得到当前点击item公交线路id
busLineQuery = new BusLineQuery("610100011265", SearchType.BY_LINE_ID, cityCode);// 第一个参数表示公交线路id,第二个参数表示公交线路id查询,第三个参数表示所在城市名或者城市区号
BusLineSearch busLineSearch = new BusLineSearch(TraBuslineActivity.this, busLineQuery);
busLineSearch.setOnBusLineSearchListener(TraBuslineActivity.this);
busLineSearch.searchBusLineAsyn();// 异步查询公交线路id
// 公交站点搜索事例
BusStationQuery query = new BusStationQuery("800", cityCode);
query.setPageSize(10);
query.setPageNumber(currentpage);
BusStationSearch busStationSearch = new BusStationSearch(this, query);
busStationSearch.setOnBusStationSearchListener(this);
busStationSearch.searchBusStationAsyn();
}
/**
* 初始化AMap对象
*/
private void init() {
if (aMap == null) {
aMap = mapView.getMap();
setUpMap();
}
}
/**
* 设置marker的监听和信息窗口的监听
*/
private void setUpMap() {
aMap.setOnMarkerClickListener(this);
aMap.setInfoWindowAdapter(this);
}
/**
* 方法必须重写
*/
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
/**
* 方法必须重写
*/
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
}
/**
* 方法必须重写
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
/**
* 方法必须重写
*/
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
/**
* 提供一个给默认信息窗口定制内容的方法
*/
@Override
public View getInfoContents(Marker marker) {
return null;
}
/**
* 提供一个个性化定制信息窗口的方法
*/
@Override
public View getInfoWindow(Marker marker) {
return null;
}
/**
* 点击marker回调函数
*/
@Override
public boolean onMarkerClick(Marker marker) {
return false;// 点击marker时把此marker显示在地图中心点
}
@Override
public void onNothingSelected(AdapterView> arg0) {
cityCode = "029";
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
@Override
public void onItemSelected(AdapterView> arg0, View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
}
/**
* 公交站点查询结果回调
*/
@Override
public void onBusStationSearched(BusStationResult result, int rCode) {
if (rCode == 0) {
// busStationResult = result;
// stationItems = result.getBusStations();
// System.out.println("===========" + stationItems.size());
// for (int i = 0; i < stationItems.size(); i++) {
// System.out.println("===========" +
// stationItems.get(i).getBusStationName());
// }
if (result != null && result.getPageCount() > 0 && result.getBusStations() != null && result.getBusStations().size() > 0) {
busStationResult = result;
stationItems = result.getBusStations();
} else {
// ToastUtil.show(TraBuslineActivity.this, R.string.no_result);
}
} else if (rCode == 27) {
ToastUtil.show(TraBuslineActivity.this, R.string.error_network);
} else if (rCode == 32) {
ToastUtil.show(TraBuslineActivity.this, R.string.error_key);
} else {
ToastUtil.show(TraBuslineActivity.this, R.string.error_other);
}
}
/**
* 公交线路查询结果回调
*/
@Override
public void onBusLineSearched(BusLineResult result, int rCode) {
if (rCode == 0) {
if (result != null && result.getQuery() != null && result.getQuery().equals(busLineQuery)) {
if (result.getQuery().getCategory() == SearchType.BY_LINE_NAME) {
if (result.getPageCount() > 0 && result.getBusLines() != null && result.getBusLines().size() > 0) {
busLineResult = result;
lineItems = result.getBusLines();
}
} else if (result.getQuery().getCategory() == SearchType.BY_LINE_ID) {
aMap.clear();// 清理地图上的marker
busLineResult = result;
lineItems = busLineResult.getBusLines();
System.out.println("===========" + lineItems.get(0).getBusStations().size());
for (int i = 0; i < lineItems.get(0).getBusStations().size(); i++) {
System.out.println("===========" + lineItems.get(0).getBusStations().get(i).getBusStationName());
}
//获取经过的站点名称
ShowStationNameAdapter showStationNameAdapter = new ShowStationNameAdapter(TraBuslineActivity.this, lineItems.get(0)
.getBusStations());
show_name_station.setAdapter(showStationNameAdapter);
BusLineOverlay busLineOverlay = new BusLineOverlay(this, aMap, lineItems.get(0));
busLineOverlay.removeFromMap();
busLineOverlay.addToMap();
busLineOverlay.zoomToSpan();
}
} else {
ToastUtil.show(TraBuslineActivity.this, R.string.no_result);
}
} else if (rCode == 27) {
ToastUtil.show(TraBuslineActivity.this, R.string.error_network);
} else if (rCode == 32) {
ToastUtil.show(TraBuslineActivity.this, R.string.error_key);
} else {
ToastUtil.show(TraBuslineActivity.this, R.string.error_other);
}
}
@Override
public void initWidget() {
titleTv = (TextView) findViewById(R.id.titleText);
titleTv.setText("公交线路");
}
@Override
public void widgetClick(View v) {
// TODO Auto-generated method stub
}
}
四、有什么问题可以加
QQ群:261742462