Android自定义控件实现不规则区域点击事件

时间:2022-06-01 22:16:32

本文实例为大家分享了Android实现不规则区域点击事件的具体代码,供大家参考,具体内容如下

先看看效果

Android自定义控件实现不规则区域点击事件

对于上面的图形实现主要用到svg,通过解析svg获取不规则的图形,对于svg文件这个一般需要美工提供,不需要我们开发实现。

实现上面效果第一步是解析svg文件代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package demo.zjd.com.*demo.utils;
 
import android.graphics.RectF;
import android.util.Xml;
 
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
 
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
 
import demo.zjd.com.*demo.bean.CityPath;
import demo.zjd.com.*demo.bean.ViewAttr;
import demo.zjd.com.*demo.calback.ParserCallBack;
 
/**
 * Created by zhangjd on 2017/6/1.
 * 解析svg xml
 */
 
public class SVGXmlParserUtils {
 
  public static void parserXml(final InputStream in, final ParserCallBack mParserCallBack){
    new Thread(new Runnable() {
      @Override
      public void run() {
        List<CityPath> list=new ArrayList<>();
        ViewAttr mViewAttr=new ViewAttr();
        parserXml(in,list,mViewAttr);
        if(mParserCallBack!=null){
          mParserCallBack.callback(list,mViewAttr);
        }
      }
    }).start();
  }
 
  private static void parserXml(InputStream in, List<CityPath> list, ViewAttr mViewAttr){
    XmlPullParser parser = Xml.newPullParser();
    RectF mRectF=new RectF();
    try {
      parser.setInput(in, "UTF-8");
      int eventType = parser.getEventType();
      String name = null;
      CityPath mCityPath = null;
      list.clear();
      while (eventType != XmlPullParser.END_DOCUMENT) {
        switch (eventType) {
          case XmlPullParser.START_DOCUMENT:// 文档开始事件,可以进行数据初始化处理
            break;
          case XmlPullParser.START_TAG:// 开始元素事件
            name = parser.getName();
            if ("path".equals(name)) {
              mCityPath = new CityPath();
              mCityPath.setId(parser.getAttributeValue(null, "id"));
              mCityPath.setTitle(parser.getAttributeValue(null, "title"));
              mCityPath.setPathData(parser.getAttributeValue(null, "d"));
            }
            break;
          case XmlPullParser.END_TAG:// 结束元素事件
            name = parser.getName();
            if ("path".equals(name)) {//这个地方主要处理屏幕适配问题,后面后详细讲解
              mCityPath.initPath();
              //处理path的边界
              //计算控制点的边界
              mCityPath.getmPath().computeBounds(mRectF, true);
              mViewAttr.colSize(mRectF);
              list.add(mCityPath);
            }
            break;
        }
        eventType = parser.next();
      }
    } catch (XmlPullParserException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if(in!=null){
        try {
          in.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
 
  }
}

解析完svg文件之后就是绘制图像代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (list == null) {
      return;
    }
    //    Matrix mMatrix = new Matrix();
//    mMatrix.postScale(0.5f,0.5f);
//    mMatrix.setScale(0.5f,0.5f);//这个地方要用concat方法不能用这个方法
//    canvas.concat(mMatrix);
    //上面的方法也可以
//    canvas.restore();
    canvas.scale(scale, scale);
    canvas.drawColor(Color.YELLOW);
    for (int i = 0; i < list.size(); i++) {
      CityPath path = list.get(i);
      //绘制边的颜色
      mPaint.setStrokeWidth(2);
      mPaint.setStyle(Paint.Style.STROKE);
      mPaint.setColor(Color.GRAY);
      canvas.drawPath(path.getmPath(), mPaint);
    }
    if (mPath != null) {//mPath代表的是选中区域的path,如果不为空则一点击选中区域了
      mPaint.setStrokeWidth(1);
      mPaint.setStyle(Paint.Style.FILL);
      mPaint.setColor(Color.GREEN);
      mPaint.setShadowLayer(8,2,2,Color.BLACK);
      canvas.drawPath(mPath, mPaint);
    }
    mPaint.clearShadowLayer();
 
}

实现上面的方法就可以会出一个地图了,但是没有点击事件,接下来实现点击事件代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public boolean onTouchEvent(MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {//点击的时候出发
    float x = event.getX();
    float y = event.getY();
    if (list != null)
      for (int i = 0; i < list.size(); i++) {//多所有的path进行遍历
        CityPath cityPath = list.get(i);
        if (cityPath.isArea(x / scale, y / scale)) {//这个地方要注意了,在查找点是否在path区域中药除以上面的缩放比例
          mPath = cityPath.getmPath();
          postInvalidate();
          Toast.makeText(getContext(), cityPath.getTitle(), Toast.LENGTH_SHORT).show();
          break;
        }
      }
  }
  return super.onTouchEvent(event);
}

出发事件实现中主要的核心是判断点是否在path区域内实现代码如下:

?
1
2
3
4
5
6
7
8
public boolean isArea(float x,float y){
   RectF r=new RectF();
   //计算控制点的边界
   mPath.computeBounds(r, true);
   //设置区域路径和剪辑描述的区域
   re.setPath(mPath, new Region((int)r.left,(int)r.top,(int)r.right,(int)r.bottom));
   return re.contains((int)x, (int)y);
}

上面的代码就可以实现不规则区域的点击了,接下来主要文件就是如何保证通过解析的svg文件可以再不同手机上的显示适配,我这里实现的方法是将每个path的最小外嵌矩形的大小都统计出来,然后进行整合获取所有path所在区域的最小值,然后和控件的大小进行比较算出缩放比代码如下:

?
1
2
3
4
5
6
7
8
9
10
//处理path的边界
//计算控制点的边界
  mCityPath.getmPath().computeBounds(mRectF, true);
   mViewAttr.colSize(mRectF);
  public void colSize(RectF mRectF) {    
  left = left == null ? mRectF.left : Math.min(mRectF.left, left);
    top = top == null ? mRectF.top : Math.min(mRectF.top, top);
    right = right == null ? mRectF.right : Math.max(mRectF.right, right);
    bottom = bottom == null ? mRectF.bottom : Math.max(mRectF.bottom, bottom);
}

适配完成之后就大功告成,下面是代码的地址,如有改进的地方欢迎提出

下载:代码地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/zjd934784273/article/details/72841295