【问答】用SVG矢量图形自定义不规则控件——中国地图

1、提出问题

在必问上有一位同学提出了关于不规则项的绘制问题,具体可参照如下链接:https://biwen.csdn.net/question/3399

2、分析问题

■ 笔者的分析:
由于指定的图形为不规则的,同时需要根据数据对不规则项设置不同的颜色,滑动到不同的不规则项还需要改变背景色。其中的不规则,就需要开发者去绘制去自定义。

在做复杂的自定义动画的时候,我们的开发者会先定义Path来规划动画的路径。然后再在不同的节点去设置动画。本例中其实也可以参照这个思想去解决问题。矢量图标SVG其实就是定义多个PathData绘制图形的。

■ SVG
在解决问题之前,我们准备一下关于SVG的知识:
https://www.w3school.com.cn/svg/index.asp

由于我们需要解析路径数据PathData,先了解一下:

  • M = moveto(M X,Y) :将画笔移动到指定的坐标位置
  • L =lineto(L X,Y) :画直线到指定的坐标位置
  • H = horizontal lineto(H X):画水平线到指定的X坐标位置
  • V = vertical 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):二次贝赛曲线
  • T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射
  • A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
  • Z = closepath():关闭路径

3、解决问题

3.1、数据准备

通过图形的SVG文件和分析数据,生成模拟数据供程序调用。

3.2、架构设计

下面对这个业务进行架构设计。

(1)全体绘制接口

该接口主要就是对不规则View的抽象,透过接口我们可以知道测量指定图形的宽和高,需要

  • 根据路径数据,测量宽和高
  • 初始数据和刷新数据
  • 绘制图像
  • 管理事件
public interface MapDrawer {
   
	// 刷新数据
    void setData(MapConfigEntity config, Map<String, Integer> data);
    // 测量宽高
    VectorManager.Viewport getViewport();
    // 绘制
    void onDraw(Canvas canvas, Paint paint);
    // 事件管理
    boolean onTouch(MapPointEntity dataPoint);
}
(2)单个绘制接口

单个不规则图形接口需要实现如下功能。

  • 绘制文字
  • 绘制区域
  • 根据路径数据,判断是否在区域内。
public interface MapItemDrawer {
   
    void drawText(Canvas canvas, Paint paint);
    void drawRegion(Canvas canvas, Paint paint);
    boolean checkIsSelected(float x, float y);
    Path getPath();
}
(3)样式定义

根据自己的需要实现MapStyleRuler的接口。

MapStyleRuler

  • MapNormalStyle(默认状态)
  • MapSelectStyle(选中状态)
public interface MapStyleRuler {
   
    MapTextSelector.MapTextStyle getTextStyle();
    MapShapeSelector.MapShapeStyle getShapeStyle();
}
3.3、功能实现

下面对功能实现做说明。

(1)模拟数据读取

从文件读取数据或者从服务端都是耗时操作,所以需要开启子线程去读取。
这里只是模拟获取数据,就简单的封装了AsyncTask,文件读取的时候可以直接使用。
服务端读取数据,可以用Retrofit+LiveData或者Retrofit+RxJava实现。

public class MapActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
   
    // 启动获取数据线程。
    private void initData() {
   
        MapMockDataFactory factory = new MapMockDataFactory(this,
                new MapMockDataFactory.Callback() {
   
                    @Override
                    public void onFinish(MapConfigEntity config, MapDataEntity data) {
   
                        mConfig = config;
                        mData = data;
                        splitData();
                    }
                });
        factory.execute();
    }
}

// 封装AsyncTask,用于读取文件等耗时操作。
public class MapMockDataFactory extends AsyncTask<Void, Void, Void> {
   
    private WeakReference<Context> mContext;
    private MapConfigEntity mConfig;
    private MapDataEntity mData;
    private Callback mCallback;

    public interface Callback {
   
        void onFinish(MapConfigEntity config, MapDataEntity data);
    }

    public MapMockDataFactory(Context context, Callback callback) {
   
        this.mContext = new WeakReference<>(context.getApplicationContext());
        this.mCallback = callback;
    }

    @Override
    protected Void doInBackground(Void... voids) {
   
        mConfig = getMockMapConfig(mContext.get());
        mData = getMockMapData(mContext.get());
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
   
        super.onPostExecute(aVoid);
        mCallback.onFinish(mConfig, mData);
    }

    private MapConfigEntity getMockMapConfig(final Context context) {
   
        MapConfigEntity entity = null;
        try {
   
            InputStream inputStream = context.getApplicationContext()
                    .getResources()
                    .openRawResource(R.raw.map_config);
            InputStreamReader reader = new InputStreamReader(inputStream);
            Gson gson = new Gson();
            entity = gson.fromJson(reader, MapConfigEntity.class);
        } catch (Exception e) {
   
            e.printStackTrace();
        }
        return entity;
    }

    private MapDataEntity getMockMapData(final Context context) {
   
        MapDataEntity entity = null;
        try {
   
            InputStream inputStream = context.getApplicationContext()
                    .getResources()
                    .openRawResource(R.raw.map_data);
            InputStreamReader reader = new InputStreamReader(inputStream);
            Gson gson = new Gson()
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要修改SVG图形的背景色,可以使用CSS样式。以下是一些方法: 1. 在SVG代码中添加样式属性:可以在SVG代码中添加样式属性来修改背景颜色。例如: ``` <svg width="100" height="100"> <rect x="0" y="0" width="100" height="100" style="fill: #f00;" /> <circle cx="50" cy="50" r="40" /> </svg> ``` 在上面的代码中,我们使用`<rect>`元素创建一个矩形,并使用`style`属性将其背景颜色设置为红色。 2. 使用CSS选择器:可以使用CSS选择器来选择SVG元素,并为其添加样式。例如: ``` <style> #background { fill: #f00; } </style> <svg width="100" height="100"> <rect id="background" x="0" y="0" width="100" height="100" /> <circle cx="50" cy="50" r="40" /> </svg> ``` 在上面的代码中,我们使用CSS选择器`#background`来选择`<rect>`元素,并将其背景颜色设置为红色。 3. 使用外部CSS文件:可以将SVG代码保存在一个单独的文件中,并使用外部CSS文件来为其添加样式。例如: ``` <!-- SVG 文件:background.svg --> <svg width="100" height="100"> <rect id="background" x="0" y="0" width="100" height="100" /> <circle cx="50" cy="50" r="40" /> </svg> <!-- CSS 文件:style.css --> #background { fill: #f00; } ``` 在上面的代码中,我们将SVG代码保存在`background.svg`文件中,并将样式保存在`style.css`文件中。通过将`style.css`文件链接到`background.svg`文件中,我们可以为SVG添加样式。 无论使用哪种方法,都可以通过修改`fill`属性来修改SVG元素的背景颜色。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值