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

上来先看看效果


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

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

package demo.zjd.com.taiwandemo.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.taiwandemo.bean.CityPath;
import demo.zjd.com.taiwandemo.bean.ViewAttr;
import demo.zjd.com.taiwandemo.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文件之后就是绘制图像代码如下:

 @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();

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

    @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区域内实现代码如下:


  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所在区域的最小值,然后和控件的大小进行比较算出缩放比代码如下:

  //处理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);
}


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





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的图形界面和功能库。要实现不规则形状的进度条,可以使用Qt的绘图功能和自定义控件。 以下是一种实现不规则形状进度条的方法: 1. 创建一个自定义的QWidget子类,作为进度条的容器。 2. 在该自定义控件中重写paintEvent函数,使用Qt的绘图功能绘制进度条的背景和前景。 3. 根据进度值计算出前景的宽度,并使用绘图函数绘制前景。 4. 使用QPainterPath类创建一个不规则形状的路径,可以使用addRect、addEllipse等函数添加矩形、椭圆等形状。 5. 使用setClipPath函数将绘图区域限制在不规则形状的路径内。 6. 在paintEvent函数中绘制进度条的背景和前景。 下面是一个简单的示例代码: ```cpp #include <QtWidgets> class IrregularProgressBar : public QWidget { public: IrregularProgressBar(QWidget *parent = nullptr) : QWidget(parent), m_progress(0) { } void setProgress(int progress) { m_progress = progress; update(); } protected: void paintEvent(QPaintEvent *event) override { Q_UNUSED(event); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); // 绘制背景 painter.fillRect(rect(), Qt::lightGray); // 绘制前景 QRectF foregroundRect(rect().x(), rect().y(), rect().width() * m_progress / 100.0, rect().height()); painter.fillRect(foregroundRect, Qt::blue); // 创建不规则形状的路径 QPainterPath path; path.addRoundedRect(rect(), 10, 10); // 设置绘图区域不规则形状的路径 painter.setClipPath(path); // 绘制进度条的背景和前景 painter.fillRect(rect(), Qt::lightGray); painter.fillRect(foregroundRect, Qt::blue); } private: int m_progress; }; int main(int argc, char *argv[]) { QApplication app(argc, argv); IrregularProgressBar progressBar; progressBar.setProgress(50); progressBar.resize(300, 30); progressBar.show(); return app.exec(); } ``` 这个示例代码创建了一个自定义的QWidget子类IrregularProgressBar,通过重写paintEvent函数实现不规则形状的进度条。在paintEvent函数中,首先绘制了背景和前景,然后创建了一个圆角矩形的路径,并使用setClipPath函数将绘图区域限制在该路径内,最后再次绘制了背景和前景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值