RecyclerViewAndroid全面艺术解析

Android全面艺术解析之地图圈地

2017-04-24  本文已影响97人  KaiS

最近项目用到地图绘制效果,可以选中相应的区域进行判读点位是否在制定区域内,先看效果。

闭合区域效果.png 非闭合区域效果.png 自定义区域效果.png 项目效果图.png

下面我们先看一下最主要的类,自定义类PathView:

package com.example.lenovo.enclosure;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 路径绘制
 * Created by kaidaye on 2017/4/19.
 */
public class PathView extends View {
    /**
     * 线条颜色值
     */
    private int paintColor = 0xff4598e5;
    /**
     * 第二条线颜色值
     */
    private int paintTransparentColor = 0x154598e5;

    /**
     * 线条宽度
     */
    private float strokeWidth = 15.0f;
    /**
     * 第二条线宽度
     */
    private float xustrokeWidth = 200.0f;
    /**
     * 透明度
     */
    private int alpha = 80;

    /**
     * 闭合X坐标距
     */
    private int closeX = 300;
    /**
     * 闭合Y坐标距
     */
    private int closeY = 300;
    /**
     * 是否是闭合只需要闭合状态
     */
    private Boolean isClose = false;

    /**
     * 当前闭合状态,true表示闭合,false 表示未闭合
     */
    private Boolean isStatus = false;

    private Paint mPaint = new Paint();
    private Paint transparentPaint = new Paint();
    private Path pathCircle = new Path();
    private Path mPath = new Path();
    private Path transparentPath = new Path();
    private Region region = new Region();

    private float pathX;
    private float pathY;

    private float startX;
    private float startY;

    private Boolean isDrwable = false;
    private Boolean isActionUp = false;
    private OnFinishListener listener;

    public PathView(Context context) {
        super(context);
        init();
    }

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


