图标放大缩小移动,加标注点,并带点击效果的实现

第一:了解三个类
Canvas,在英语中,这个单词的意思是帆布。在Android中,则把Canvas当做画布,只要我们借助设置好的画笔(Paint类)就可以在画布上绘制我们想要的任何东西;另外它也是显示位图(Bitmap类)的核心类。随用户的喜好,Canvas还可设置一些关于画布的属性,比如,画布的颜色、尺寸等。Canvas提供了如下一些方法:
一种就是使用普通View的canvas画图,还有一种就是使用专门的SurfaceView的canvas来画图。两种的主要是区别就是可以在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。前面一种适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的;而后一种主要用在游戏,高品质动画方面的画图。
1、将会以颜色ARBG填充整个控件的Canvas背景
mCanvas.drawARGB(122, 10, 159, 163) ;
2、将会以颜色ARBG填充整个控件的Canvas背景
mCanvas.drawColor(Color.BLUE) ;
3、绘制颜色,但是要制定一个mode
mCanvas.drawColor(Color.BLUE, Mode.SCREEN) ;
4、画背景,跟2等效
mCanvas.drawPaint(mPaint) ;
5、画一个点
mCanvas.drawPoint(23, 23, mPaint) ;
6、画很多点这里的float[] 表示{x0,y0,x1,y1,x2,y2,x3,y3…..}
mCanvas.drawPoints(new float[]{10,11,10,12,10,13,10,14,10,15,10,16}, mPaint) ;
7、画线
mCanvas.drawLine(…) ;
8、画长方形 Rect 和RectF的区别?
精度不一样,Rect是使用int类型作为数值,RectF是使用float类型作为数值
Rect r = new Rect(10,10,50,50) ;
mCanvas.drawRect(r, mPaint) ;
RectF rf = new RectF(10,10,50,50) ;
mCanvas.drawRect(rf, mPaint) ;
mCanvas.drawRect(10, 10, 50, 50, mPaint) ;
9、画椭圆 初始化RectF的参数是(left,top,right,bottom)
RectF rf = new RectF(100,100 ,200 ,250) ;
mCanvas.drawOval(rf, mPaint) ;
10、画圆 (圆心x0,圆心y0,半径,paint)
mCanvas.drawCircle(100, 100, 50, mPaint) ;
11、画圆弧 RectF对象表明内切矩形的(left,top,right,bottom)
RectF rf = new RectF(100 ,100 ,200 ,200) ;
参数(rf,startAngle ,angle ,sweepAngle ,paint) sweepAngle表明是否显示圆弧三角形 angle画多少度
mCanvas.drawArc(rf, 60, 30, true, mPaint) ;
12、绘制圆角矩形 RectF是矩形的(left,top,right,bottom)
RectF rf = new RectF(100 ,100 ,200 ,200) ;
50表明x方向的半径,20表示y方向的半径
mCanvas.drawRoundRect(rf, 50, 20, mPaint) ;
13、画任意多边形
Path path = new Path() ;
path.moveTo(100, 100) ;
path.lineTo(200, 200) ;
path.lineTo(300, 200) ;
mCanvas.drawPath(path, mPaint) ;
14、通过Path对象,也可以画其他的图形
Path path = new Path() ;
path.addCircle(100, 100, 20, Path.Direction.CCW) ;
mCanvas.drawPath(path ,mPaint);
drawBitmap
drawText
drawPicture
Rect r = new Rect(100,100,200,200) ;
ByteArrayOutputStream out = new ByteArrayOutputStream();
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bg) ;
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out) ;
InputStream in = new ByteArrayInputStream(out.toByteArray()) ;
Picture picture = Picture.createFromStream(mContext.getResources().openRawResource(R.raw.bg)) ;
mCanvas.drawPicture(picture) ;
15、画bitmap对象
mCanvas.drawBitmap(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bg),100, 100, mPaint) ;
16、Matrix中包含了对Bitmap的处理操作
Matrix m = new Matrix() ;
m.postScale(2, 2) ;
m.postRotate(60) ;
mCanvas.drawBitmap(BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bg), m, mPaint) ;
Paint即画笔,在绘制文本和图形用它来设置图形颜色, 样式等绘制信息。
1.图形绘制
setARGB(int a,int r,int g,int b);
设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
setAlpha(int a);
设置绘制图形的透明度。
setColor(int color);
设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
setAntiAlias(boolean aa);
设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
setDither(boolean dither);
设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
setFilterBitmap(boolean filter);
如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示
速度,本设置项依赖于dither和xfermode的设置
setMaskFilter(MaskFilter maskfilter);
设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等
setColorFilter(ColorFilter colorfilter);
设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
setPathEffect(PathEffect effect);
设置绘制路径的效果,如点画线等
setShader(Shader shader);
设置图像效果,使用Shader可以绘制出各种渐变效果
setShadowLayer(float radius ,float dx,float dy,int color);
在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
setStyle(Paint.Style style);
设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
setStrokeCap(Paint.Cap cap);
当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式
Cap.ROUND,或方形样式Cap.SQUARE
setSrokeJoin(Paint.Join join);
设置绘制时各图形的结合方式,如平滑效果等
setStrokeWidth(float width);
当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
setXfermode(Xfermode xfermode);
设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
2.文本绘制
setFakeBoldText(boolean fakeBoldText);
模拟实现粗体文字,设置在小字体上效果会非常差
setSubpixelText(boolean subpixelText);
设置该项为true,将有助于文本在LCD屏幕上的显示效果
setTextAlign(Paint.Align align);
设置绘制文字的对齐方向
setTextScaleX(float scaleX);
设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
setTextSize(float textSize);
设置绘制文字的字号大小
setTextSkewX(float skewX);
设置斜体文字,skewX为倾斜弧度
setTypeface(Typeface typeface);
设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
setUnderlineText(boolean underlineText);
设置带有下划线的文字效果
setStrikeThruText(boolean strikeThruText);
设置带有删除线的效果
Matrix类,Matrix是一个3 x 3的矩阵,他对图片的处理分为四个基本类型:
cosX -sinX translateX
sinX cosY translateY
0 0 scale
通过这个矩阵实现下面这些变化
1、Translate————平移变换
2、Scale————缩放变换
3、Rotate————旋转变换
4、Skew————错切变换
在Android的API里对于每一种变换都提供了三种操作方式:set(用于设置Matrix中的值)、post(后乘,根据矩阵的原理,相当于左乘)、pre(先乘,相当于矩阵中的右乘)。默认时,这四种变换都是围绕(0,0)点变换的,当然可以自定义围绕的中心点,通常围绕中心点。
首先说说平移,在对图片处理的过程中,最常用的就是对图片进行平移操作,该方法为setTranslate(),平移意味着在x轴和y轴上简单地移动图像。setTranslate方法采用两个浮点数作为参数,表示在每个轴上移动的数量。第一个参数是图像将在x轴上移动的数量,而第二个参数是图像将在y轴上移动的数量。在x轴上使用正数进行平移将向右移动图像,而使用负数将向左移动图像。在y轴上使用正数进行平移将向下移动图像,而使用负数将向上移动图像。
再看缩放,Matrix类中另一个有用的方法是setScale方法。它采用两个浮点数作为参数,分别表示在每个轴上所产生的缩放量。第一个参数是x轴的缩放比例,而第二个参数是y轴的缩放比例。如:matrix.setScale(1.5f,1);
比较复杂的就是图片的旋转了,内置的方法之一是setRotate方法。它采用一个浮点数表示旋转的角度。围绕默认点(0,0),正数将顺时针旋转图像,而负数将逆时针旋转图像,其中默认点是图像的左上角,如:
Matrix matrix = new Matrix();
matrix.setRotate(15);
另外,也可以使用旋转的角度及围绕的旋转点作为参数调用setRotate方法。选择图像的中心点作为旋转点,如:
matrix.setRotate(15,bmp.getWidth()/2,bmp.getHeight()/2);
对于错切变换,由于本博主的知识有限这里不作解释。
了解这些基础知识后,我们开始实现我们的功能先上图:
第一张加一个标注物眼睛附近
这里写图片描述
第二张放大后眼睛那个标注物不见了
这里写图片描述
第三张移动后那个标注物显示出来,并且继续添加一个标注物第二个标注物手附近
这里写图片描述
缩小之后我们点击标注物看打印信息,如果点击的是标注物则打印出第几个标注物,如果点击的不是标注物则新增标志物截图如下
这里写图片描述
这里写图片描述
基本功能实现代码如下:

