工作生活

SVG 矢量图的加载

2019-07-05  本文已影响0人  石器时代小古董

一、什么是 SVG

SVG 是可缩放矢量图形,用户可以用代码来直接描绘图像。区别于 JPG 和 PNG 的需要用引擎来加载的图片,它直接用画布绘制,所以是无损失的。

二、SVG 的优点

  1. SVG文件时纯粹的XML 可以用很多工具打开编辑
    2. SVG比其他的图片都要更小
    3. SVG是一种可伸缩的图片
    4. SVG 图像可以在任何分辨率下轻松打印
    5. SVG 可以自定义

三、它能做什么

1.APP的图表
2.自定义的复杂动画控件
3.复杂的动画

四、如何获得 SVG

使用 PhotoShop 就可以将图片转换成 SVG 的图片。

五、SVG 常用标识

PathData:图片数据边界

六、如何使用 SVG

一个 SVG 的图片效果如下


image.png

SVG 图片实际上是xml的格式,所以加载一个 SVG 可以使用 DocumentBuilderFactory 对象,关注它的Path节点,这个节点点的pathData元素代表着要绘制的路径。使用 Android 原生的 DocumentBuilderFactory 类可以解析 xml。

final InputStream inputStream = context.getResources().openRawResource(R.raw.china);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  //取得DocumentBuilderFactory实例
DocumentBuilder builder = null; //从factory获取DocumentBuilder实例
try {
          builder = factory.newDocumentBuilder();
          Document doc = builder.parse(inputStream);   //解析输入流 得到Document实例
          Element rootElement = doc.getDocumentElement();
         // 获取所有 path 节点的内容
          NodeList items = rootElement.getElementsByTagName("path")
           for (int i = 0; i < items.getLength(); i++) {
                    Element element = (Element) items.item(i);
                    // 获取其中 pathData 元素的内容
                    String pathData = element.getAttribute("android:pathData");
                   ........
 }

Android中提供了 PathParser 对象来解析 pathData 数据,将这个数据转换成 android 的 Path 对象

Path path = PathParser.createPathFromPathData(pathData);

拿到Path对象以后就可直接通过canvas的drawPath方法来直接绘制

canvas.drawPath(path,paint);

七、如何给svg矢量图添加点击事件

1.首先需要判断点击事件是否作用在 Path 的区域
2.如果在这个区域内容我们在onTouchEvent里面可以触发事件。

android 提供了 Region 对象来描述一个 Path 的范围,并且提供了 contains 函数判断某一个点是否在这个区域内

public boolean isContains(float x, float y, Path path){
   RectF rect = new RectF();
   path.computeBounds(rectF,true);
   Region region = new Region();
   region.setPath(path,new Region(rect.left,rect.top,rect.right,rect.bottom));
   return region.contains((int)x,(int)y);
}
public boolean onTouchEvent(MotionEvent event){
    if (isContains(event.getX(),event.getY())){
       mListeners.onClickListener();
    }
}

八、适配 SVG 的自定义 View

SVG 图片可能不能适应画布的大小,所以需要针对画布进行适配,适配的方案是计算 SVG 和 自定义 View 的计算缩放比,在加载 SVG 图片并解析 Path 的时候就计算出这个 SVG 图片的宽度,因为 SVG 可能分成多个 Path。可以在加载的时候就求出每个Path对应的范围,找到 left 和 top 的最小值,right 和 bottom 最小值

 int left = -1;
 int right = -1;
 int top = -1;
 int bottom = -1;
  for (int i = 0; i < items.getLength(); i++) {
                   ......
                    Path path = PathParser.createPathFromPathData(pathData);
                    ProviceItem proviceItem = new ProviceItem(path);
                    proviceItem.setDrawColor(colorArray[i % 4]);
                    RectF rect = new RectF();
                    path.computeBounds(rect, true);

                    // 求左上右下的极值 然后和View 做比较进行适配
                    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);
 }
  totalRect = new RectF(left, top, right, bottom);

在 onMeasure 的时候计算出缩放比例

 @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) {
            canvas.save();
            // 对整个画布缩放
            canvas.scale(scale, scale);
            ......
        }

    }

同时onTouch事件的位置也要进行缩放处理

public boolean onTouchEvent(MotionEvent event){
    if (isContains(event.getX()/scale,event.getY()/scale)){
       mListeners.onClickListener();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读