    private void init() {
        //设置画笔颜色
        mPaint.setColor(paintColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(strokeWidth);

        transparentPaint.setColor(paintTransparentColor);
        transparentPaint.setStyle(Paint.Style.STROKE);
        transparentPaint.setStrokeCap(Paint.Cap.ROUND);
        transparentPaint.setStrokeWidth(xustrokeWidth);
        transparentPaint.setAlpha(alpha);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(mPath, mPaint);
        if (isDrwable) {
            canvas.drawPath(transparentPath, transparentPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isActionUp) {
            isActionUp = false;
            mPaint.setStyle(Paint.Style.STROKE);
            return true;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchDown(event);
                break;
            case MotionEvent.ACTION_MOVE:
                touchMove(event);
                break;
            case MotionEvent.ACTION_UP:
                if (isClose) {//只需要闭合状态
                    mPath.close();//路径闭合方法,如果起点和结束点没有关闭调用此方法会直接将起点和结束的连线
                    transparentPath.close();
                    transparentPaint.setStyle(Paint.Style.FILL);
                    transparentPaint.setStrokeWidth(strokeWidth);
                    isStatus = true;
                    RectF r = new RectF();
                    mPath.computeBounds(r, true);
                    //设置区域路径和剪辑描述的区域
                    region.setPath(mPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
                } else {//闭合与非闭合两种状态
                    if (Math.abs(startX - event.getX()) <= closeX && Math.abs(startY - event.getY()) <= closeY) {//距离较近可以闭合
                        mPath.close();//路径闭合方法,如果起点和结束点没有关闭调用此方法会直接将起点和结束的连线
                        transparentPath.close();
                        transparentPaint.setStyle(Paint.Style.FILL);
                        transparentPaint.setStrokeWidth(strokeWidth);
                        isStatus = true;
                        RectF r = new RectF();
                        mPath.computeBounds(r, true);
                        //设置区域路径和剪辑描述的区域
                        region.setPath(mPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
                    } else {
                        RectF rCircle = new RectF();
                        pathCircle.computeBounds(rCircle, true);
                        region.setPath(pathCircle, new Region((int) rCircle.left, (int) rCircle.top, (int) rCircle.right, (int) rCircle.bottom));
                        isStatus = false;
                    }
                }
                isDrwable = true;

                if (listener != null)
                    listener.onFinish(region);
                break;
        }
        postInvalidate();
        return true;
    }

    //手指在屏幕上滑动时调用
    private void touchMove(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        Point point = new Point();
        point.x = (int) event.getX();
        point.y = (int) event.getY();
        pathCircle.addCircle(event.getX(), event.getY(), (xustrokeWidth - strokeWidth) / 2, Path.Direction.CCW);
        final float dx = Math.abs(x - pathX);
        final float dy = Math.abs(y - pathY);
        //两点之间的距离大于等于3时,生成贝塞尔绘制曲线
        if (dx >= 3 || dy >= 3) {
            //设置贝塞尔曲线的操作点为起点和终点的一半
            float cX = (x + pathX) / 2;
            float cY = (y + pathY) / 2;
            //二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点
            mPath.quadTo(pathX, pathY, cX, cY);
            transparentPath.quadTo(pathX, pathY, cX, cY);
            //第二次执行时,第一次结束调用的坐标值将作为第二次调用的初始坐标值
            pathX = x;
            pathY = y;
        }
    }

    private void touchDown(MotionEvent event) {
        reset();
        pathX = event.getX();
        pathY = event.getY();
        startX = event.getX();
        startY = event.getY();
        mPath.moveTo(pathX, pathY);
        transparentPath.moveTo(pathX, pathY);
        isActionUp = true;
    }

    /**
     * 清除所有内容
     */
    public void reset() {
        mPath.reset();
        transparentPath.reset();
        pathCircle.reset();
        init();
        isDrwable = false;
    }


    public Boolean getClose() {
        return isClose;
    }

    /**
     * 是否只是闭合状态
     *
     * @param close
     */
    public void setClose(Boolean close) {
        isClose = close;
    }

    public int getPaintColor() {
        return paintColor;
    }

    /**
     * 设置画笔颜色
     *
     * @param paintColor
     */
    public void setPaintColor(int paintColor) {
        this.paintColor = paintColor;
    }

    public int getPaintTransparentColor() {
        return paintTransparentColor;
    }

    /**
     * 设置粗画笔颜色
     *
     * @param paintTransparentColor
     */
    public void setPaintTransparentColor(int paintTransparentColor) {
        this.paintTransparentColor = paintTransparentColor;
    }

    public int getCloseX() {
        return closeX;
    }

    /**
     * 设置闭合X距离
     *
     * @param closeX
     */
    public void setCloseX(int closeX) {
        this.closeX = closeX;
    }

    public int getCloseY() {
        return closeY;
    }

    /**
     * 设置闭合Y距离
     *
     * @param closeY
     */
    public void setCloseY(int closeY) {
        this.closeY = closeY;
    }

    /**
     * 当前闭合状态
     *
     * @return
     */
    public Boolean getStatus() {
        return isStatus;
    }

    public void setStatus(Boolean status) {
        isStatus = status;
    }

    public float getStrokeWidth() {
        return strokeWidth;
    }

    /**
     * 设置线条宽度
     *
     * @param strokeWidth
     */
    public void setStrokeWidth(float strokeWidth) {
        this.strokeWidth = strokeWidth;
        mPaint.setStrokeWidth(strokeWidth);
    }

    public float getXustrokeWidth() {
        return xustrokeWidth;
    }

    /**
     * 设置第二条线宽度
     *
     * @param xustrokeWidth
     */
    public void setXustrokeWidth(float xustrokeWidth) {
        this.xustrokeWidth = xustrokeWidth;
        transparentPaint.setStrokeWidth(xustrokeWidth);
    }

    /**
     * 设置第二条线透明度
     *
     * @param alpha
     */
    public void setAlpha(int alpha) {
        this.alpha = alpha;
        transparentPaint.setAlpha(alpha);
    }

    public void setOnFinishListener(OnFinishListener listener) {
        this.listener = listener;
    }

    public interface OnFinishListener {
        public void onFinish(Region p);
    }

}

其中最主要的几个方法,第一:init()方法,初始化画笔,设置透明度等等。第二:onTouchEvent()方法会获取用户的点击事件,用户点击调用touchDown()方法,调用完成之后用户开始进行“画”,画就会调用移动方法touchMove(),当移动完成就抬起手指运行ACTION_UP之后的代码。

我们直接开始说touchMove()方法:
根据移动的X,Y坐标与上一个点位的X,Y坐标进行相减取绝对值,如果绝对值大于3就进行path路径的添加,不过这里需要注意的是,添加的路径是贝塞尔曲线,没有用lineto,而是用的quadTo,主要是为了解决路径不平滑问题。

下面就是ACTION_UP,手指抬起运行的代码:
第一先判断,当前是否只需要闭合一种状态,如果只需要这种状态,进行路径闭合状态的关闭

path.close();//路径闭合方法,如果起点和结束点没有关闭调用此方法会直接将起点和结束的连线

然后进行路径的区域获取

 RectF r = new RectF();//添加一个矩形
 mPath.computeBounds(r, true);//设置路径的区域为矩形的区域

最后将路径的区域和矩形的区域取交集,也就是闭合区域的路径

 region.setPath(mPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));

同理,非闭合区域状态一样判断。

MainActivity代码:

package com.example.lenovo.enclosure;

import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Region;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
import com.baidu.mapapi.map.GroundOverlayOptions;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.OverlayOptions;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.model.LatLngBounds;

public class MainActivity extends AppCompatActivity implements PathView.OnFinishListener{

    private PathView pview;
    private MapView bmapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        bmapView = (MapView) findViewById(R.id.bmapView);
        pview = (PathView) findViewById(R.id.pview);
        pview.setOnFinishListener(this);
    }

    @Override
    public void onFinish(Region p) {
//        p.contains(x,y)//闭合区域  判断用是否在区域里面
        pview.setVisibility(View.GONE);
        Point point = new Point(0, 0);
        Point point1 = new Point(pview.getWidth(), pview.getHeight());
        BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(getViewBitmap(pview));
        LatLng southwest = bmapView.getMap().getProjection().fromScreenLocation(point);
        LatLng northeast = bmapView.getMap().getProjection().fromScreenLocation(point1);
        LatLngBounds bounds = new LatLngBounds.Builder().include(northeast).include(southwest).build();
        OverlayOptions path = new GroundOverlayOptions().positionFromBounds(bounds).image(bitmapDescriptor).zIndex(9);
        bmapView.getMap().addOverlay(path);
    }

    /**
     * 将View转换为图片
     * @param v
     * @return
     */
    private Bitmap getViewBitmap(View v) {
        v.clearFocus();
        v.setPressed(false);
        boolean willNotCache = v.willNotCacheDrawing();
        v.setWillNotCacheDrawing(false);

        // Reset the drawing cache background color to fully transparent
        // for the duration of this operation
        int color = v.getDrawingCacheBackgroundColor();
        v.setDrawingCacheBackgroundColor(0);
        if (color != 0) {
            v.destroyDrawingCache();
        }
        v.buildDrawingCache();
        Bitmap cacheBitmap = v.getDrawingCache();
        if (cacheBitmap == null) {
            Log.e("Folder", "failed getViewBitmap(" + v + ")", new RuntimeException());
            return null;
        }
        Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);

        // Restore the view
        v.destroyDrawingCache();
        v.setWillNotCacheDrawing(willNotCache);
        v.setDrawingCacheBackgroundColor(color);

        return bitmap;
    }
}

这里需要注意的是onFinish方法,这是当绘制状态结束之后调用(即手指抬起之后),然后隐藏布局文件的自定义控件,然后再将这个自定义控件绘制完成的view转换为图片,并且添加到mapview上面。

布局文件activity_main:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="1">

    <com.baidu.mapapi.map.MapView
        android:id="@+id/bmapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.example.lenovo.enclosure.PathView
        android:id="@+id/pview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

如有纰漏,尽请批正。谢!

如果你喜欢文摘,恰好你也看了我写的文摘,又恰巧你喜欢专研技术,那么为什么不加入我们那。(QQ群:417487178)欢迎交流。

github:https://github.com/18782930696/Enclosure

上一篇 下一篇

猜你喜欢

热点阅读