package test.com.surfaceviewoverlay;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TabHost;
import android.widget.Toast;

import java.util.ArrayList;


/**
 * Created by Administrator on 2016/5/20.
 */
public class OverSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{
    private static final String TAG="测试";
    private Canvas canvas = null; //定义画布
    private Thread th = null;     //定义线程
    private SurfaceHolder sfh = null;
    //不支持下面两种模式
    private static final int NONE = 0;
    private static final int CLICK=3;//单点模式
    /** 拖拉照片模式 */
    private static final int DRAG = 1;
    //放大缩小模式
    private static final int ZOOM = 2;
    //初始化为空模式
    private int mode = NONE;
    private PointF start = new PointF();
    private PointF mid = new PointF();
    /** 最后一次触摸时的位置 */
    private PointF end = new PointF();
    /** 地图中心位置中心 */
    private PointF screenCenter = new PointF();
    /** 图纸宽高 */
    private PointF mapCenter = new PointF();
    private Bitmap bm;//加载的地图
    /** 标注点 */
    // 缩放倍率
    private float rate = 1f;
    //图片缩放前后连个手指间的距离
    private float oldDist = 1f;
    private float newDist;
    private float oldRate = 1;
    private Bitmap b;//标注物
    //控件宽高
    private int h;
    private Matrix matrix;
    private int w;
    //图片长宽
    private int mapH;
    private int mapW;
    private float scaleH;//原始高缩放比例
    private float scaleW;//原始宽缩放比例
    private ArrayList<PositionPoint> positionPoints;//装标注点信息的点
    public OverSurfaceView(Context context) {
        super(context);
        sfh = getHolder();
        sfh.addCallback(this);
        th = new Thread(this);
        positionPoints=new ArrayList<>();
    }//,没有自定义属性,不需要再xml中使用所以只重载这个构造方法

