SVG圖使用(筆記)

開發中遇到畫不規則的圖像的時候,我們往往通過自定義View,然後自己在onDraw去繪製可能會很麻煩。這時候我們就可以使用SVG圖,可以讓UI將需要實現的效果設計好導出爲SVG圖。

SVG圖的特性
  • svg可被非常多的工具讀取和修改
  • svg與JPEG和GIF比起來,尺寸更小,可壓縮性更強
  • svg是可伸縮的
  • svg圖可以在任何分辨率下被高質量的打印
  • svg可在圖像質量不下降的情況下被放大
  • svg圖像中的文本是可自定義的,可以融入代碼片段
  • svg是純粹的xml
svg語法

M = moveto(M X, Y):將畫筆移動到指定的座標位置
L = lineto(L X, Y):畫直線到指定的座標位置
H = horizontal lineto(H X):畫水平線到指定的X座標位置
V = verical lineto(V Y):畫垂直線到指定的Y座標位置
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞爾曲線
S = smooth curveto(S X2,Y2,ENDX,ENDY)
Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次貝塞爾曲線
Z = closepath():關閉路徑

例子:利用SVG圖繪製中國地圖

public class ChinaMap extends View {
    private Paint mPaint;
    private Context mContext;
    private int[] colorArray = new int[]{
            0xFF239BD7,
            0xFF30A9E5,
            0xFF80CBF1,
            0xff9B30FF,
            0xffe52302,
            0xff20fed0,
            0xffF08080,
            0xffEE6AA7,
            0xff9AFF9A

    };
    private List<ProvinceItem> itemList;
    private ProvinceItem select;
    private RectF totalRect;
    private float scale = 1.0f;


    public ChinaMap(Context context) {
        this(context, null);
    }

    public ChinaMap(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ChinaMap(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        itemList = new ArrayList<>();
        new Thread(runnable).start();
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);
            // dom解析
            //取得DocumentBuilderFactory實例
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = null;
            try {
                // 從DocumentBuilderFactory對象中中獲取DocumentBuilder實例
                builder = builderFactory.newDocumentBuilder();
                // 解析輸入流獲取Document
                Document document = builder.parse(inputStream);
                // 解析出根節點
                Element rootElement = document.getDocumentElement();
                // 解析出所有的path節點
                NodeList nodeList = rootElement.getElementsByTagName("path");
                float left = -1;
                float right = -1;
                float top = -1;
                float bottom = -1;
                List<ProvinceItem> list = new ArrayList<>();
                for (int i = 0; i < nodeList.getLength(); i++) {
                    Element element = (Element) nodeList.item(i);
                    String pathData = element.getAttribute("android:pathData");
                    Path path = PathParser.createPathFromPathData(pathData);
                    ProvinceItem provinceItem = new ProvinceItem(path);
                    provinceItem.setDrawColor(colorArray[i % colorArray.length]);
                    RectF rect = new RectF();
                    path.computeBounds(rect, true);
                    left = left == -1 ? rect.left : Math.min(left, rect.left);
                    right = right == -1 ? rect.right : Math.max(right, rect.right);
                    top = top == -1 ? rect.top : Math.min(top, rect.top);
                    bottom = bottom == -1 ? rect.bottom : Math.max(bottom, rect.bottom);
                    list.add(provinceItem);
                }
                itemList = list;
                // 因爲left,top最終獲取的是最小的,right,bottom最終獲取的是最大的。
                totalRect = new RectF(left, top, right, bottom);
                // 刷新界面
                Handler handler = new Handler(Looper.getMainLooper());
                handler.post(() -> {
                    requestLayout();
                    postInvalidate();
                });

            } catch (ParserConfigurationException | SAXException | IOException e) {
                e.printStackTrace();
            }
        }
    };

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 獲取到當前控件寬高值
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (totalRect != null) {
            double mapWidth = totalRect.width();
            // 根據寬度去計算縮放比
            scale = (float) (width / mapWidth);
        }

        setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (itemList == null || itemList.size() == 0) return;
        canvas.save();
        // 對畫布進行縮放
        canvas.scale(scale, scale);
        for (ProvinceItem provinceItem : itemList) {
            if (provinceItem != select) {
                provinceItem.drawItem(canvas, mPaint, false);
            } else {
                select.drawItem(canvas, mPaint, true);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (itemList == null) return false;
        ProvinceItem selectItem = null;
        for (ProvinceItem provinceItem : itemList) {
            if (provinceItem.isTouch(event.getX() / scale, event.getY() / scale)) {
                selectItem = provinceItem;
            }
        }
        if (selectItem != null) {
            select = selectItem;
            postInvalidate();
        }

        return super.onTouchEvent(event);
    }
}
public class ProvinceItem {
    private Path path;

    /**
     * 繪製顏色
     */
    private int drawColor;

    public ProvinceItem(Path path) {
        this.path = path;
    }

    public void setDrawColor(int drawColor) {
        this.drawColor = drawColor;
    }

    void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
        if (isSelect) {
            // 繪製內部的顏色
            paint.clearShadowLayer();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            canvas.drawPath(path, paint);

            // 繪製邊界
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(3);
            int strokeColor = 0xFFD0E8F4;
            paint.setColor(strokeColor);
            canvas.drawPath(path, paint);
        } else {

            paint.setStrokeWidth(2);
            paint.setColor(Color.BLACK);
            paint.setStyle(Paint.Style.FILL);
            paint.setShadowLayer(8, 0, 0, 0xffffff);
            canvas.drawPath(path, paint);

            // 繪製邊界
            paint.clearShadowLayer();
            paint.setColor(drawColor);
            paint.setStyle(Paint.Style.FILL);
            paint.setStrokeWidth(2);
            canvas.drawPath(path, paint);
        }


    }

    public boolean isTouch(float x, float y) {
        RectF rectF = new RectF();
        path.computeBounds(rectF, true);
        Region region = new Region();
        region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
        return region.contains((int) x, (int) y);
    }
}

在這裏插入圖片描述

代碼下載路徑:https://github.com/siliconvallley/Summarize

發佈了30 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章