Android UI SVG使用

Android UI SVG使用


1. SVG是什么?

  • 可缩放矢量图形(Scalable Vector Graphics)
  • SVG于2003 年1月14日成为 W3C 推荐标准

2. SVG特性

  • SVG可被很多工具读取和使用
  • SVG跟PNG,JPG,GIF图像比起来,尺寸更小,可压缩性更强
  • SVG是可伸缩的
  • SVG适配性好,任意放大缩小不失真

3. SVG使用场景

  • Android SDK23 APP的图标都是由SVG来制作的
  • 不规则控件,复杂的交互,子控件重叠判断,图标等都可以用SVG来做
  • 复杂路径动画等

4. SVG使用
自己制作SVG:https://editor.method.ac/ (在线SVG编辑器)
SVG文件下载:https://www.amcharts.com/download/ (比如中国地图等)
在这里插入图片描述
5.SVG常用方法
在这里插入图片描述

接下来利用SVG画一个中国地图,并且实现各个省份有可点击事件

6.涉及到知识点

  1. 自定义View (MapView.java)
public class MapView extends View {

    private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFFFFFFF};
    private Context context;
    private List<ProvinceItem> itemList;
    private Paint paint;
    private ProvinceItem select;
    private RectF totalRect;
    private float scale = 1.0f;

    public MapView(Context context) {
        super(context);
    }

    public MapView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        this.context = context;
        paint = new Paint();
        paint.setAntiAlias(true);
        itemList = new ArrayList<>();
        loadThread.start();
    }

    @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();
            // SVG缩放适配
            scale = (float) (width / mapWidth);
        }
        setMeasuredDimension(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

    }

    private Thread loadThread = new Thread() {
        @Override
        public void run() {
            final InputStream inputStream = context.getResources().openRawResource(R.raw.china);
            // 取得DocumentBuilderFactory实例
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();  
            //从factory获取DocumentBuilder实例
            DocumentBuilder builder = null; 
            try {
                builder = factory.newDocumentBuilder();
                Document doc = builder.parse(inputStream);   
                // 解析输入流 得到Document实例
                Element rootElement = doc.getDocumentElement();
                NodeList items = rootElement.getElementsByTagName("path");
                float left = -1;
                float right = -1;
                float top = -1;
                float bottom = -1;
                List<ProvinceItem> list = new ArrayList<>();
                // 解析所有省份SVG数据,然后将省份所对应字符串转换为Path绘制数据
                for (int i = 0; i < items.getLength(); i++) {
					// Dom解析xml
                    Element element = (Element) items.item(i);
                    String pathData = element.getAttribute("android:pathData");
                    @SuppressLint("RestrictedApi")
                    Path path = PathParser.createPathFromPathData(pathData);
                    ProvinceItem proviceItem = new ProvinceItem(path);
                    proviceItem.setDrawColor(colorArray[i % 4]);
                    // 通过计算地图所占最大矩形区域(左,上,右,下位置,来计算缩放scale)
                    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(proviceItem);
                }
                itemList = list;
                totalRect = new RectF(left, top, right, bottom);
				// 刷新界面
                Handler handler = new Handler(Looper.getMainLooper());
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        requestLayout();
                        invalidate();
                    }
                });

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 适配点击事件区域X,Y
        handleTouch(event.getX() / scale, event.getY() / scale);
        return super.onTouchEvent(event);
    }

    private void handleTouch(float x, float y) {
        if (itemList == null) {
            return;
        }
        ProvinceItem selectItem = null;
        for (ProvinceItem proviceItem : itemList) {
            if (proviceItem.isTouch(x, y)) {
                selectItem = proviceItem;
            }
        }
        if (selectItem != null) {
            select = selectItem;
            postInvalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (itemList != null) {
            canvas.save();
            canvas.scale(scale, scale);
            for (ProvinceItem proviceItem : itemList) {
                if (proviceItem != select) {
                    proviceItem.drawItem(canvas, paint, false);
                } else {
                    select.drawItem(canvas, paint, true);
                }
            }
        }
    }
}

  1. 封装每个省份信息对象
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.setStrokeWidth(1);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            canvas.drawPath(path, paint);
			// 绘制省份边界
            paint.setStyle(Paint.Style.STROKE);
            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);
        }
    }

	// 判断手指点击区域所在省份
	//  Android Region 区域点击
    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);
    }

}
  1. 最后需要一个 中国地图SVG,可从上面提供网站下载

7. 参考

  1. SVG—最简单的SVG动画
  2. SVG矢量动画机制
  3. geftimov/android-pathview
  4. android中svg介绍及使用[汇总]
  5. Android实现炫酷SVG动画效果
  6. SVGA 是一种跨平台的开源动画格式
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初心一点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值