    public OverSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);//在xml中使用就要定义这个构造方法
        sfh = getHolder();
        sfh.addCallback(this);
        th = new Thread(this);
    }

    @Override
    public void run() {

    }
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        bm = BitmapFactory.decodeResource(getResources(), R.drawable.mv);
        mapH=bm.getHeight();
        mapW=bm.getWidth();
        h=getHeight();
        w=getWidth();
        screenCenter.set(w/2, h/2);
        mapCenter.set( mapW/2, mapH/2);//记录地图中心位置
        calculateScale();
        draw();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
    private void draw()
    {
        canvas  = sfh.lockCanvas() ;
        Paint paint=new Paint();
        matrix = new Matrix();

        matrix.setScale(rate, rate,mapCenter.x, mapCenter.y);
        matrix.postTranslate(screenCenter.x+(end.x - start.x)- mapCenter.x
                ,screenCenter.y+(end.y - start.y) - mapCenter.y );

            Log.e("rate1",""+rate+","+bm.getHeight()+","+bm.getWidth()+","+screenCenter.x+","+mapCenter.x*rate);
        canvas.drawColor(Color.WHITE);
        canvas.drawBitmap(bm, matrix,paint);
       /*//**背景颜色*//*
        *//**标注坐标*//*
        *//** 画标注点 */
        b = BitmapFactory.decodeResource(getResources(), R.drawable.marker);
        Matrix matrix=new Matrix();
       for(int i=0;i<positionPoints.size();i++){
        matrix.setScale(1, 1,b.getWidth()/2, b.getHeight()/2);
        matrix.postTranslate(screenCenter.x+(end.x - start.x) - bm.getWidth()*rate/2-b.getWidth()/2+positionPoints.get(i).getPointX()*rate,screenCenter.y
                +(end.y - start.y)- bm.getHeight()*rate/2-b.getHeight()/2+positionPoints.get(i).getPointY()*rate);
        canvas.drawBitmap(b,matrix, new Paint());
           Log.e("测试",screenCenter.x+"x"+b.getWidth()/2+"b"+bm.getWidth()/2+"bm"+positionPoints.get(i).getPointX()*rate+"pooint");
           Log.e("测试1",screenCenter.x+(end.x - start.x) - bm.getWidth()*rate/2-b.getWidth()/2+positionPoints.get(i).getPointX()*rate+"点击的x"+start.x);
       }
        sfh.unlockCanvasAndPost(canvas);
    }
    public void calculateScale(){
        scaleW= (float)w/mapW;
        scaleH=(float)h/mapH;
        if(scaleW<scaleH){
            rate=scaleW;
        }else {
            rate=scaleH;
        }
    }
        @Override
         public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    float startX = event.getX();
                    float startY = event.getY();
                    start.set(startX,startY );//记录开始的位置
                    end.set(startX,startY);
                    mode = CLICK;//单点模式
                    Log.e(TAG, "start------------------------" + event.getX() + "----" + event.getY());
                    break;

                case MotionEvent.ACTION_POINTER_DOWN://多点触摸
                    oldDist = spacing(event);//按下时两个手指间的距离
                    Log.d(TAG, "oldDist" + "-----------------oldDist----------");
                    Log.d(TAG, "oldDist=" + oldDist);
                    if (oldDist > 10f) {
                        screenCenter.x=getWidth()/2;
                        screenCenter.y=getHeight()/2;
                        midPoint(mid, event);
                        mode = ZOOM;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    boolean flag=whichItemImage(start.x, start.y);
                    oldRate = rate;
                    float x= (float) Math.sqrt((event.getX()-start.x)*(event.getX()-start.x)+ (event.getY()-start.y)*(event.getY()-start.y));
                    if (x<10&&mode==CLICK&&flag){
                        PositionPoint   positionPoint=new PositionPoint();//这个点是相对于图片坐标的点
                        positionPoint.setPointX((start.x-(screenCenter.x- bm.getWidth()*rate/2))/rate);
                        positionPoint.setPointY((start.y-(screenCenter.y- bm.getHeight()*rate/2))/rate);
                        positionPoints.add(positionPoint);
                        end.set(start.x, start.y);
                        draw();
                    }else if(mode==CLICK) {
                        screenCenter.set(screenCenter.x + (end.x - start.x), screenCenter.y
                                + (end.y - start.y));
                    }
                    // 记录移动后地图中心位置(坐标点在屏幕的中心)
                    Log.d(TAG, "end------------------------" + event.getX()+ "----" + event.getY());
                    break;
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    break;
                case MotionEvent.ACTION_MOVE:
                     x= (float) Math.sqrt((event.getX()-start.x)*(event.getX()-start.x)+ (event.getY()-start.y)*(event.getY()-start.y));
                    if (mode==CLICK&&x>10){//设置最后一个点的位置
                        if(screenCenter.x+(event.getX()-start.x)<mapCenter.x*rate){
                        end.set(event.getX(), event.getY());
                        draw();}
                        //}
                        Log.d("移动测试", "end----"+screenCenter.x+(event.getX()-start.x)+"------.ACTION_MOVE----"+mapCenter.x*rate+"----------"+ event.getX() + "----" + event.getY());

                    }  else if (mode == ZOOM) {
                         newDist = spacing(event);//移动后两个手指间的距离
                        if (newDist > 10f&&((newDist-oldDist)>10||(newDist-oldDist)<-10)){
                            rate = oldRate * (newDist / oldDist);//设置缩放比例
                            draw();
                        }
                    }

                    break;
            }
            return true;
        }

    /**
     * 对所画点的判断,因为图片比较小,所以在坐标点上X、Y点分别加减20dp像素,也就是在正常的情况下图片的点击区域是一个边长为40dp的正方形,
     * 因为涉及到缩放,需要乘以Scale(缩放比例)所以点击区域大小也是变的
     */
    public boolean whichItemImage(float x, float y) {
        boolean flag=true;

        for (int i = 0; i < positionPoints.size(); i++) {
            float x1 = screenCenter.x + (end.x - start.x)
                    - bm.getWidth() * rate / 2 + positionPoints.get(i).getPointX() * rate
                    + 40;
            float x2 = screenCenter.x + (end.x - start.x)
                    - bm.getWidth() * rate / 2 + positionPoints.get(i).getPointX() * rate
                    - 40;
            float y1 = screenCenter.y + (end.y - start.y)
                    - bm.getHeight() * rate / 2 + positionPoints.get(i).getPointY() * rate
                    + 68;
            float y2 = screenCenter.y + (end.y - start.y)
                    - bm.getHeight() * rate / 2 + positionPoints.get(i).getPointY() * rate
                    - 68;
            Log.d(TAG, "x1========" + x1);
            Log.d(TAG, "x2========" + x2);
            Log.d(TAG, "y1========" + y1);
            Log.d(TAG, "y2========" + y2);
            if (x <= x1 && x >= x2 && y <= y1 && y >= y2) {
                Log.e("你点击了哪个按钮", ""+i);
                flag=false;
            }
        }
  return flag;
    }
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return  (float)Math.sqrt(x * x + y * y);
    }
