自定義View——地鐵顯示牌效果

預覽效果

在這裏插入圖片描述

目錄

SubwayBoardView.java

代碼

public class SubwayBoardView extends View {

    private Paint bgPaint, tbPaint, centerBgPaint, centerRingPaint, centerCirclePaint, centerCircleRingPaint, noStationPaint, stationPaint, doorPaint;

    private TextPaint centerTextPaint, stationTextPaint, currentStationTextPaint, doorTextPaint;

    private float barHeight = DensityUtil.dp2Px(getContext(), 20);

    private float centerCircleWidth;
    private float centerRingWidth;
    private float centerCircleRingStrokeWidth = DensityUtil.dp2Px(getContext(), 5);
    private float centerRingStrokeWidth = DensityUtil.dp2Px(getContext(), 36);

    private float centerCircleRingSweepAngle = 0f;
    private ObjectAnimator centerCircleRingAnim;

    private List<String> noStationStrs = new ArrayList<>();
    private List<String> stationStrs = new ArrayList<>();
    private String currentStationStrs = "杭州站";
    private Bitmap doorBitmap;
    private Camera camera;

    public SubwayBoardView(Context context) {
        super(context);
        initView();
    }

    public SubwayBoardView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public SubwayBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        //全背景
        bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        bgPaint.setStyle(Paint.Style.FILL);
        bgPaint.setColor(Color.parseColor("#85919a"));

        //上下邊欄
        tbPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        tbPaint.setStyle(Paint.Style.FILL);
        tbPaint.setColor(Color.parseColor("#c21b2c"));

        //中間欄
        centerBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        centerBgPaint.setStyle(Paint.Style.FILL);
        centerBgPaint.setColor(Color.parseColor("#92a3d1"));

        //中間空白圓環區域
        centerRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        centerRingPaint.setStyle(Paint.Style.STROKE);
        centerRingPaint.setStrokeWidth(centerRingStrokeWidth);
        centerRingPaint.setColor(Color.parseColor("#85919a"));

        //中間圓
        centerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        centerCirclePaint.setStyle(Paint.Style.FILL);
        centerCirclePaint.setColor(Color.parseColor("#c21b2c"));

        //中間圓邊上的圓環
        centerCircleRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        centerCircleRingPaint.setStyle(Paint.Style.STROKE);
        centerCircleRingPaint.setStrokeWidth(centerCircleRingStrokeWidth);
        centerCircleRingPaint.setStrokeCap(Paint.Cap.ROUND);
        centerCircleRingPaint.setColor(Color.parseColor("#6e8ca6"));

        //中間文字
        centerTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        centerTextPaint.setStyle(Paint.Style.FILL);
        centerTextPaint.setFakeBoldText(true);
        centerTextPaint.setColor(Color.parseColor("#333333"));
        centerTextPaint.setTextAlign(Paint.Align.CENTER);
        centerTextPaint.setShadowLayer(3, 3, 3, Color.parseColor("#6e8ca6"));
        centerTextPaint.setTextSize(DensityUtil.sp2px(getContext(), 24));

        //未到達的站
        noStationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        noStationPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        noStationPaint.setColor(Color.parseColor("#c21b2c"));

        //未到站文字
        stationTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        stationTextPaint.setStyle(Paint.Style.FILL);
        stationTextPaint.setColor(Color.parseColor("#333333"));
        stationTextPaint.setTextAlign(Paint.Align.CENTER);
        stationTextPaint.setShadowLayer(3, 3, 3, Color.parseColor("#6e8ca6"));
        stationTextPaint.setTextSize(DensityUtil.sp2px(getContext(), 18));

        noStationStrs.add("寧波站");
        noStationStrs.add("上虞站");
        noStationStrs.add("紹興站");

        //已到達的站
        stationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        stationPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        stationPaint.setColor(Color.parseColor("#7586b2"));

        stationStrs.add("南京站");
        stationStrs.add("蘇州站");
        stationStrs.add("上海站");

        //到站文字
        currentStationTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        currentStationTextPaint.setStyle(Paint.Style.FILL);
        currentStationTextPaint.setFakeBoldText(true);
        currentStationTextPaint.setColor(Color.parseColor("#3d5d9a"));
        currentStationTextPaint.setTextAlign(Paint.Align.LEFT);
        currentStationTextPaint.setTextSize(DensityUtil.sp2px(getContext(), 18));

        doorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        doorBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.open_door);

        doorTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        doorTextPaint.setStyle(Paint.Style.FILL);
        doorTextPaint.setColor(Color.parseColor("#c21b2c"));
        doorTextPaint.setTextAlign(Paint.Align.LEFT);
        doorTextPaint.setTextSize(DensityUtil.sp2px(getContext(), 14));

        camera = new Camera();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();

        int centerX = width / 2;
        int centerY = height / 2;

        //計算中間空白圓形寬度
        if (0 == centerRingWidth) {
            centerRingWidth = (height - DensityUtil.dp2Px(getContext(), 12)) * 1f / 2;
        }
        //計算中間圓的半徑
        if (0 == centerCircleWidth) {
            centerCircleWidth = centerRingWidth - DensityUtil.dp2Px(getContext(), 8);
        }

        //背景
        canvas.drawRect(0, 0, width, height, bgPaint);

        //上下欄
        canvas.drawRect(0, 0, width, barHeight, tbPaint);
        canvas.drawRect(0, height - barHeight, width, height, tbPaint);

        //中間圓環空白區域
        canvas.drawCircle(centerX, centerY, centerRingWidth, centerRingPaint);

        //中間欄
        float centerLineT = barHeight + DensityUtil.dp2Px(getContext(), 10);
        float centerLineB = height - barHeight - DensityUtil.dp2Px(getContext(), 10);
        canvas.drawRect(0, centerLineT, width, centerLineB, centerBgPaint);

        //中間圓
        canvas.drawCircle(centerX, centerY, centerCircleWidth, centerCirclePaint);

        //中間圓環
        if (centerCircleRingSweepAngle > 0) {
            canvas.drawArc(centerX - centerCircleWidth - (centerCircleRingStrokeWidth / 2), centerY - centerCircleWidth - (centerCircleRingStrokeWidth / 2), centerX + centerCircleWidth + (centerCircleRingStrokeWidth / 2), centerY + centerCircleWidth + (centerCircleRingStrokeWidth / 2), -90f, centerCircleRingSweepAngle, false, centerCircleRingPaint);
        }

        //中間文字
        Paint.FontMetrics fontMetrics = centerTextPaint.getFontMetrics();
        float dx = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        canvas.drawText(currentStationStrs, centerX, centerY + dx, centerTextPaint);

        //未到站
        float stationStart = DensityUtil.dp2Px(getContext(), 20);
        float stationWidth = DensityUtil.dp2Px(getContext(), 40);
        float stationPadding = DensityUtil.dp2Px(getContext(), 20);
        for (int i = 0; i < noStationStrs.size(); i++) {
            canvas.drawPath(getStationView(stationStart + (stationWidth + stationPadding) * i, stationWidth, centerLineT, centerLineB), noStationPaint);

            //保存畫布
            canvas.save();
            String stationStr = noStationStrs.get(i);
            Paint.FontMetrics fm = stationTextPaint.getFontMetrics();
            //文字高度
            float fontHeight = (fm.bottom - fm.top) * stationStr.length();
            //顯示高度
            float showHeigth = centerLineB - centerLineT;
            //移動畫布
            canvas.translate(stationStart + (stationWidth + stationPadding) * i + stationWidth / 3, centerLineT + (showHeigth - fontHeight) / 2);
            float strWidth = stationTextPaint.measureText(stationStr) / stationStr.length();
            StaticLayout staticLayout;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                staticLayout = StaticLayout.Builder.obtain(stationStr, 0, stationStr.length(), stationTextPaint, (int) strWidth).build();
            } else {
                staticLayout = new StaticLayout(stationStr, stationTextPaint, (int) strWidth, Layout.Alignment.ALIGN_CENTER, 1, 0, true);
            }
            //繪製
            staticLayout.draw(canvas);
            //還原畫布
            canvas.translate(-stationStart + (stationWidth + stationPadding) * i, -centerLineT);
            canvas.restore();
        }

        //已過站
        float stationEnd = getWidth() - DensityUtil.dp2Px(getContext(), 20) - stationWidth;
        for (int i = 0; i < stationStrs.size(); i++) {
            canvas.drawPath(getStationView(stationEnd - (stationWidth + stationPadding) * i, stationWidth, centerLineT, centerLineB), stationPaint);

            //保存畫布
            canvas.save();
            String stationStr = noStationStrs.get(i);
            Paint.FontMetrics fm = stationTextPaint.getFontMetrics();
            //文字高度
            float fontHeight = (fm.bottom - fm.top) * stationStr.length();
            //顯示高度
            float showHeigth = centerLineB - centerLineT;
            //移動畫布
            canvas.translate(stationEnd - (stationWidth + stationPadding) * i + stationWidth / 3, centerLineT + (showHeigth - fontHeight) / 2);
            float strWidth = stationTextPaint.measureText(stationStr) / stationStr.length();
            StaticLayout staticLayout;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                staticLayout = StaticLayout.Builder.obtain(stationStr, 0, stationStr.length(), stationTextPaint, (int) strWidth).build();
            } else {
                staticLayout = new StaticLayout(stationStr, stationTextPaint, (int) strWidth, Layout.Alignment.ALIGN_CENTER, 1, 0, true);
            }
            //繪製
            staticLayout.draw(canvas);
            //還原畫布
            canvas.translate(-stationStart + (stationWidth + stationPadding) * i, -centerLineT);
            canvas.restore();
        }

        //到達站
        String curentStr = "停靠站" + currentStationStrs;
        float fontwidth = stationTextPaint.measureText(curentStr) / curentStr.length();
        float pointX = centerX - centerRingWidth - fontwidth * 3 - DensityUtil.dp2Px(getContext(), 26);
        Paint.FontMetrics fm = stationTextPaint.getFontMetrics();
        float pointY = centerLineT + ((centerLineB - centerLineT) - (fm.bottom - fm.top) * 2) / 2;
        canvas.save();
        canvas.translate(pointX, pointY);
        StaticLayout staticLayout;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            staticLayout = StaticLayout.Builder.obtain(curentStr, 0, curentStr.length(), stationTextPaint, (int) (fontwidth * 3)).build();
        } else {
            staticLayout = new StaticLayout(curentStr, stationTextPaint, (int) (fontwidth * 3), Layout.Alignment.ALIGN_CENTER, 1, 0, true);
        }
        //繪製
        staticLayout.draw(canvas);
        canvas.translate(-pointX, -centerLineT);
        canvas.restore();

        //開門提示
        String primt = "注意開門";
        float doorTextWidth = doorTextPaint.measureText(primt);
        Paint.FontMetrics doorTextFm = doorTextPaint.getFontMetrics();
        float doorTextheight = doorTextFm.bottom - doorTextFm.top;
        float dy = doorTextheight / 2 - doorTextFm.bottom;
        int doorTextLeft = (int) (centerX + centerRingWidth + DensityUtil.dp2Px(getContext(), 26));
        Rect rect = new Rect();
        rect.left = (int) (doorTextLeft + ((doorTextWidth - doorBitmap.getWidth()) / 2));
        rect.top = (int) (centerLineT + ((centerLineB - centerLineT) - (doorBitmap.getHeight() + DensityUtil.dp2Px(getContext(), 6) +  + doorTextheight)) / 2);
        rect.right = rect.left + doorBitmap.getWidth();
        rect.bottom = rect.top + doorBitmap.getHeight();
        //旋轉
        canvas.save();
        camera.save();
        canvas.translate(rect.left, rect.top);
        camera.rotateY(-45);
        camera.applyToCanvas(canvas);
        canvas.translate(-rect.left, -rect.top);
        camera.restore();
        canvas.drawBitmap(doorBitmap, null, rect, doorPaint);
        canvas.restore();
        canvas.drawText(primt, doorTextLeft, rect.bottom + DensityUtil.dp2Px(getContext(), 6) + (doorTextheight / 2) + dy, doorTextPaint);
    }

    /**
     * 獲取站信息
     *
     * @param pl
     * @param width
     * @param centerLineT
     * @param centerLineB
     * @return
     */
    private Path getStationView(float pl, float width, float centerLineT, float centerLineB) {
        float pt = centerLineT;
        float pr = pl + width;
        float pb = centerLineB;
        float r = (pr - pl) / 3;
        Path path = new Path();
        path.moveTo(pl, pt);
        path.lineTo(pr, pt);
        path.quadTo(pr - r, pt + (pb - pt) / 2, pr, pb);
        path.lineTo(pl, pb);
        path.quadTo(pl - r, pt + (pb - pt) / 2, pl, pt);
        path.close();
        return path;
    }

    public void setCenterCircleRingSweepAngle(float centerCircleRingSweepAngle) {
        this.centerCircleRingSweepAngle = centerCircleRingSweepAngle;
        invalidate();
    }

    /**
     * 開始中間圓動畫
     */
    public void animCenterCircleRing() {
        if (null == centerCircleRingAnim) {
            centerCircleRingAnim = ObjectAnimator.ofFloat(this, "centerCircleRingSweepAngle", 0f, 360f);
            centerCircleRingAnim.setDuration(3000);
            centerCircleRingAnim.setInterpolator(new LinearInterpolator());
            centerCircleRingAnim.setRepeatCount(ValueAnimator.INFINITE);
            centerCircleRingAnim.setRepeatMode(ValueAnimator.RESTART);
        }
        centerCircleRingAnim.start();
    }

    /**
     * 停止
     */
    public void stopAnimCenterCircleRing() {
        if (null != centerCircleRingAnim) {
            centerCircleRingAnim.cancel();
        }
        setCenterCircleRingSweepAngle(0);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章