本篇主要给大家写一个展示公交线路的自定义 view
第一次在简书上写文章, 有点紧张怎么办? 我的第一次就这样没了, 怎么办? 嘻嘻....('')
前言
step, 一步一个脚印不管做人还是做事, 都应该这样没有浮夸, 没有好高骛远, 根据计划安排脚踏实地一步一步做好每件事情成功也许离你不远了! 这是一个自定义 view, 一个高性能的 view(信不信你自己看着办, 哈哈哈...) 用于展示公交线路的每一个 station! 废话不多说, 直接上效果图
效果图
图 1:
2.jpg
图 2:
VerticalStepView 效果 02.png
看到上面的图是不是有些紧张? 别慌, 蛋定, 蛋定! 每个站都由几个部分组成:
绘制序列号背景圆圈
绘制序列号
绘制车站名称
绘制上转下
按照上列绘制顺序, 以
序列号背景圆圈的中心点
作为基点进行绘制
接下来我们看一下具体实现
实现
创建一个普通的自定义 view
我们给它命名为 VerticalStepView, 并创建两个画笔:
Paint paint---- 普通画笔
TextPaint textPaint---- 文字画笔
- VerticalStepView.java
- public class VerticalStepView extends View {
- // 普通画笔
- private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- // 文字画笔
- private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
- public VerticalStepView(Context context) {
- this(context, null);
- }
- public VerticalStepView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public VerticalStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
- }
创建数据模型 JavaBean 根据每个站的组成部分, 我们不难定义出数据模型, 我们给它命名 RouteViewPoint 数据模型里的每个字段基本上都有注释, 自行看注释理解
- RouteViewPoint.java
- public class RouteViewPoint {
- public static final int DEFAULT_TEXT_COLOR = 0xFF333333;
- private int key;// 标识键 (这个属性相当于一张数据表的主键字段, 辅助你完成一些其他的操作)
- private int basicX;// 基点 (X 轴方向坐标)
- private int basicY;// 基点 (Y 轴方向坐标)
- // 这是序列号的背景圆一些相关属性
- private float radius = 10;// 圆半径
- private int borderColor = DEFAULT_TEXT_COLOR;// 圆框颜色
- private int borderWidth = 2;// 圆框粗细
- private int backgroundColor = Color.WHITE;// 圆背景颜色
- private int distance;// 与前一个圆的距离
- // 序号
- private String index;
- private int indexColor = DEFAULT_TEXT_COLOR;
- private float indexSize;
- private boolean indexBold;
- // 公交
- private String transit;
- private int transitColor = DEFAULT_TEXT_COLOR;
- private float transitSize;
- private boolean transitBold;// 是否加粗显示
- // 上转下
- private String cursor;
- private int cursorColor = DEFAULT_TEXT_COLOR;
- private float cursorSize;
- private boolean cursorBold;// 是否加粗显示
- // 起点站终点
- private String label;
- private int labelColor = DEFAULT_TEXT_COLOR;
- private float labelSize;
- private boolean labelBold;// 是否加粗显示
- }
测量 VerticalStepView 的宽高我们要根据数据测量出 View 的宽高, 否则即使绘制完毕也看不见效果这里主要测量高度
数据集:
private List<RouteViewPoint> points = new ArrayList<>();
高度 = getPaddingTop() + 第一个圆的半径 + 所有圆之间的距离和 + 最后一个圆的半径 + getPaddingBottom()
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int offsetY = getPaddingTop();
- int startX = (int) (getPaddingLeft() + 10 + getMaxTransitWidth() + getMaxRadius() + 0.5f);
- if (points.size() > 1) {
- for (int i = 0; i < points.size(); i++) {
- RouteViewPoint p = points.get(i);
- if (i == 0)
- offsetY += p.getRadius();
- if (i > 0)
- offsetY += p.getDistance();
- p.setBasicX(startX);
- p.setBasicY(offsetY);
- if (i == points.size() - 1)
- offsetY += p.getRadius();
- }
- } else if (points.size() == 1) {
- RouteViewPoint p = points.get(0);
- offsetY += p.getRadius();
- p.setBasicY(offsetY);
- offsetY += p.getRadius();
- }
- offsetY += getPaddingBottom();
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(offsetY, MeasureSpec.EXACTLY);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
在
onDraw(Canvas canvas)
方法中绘制数据
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // 画连接起所有圆的那条竖线: 从第一个圆中心到最后一个圆的中心
- if (points.size() > 1) {
- paint.setStrokeWidth(lineWidth);
- paint.setStyle(Paint.Style.STROKE);
- paint.setColor(lineColor);
- canvas.drawLine(
- points.get(0).getBasicX(),
- points.get(0).getBasicY(),
- points.get(points.size() - 1).getBasicX(),
- points.get(points.size() - 1).getBasicY(),
- paint
- );
- }
- // 绘制所有数据
- for (RouteViewPoint p : points) {
- drawCircle(canvas, p, paint);
- drawIndex(canvas, p, textPaint);
- drawLabel(canvas, p, 10, textPaint);
- drawTransit(canvas, p, 10, textPaint);
- drawCursor(canvas, p, 4, textPaint);
- }
- }
绘制序列号背景圆圈:
- /**
- * 画圆
- *
- * @param canvas
- * @param p
- * @param paint
- */
- private void drawCircle(Canvas canvas, RouteViewPoint p, Paint paint) {
- if (p == null)
- return;
- paint.setColor(p.getBackgroundColor());
- paint.setStyle(Paint.Style.FILL);
- canvas.drawCircle(p.getBasicX(), p.getBasicY(), p.getRadius(), paint);
- paint.setColor(p.getBorderColor());
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(p.getBorderWidth());
- canvas.drawCircle(p.getBasicX(), p.getBasicY(), p.getRadius(), paint);
- }
绘制序列号:
- /**
- * 画序号
- *
- * @param canvas
- * @param p
- * @param textPaint
- */
- private void drawIndex(Canvas canvas, RouteViewPoint p, TextPaint textPaint) {
- if (p == null)
- return;
- String index = p.getIndex();
- if (index == null || index.length() == 0)
- return;
- textPaint.setColor(p.getIndexColor());
- textPaint.setTextSize(p.getIndexSize());
- textPaint.setTypeface(Typeface.defaultFromStyle(p.isIndexBold() ? Typeface.BOLD : Typeface.NORMAL));
- textPaint.getTextBounds(index, 0, index.length(), textBoundRect);
- Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
- int w = textBoundRect.right - textBoundRect.left;
- int h = textBoundRect.bottom - textBoundRect.top;
- float xStart = p.getBasicX() - w / 2.0f;
- float baseLine = p.getBasicY() - h / 2.0f + (h - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
- canvas.drawText(index, xStart, baseLine, textPaint);
- }
绘制站点名称:
- /**
- * 画标签
- *
- * @param canvas
- * @param p
- * @param marginLeft
- * 文字与圆左边的距离
- * @param textPaint
- */
- private void drawLabel(Canvas canvas, RouteViewPoint p, int marginLeft, TextPaint textPaint) {
- if (p == null)
- return;
- String label = p.getLabel();
- if (label == null || label.length() == 0)
- return;
- textPaint.setColor(p.getLabelColor());
- textPaint.setTextSize(p.getLabelSize());
- textPaint.setTypeface(Typeface.defaultFromStyle(p.isLabelBold() ? Typeface.BOLD : Typeface.NORMAL));
- textPaint.getTextBounds(label, 0, label.length(), textBoundRect);
- Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
- int w = textBoundRect.right - textBoundRect.left;
- int h = textBoundRect.bottom - textBoundRect.top;
- float xStart = p.getBasicX() + getMaxRadius() + marginLeft;
- float baseLine = p.getBasicY() - h / 2.0f + (h - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
- canvas.drawText(label, xStart, baseLine, textPaint);
- }
绘制上转下:
- /**
- * 画上转下
- *
- * @param canvas
- * @param p
- * @param marginRight
- * 文字与圆右边的距离
- * @param textPaint
- */
- private void drawTransit(Canvas canvas, RouteViewPoint p, int marginRight, TextPaint textPaint) {
- if (p == null)
- return;
- String transit = p.getTransit();
- if (transit == null || transit.length() == 0)
- return;
- textPaint.setColor(p.getTransitColor());
- textPaint.setTextSize(p.getTransitSize());
- textPaint.setTypeface(Typeface.defaultFromStyle(p.isTransitBold() ? Typeface.BOLD : Typeface.NORMAL));
- textPaint.getTextBounds(transit, 0, transit.length(), textBoundRect);
- Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
- int w = textBoundRect.right - textBoundRect.left;
- int h = textBoundRect.bottom - textBoundRect.top;
- float xStart = p.getBasicX() - w - p.getRadius() - marginRight;
- float baseLine = p.getBasicY() - h / 2.0f + (h - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
- canvas.drawText(transit, xStart, baseLine, textPaint);
- }
绘制游标:
- /**
- * 画游标
- *
- * @param canvas
- * @param p
- * @param marginLeft
- * 文字与圆右边的距离
- * @param textPaint
- */
- private void drawCursor(Canvas canvas, RouteViewPoint p, int marginLeft, TextPaint textPaint) {
- if (p == null)
- return;
- String cursor = p.getCursor();
- if (cursor == null || cursor.length() == 0)
- return;
- textPaint.setColor(p.getCursorColor());
- textPaint.setTextSize(p.getCursorSize());
- textPaint.setTypeface(Typeface.defaultFromStyle(p.isCursorBold() ? Typeface.BOLD : Typeface.NORMAL));
- textPaint.getTextBounds(cursor, 0, cursor.length(), textBoundRect);
- Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
- int w = textBoundRect.right - textBoundRect.left;
- int h = textBoundRect.bottom - textBoundRect.top;
- float xStart = p.getBasicX() + getMaxRadius() + marginLeft;
- float newBasicY = p.getBasicY() + p.getRadius() + h / 2.0f;
- float baseLine = newBasicY - h / 2.0f + (h - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
- // float xStart = p.getBasicX() + getMaxRadius() + marginLeft + 10 + rect.right - rect.left;
- // float baseLine = p.getBasicY() - h / 2.0f + (h - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
- canvas.drawText(cursor, xStart, baseLine, textPaint);
- }
自此, 整个 VeritcalStepView 就完成了
用法
这里使用百度地图 API 路线规划举个栗子:
- /**
- * 同城路线
- *
- * @param routeLine
- * @return
- */
- private List<RouteViewPoint> getSameCityPoints(MassTransitRouteLine routeLine) {
- float txt10 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, itemView.getResources().getDisplayMetrics());
- List<RouteViewPoint> points = new ArrayList<>();
- List<List<MassTransitRouteLine.TransitStep>> steps = routeLine.getNewSteps();
- for (int i = 0; i < steps.size(); i++) {
- List<MassTransitRouteLine.TransitStep> subSteps = steps.get(i);
- MassTransitRouteLine.TransitStep transitStep = subSteps.get(0);
- RouteViewPoint start = getBasePoint();
- RouteViewPoint end = getBasePoint();
- start.setIndex(String.valueOf(i + 1));
- end.setIndex(String.valueOf(i + 2));
- switch (transitStep.getVehileType()) {
- case ESTEP_WALK:
- start.setLabel("起点");
- start.setLabelColor(0xFF999999);
- RouteViewPoint center = getBasePoint();
- center.setKey(100);
- center.setRadius(10);
- center.setLabelColor(0xFF999999);
- center.setLabel(transitStep.getInstructions());
- center.setLabelSize(txt10);
- end.setLabel("终点");
- end.setLabelColor(0xFF999999);
- if (i == 0) {
- points.add(start);
- points.add(center);
- } else if (i == steps.size() - 1) {
- points.add(center);
- points.add(end);
- } else {
- points.add(center);
- }
- break;
- case ESTEP_TRAIN:
- case ESTEP_DRIVING:
- case ESTEP_COACH:
- case ESTEP_PLANE:
- case ESTEP_BUS:
- BusInfo busInfo = transitStep.getBusInfo();
- start.setKey(-1);
- start.setCursor(getBuses(subSteps));
- start.setLabel(getStationName(busInfo.getDepartureStation()));
- points.add(start);
- int stopNum = busInfo.getStopNum() - 1;
- if (stopNum < 0)
- stopNum = 0;
- for (int j = 0; j < stopNum; j++) {
- RouteViewPoint stopPoint = getBasePoint();
- stopPoint.setKey(101);
- stopPoint.setRadius(10);
- stopPoint.setBackgroundColor(0xFF00BA86);
- stopPoint.setBorderColor(0xFF00BA86);
- stopPoint.setLabelColor(0xFF999999);
- stopPoint.setLabel("");
- stopPoint.setLabelSize(txt10);
- points.add(stopPoint);
- }
- end.setKey(1);
- end.setLabel(getStationName(busInfo.getArriveStation()));
- points.add(end);
- break;
- }
- }
- for (int i = 0; i < points.size(); i++) {
- if (i > 0) {
- RouteViewPoint p = points.get(i);
- RouteViewPoint pre = points.get(i - 1);
- switch (p.getKey()) {
- case 101:
- p.setDistance(pre.getKey() == 101 ? 30 : 80);
- break;
- case 100:
- if (pre.getKey() < 100)
- p.setDistance(80);
- break;
- default:
- if (pre.getKey() == 101 ||
- pre.getKey() == 100)
- p.setDistance(80);
- break;
- }
- }
- }
- return points;
- }
- /**
- * 跨城路线
- * @param routeLine
- * @return
- */
- private List<RouteViewPoint> getDifferentCityPoints(MassTransitRouteLine routeLine) {
- float txt10 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, itemView.getResources().getDisplayMetrics());
- List<RouteViewPoint> points = new ArrayList<>();
- List<List<MassTransitRouteLine.TransitStep>> steps = routeLine.getNewSteps();
- for (int i = 0; i < steps.size(); i++) {
- List<MassTransitRouteLine.TransitStep> subSteps = steps.get(i);
- for (int j = 0; j < subSteps.size(); j++) {
- MassTransitRouteLine.TransitStep transitStep = subSteps.get(j);
- RouteViewPoint start = getBasePoint();
- RouteViewPoint end = getBasePoint();
- start.setIndex(String.valueOf(i + 1));
- end.setIndex(String.valueOf(i + 2));
- switch (transitStep.getVehileType()) {
- case ESTEP_WALK:
- start.setLabel("起点");
- start.setLabelColor(0xFF999999);
- RouteViewPoint center = getBasePoint();
- center.setKey(100);
- center.setRadius(10);
- center.setLabelColor(0xFF999999);
- center.setLabel(transitStep.getInstructions());
- center.setLabelSize(txt10);
- end.setLabel("终点");
- end.setLabelColor(0xFF999999);
- if (i == 0) {
- points.add(start);
- points.add(center);
- } else if (i == steps.size() - 1) {
- points.add(center);
- points.add(end);
- } else {
- points.add(center);
- }
- break;
- case ESTEP_TRAIN:
- case ESTEP_DRIVING:
- case ESTEP_COACH:
- case ESTEP_PLANE:
- case ESTEP_BUS:
- BusInfo busInfo = transitStep.getBusInfo();
- start.setKey(-1);
- start.setCursor(getBuses(subSteps));
- start.setLabel(getStationName(busInfo.getDepartureStation()));
- points.add(start);
- int stopNum = busInfo.getStopNum() - 1;
- if (stopNum < 0)
- stopNum = 0;
- for (int k = 0; k < stopNum; k++) {
- RouteViewPoint stopPoint = getBasePoint();
- stopPoint.setKey(101);
- stopPoint.setRadius(10);
- stopPoint.setBackgroundColor(0xFF00BA86);
- stopPoint.setBorderColor(0xFF00BA86);
- stopPoint.setLabelColor(0xFF999999);
- stopPoint.setLabel("");
- stopPoint.setLabelSize(txt10);
- points.add(stopPoint);
- }
- end.setKey(1);
- end.setLabel(getStationName(busInfo.getArriveStation()));
- points.add(end);
- break;
- }
- }
- }
- for (int i = 0; i < points.size(); i++) {
- if (i > 0) {
- RouteViewPoint p = points.get(i);
- RouteViewPoint pre = points.get(i - 1);
- switch (p.getKey()) {
- case 101:
- p.setDistance(pre.getKey() == 101 ? 30 : 80);
- break;
- case 100:
- if (pre.getKey() < 100)
- p.setDistance(80);
- break;
- default:
- if (pre.getKey() == 101 ||
- pre.getKey() == 100)
- p.setDistance(80);
- break;
- }
- }
- }
- return points;
- }
- private RouteViewPoint getBasePoint() {
- float textSize10 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, itemView.getResources().getDisplayMetrics());
- float textSize12 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12, itemView.getResources().getDisplayMetrics());
- RouteViewPoint p = new RouteViewPoint();
- p.setDistance(120);
- p.setRadius(20);
- p.setBackgroundColor(0xFFCCCCCC);
- p.setBorderColor(0xFFCCCCCC);
- p.setIndexSize(textSize10);
- p.setCursorSize(textSize12);
- p.setCursorColor(0xFFFF00FF);
- p.setTransitSize(textSize12);
- p.setTransitColor(0xFF999999);
- p.setLabelSize(textSize12);
- return p;
- }
- private String getBuses(List<MassTransitRouteLine.TransitStep> subSteps) {
- if (subSteps == null || subSteps.isEmpty())
- return "";
- StringBuilder builder = new StringBuilder();
- builder.append("乘坐 |");
- for (int i = 0; i < subSteps.size(); i++) {
- BusInfo busInfo = subSteps.get(i).getBusInfo();
- builder.append(busInfo.getName().replace("路", ""));
- if (i < subSteps.size() - 1)
- builder.append(" ");
- }
- return builder.toString();
- }
- private String getStationName(String station) {
- if (station == null || station.length() == 0)
- return "";
- if (station.endsWith("站站"))
- return station.substring(0, station.length() - 1);
- else
- return station;
- }
MassTransitRouteLine 这个是百度地图 API 里的跨城路线规划的 JavaBean
源码
下载源码: https://github.com/JustinRoom/JscKit/tree/master/VerticalStepView
来源: http://www.jianshu.com/p/7721572fe13c