//求重点坐标
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }
}
由于时间关系我会在下一篇博客将代码解释清楚,并详细的告诉读者在实现功能的时候注意细节。
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
实现图片标注放大缩小并存储标注数据,可以使用以下步骤: 1. 载图片:使用Vue的`<img>`标签载图片。 2. 标注图片:使用Vue的`<canvas>`标签绘制图片,通过鼠标事件监听用户的标注操作,将标注数据存储在Vue的数据中。 3. 放大缩小:通过CSS的`transform`属性实现图片的放大缩小效果,同时也需要更新绘制标注的画布大小和坐标。 4. 存储标注数据:将标注数据存储在本地存储或后端数据库中,可以使用Vue的`localStorage`或`axios`库实现。 下面是一个简单的示例代码: ``` <template> <div> <img ref="image" :src="imageUrl" @load="onImageLoad"> <div ref="canvasWrapper" class="canvas-wrapper"> <canvas ref="canvas" @mousedown="onMouseDown" @mousemove="onMouseMove" @mouseup="onMouseUp"></canvas> </div> </div> </template> <script> export default { data() { return { imageUrl: 'https://image.url', imageWidth: 0, imageHeight: 0, canvasWidth: 0, canvasHeight: 0, canvasLeft: 0, canvasTop: 0, isMouseDown: false, startX: 0, startY: 0, annotations: [] } }, methods: { onImageLoad() { this.imageWidth = this.$refs.image.width this.imageHeight = this.$refs.image.height this.canvasWidth = this.imageWidth this.canvasHeight = this.imageHeight this.$refs.canvas.width = this.canvasWidth this.$refs.canvas.height = this.canvasHeight this.canvasLeft = this.$refs.canvasWrapper.offsetLeft this.canvasTop = this.$refs.canvasWrapper.offsetTop this.drawAnnotations() }, onMouseDown(event) { this.isMouseDown = true this.startX = event.pageX - this.canvasLeft this.startY = event.pageY - this.canvasTop }, onMouseMove(event) { if (this.isMouseDown) { const x = event.pageX - this.canvasLeft const y = event.pageY - this.canvasTop const width = x - this.startX const height = y - this.startY const annotation = { x: this.startX, y: this.startY, width, height } this.annotations.push(annotation) this.drawAnnotations() } }, onMouseUp() { this.isMouseDown = false }, drawAnnotations() { const ctx = this.$refs.canvas.getContext('2d') ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) ctx.drawImage(this.$refs.image, 0, 0, this.imageWidth, this.imageHeight) for (const annotation of this.annotations) { ctx.strokeRect(annotation.x, annotation.y, annotation.width, annotation.height) } }, zoomIn() { this.canvasWidth *= 1.2 this.canvasHeight *= 1.2 this.$refs.canvas.width = this.canvasWidth this.$refs.canvas.height = this.canvasHeight this.drawAnnotations() }, zoomOut() { this.canvasWidth /= 1.2 this.canvasHeight /= 1.2 this.$refs.canvas.width = this.canvasWidth this.$refs.canvas.height = this.canvasHeight this.drawAnnotations() }, saveAnnotations() { localStorage.setItem('annotations', JSON.stringify(this.annotations)) }, loadAnnotations() { const annotations = JSON.parse(localStorage.getItem('annotations')) if (annotations) { this.annotations = annotations this.drawAnnotations() } } } } </script> <style scoped> .canvas-wrapper { position: relative; overflow: hidden; } canvas { position: absolute; top: 0; left: 0; } </style> ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值