1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Android實現(xiàn)四色填充地圖功能

        共 17446字,需瀏覽 35分鐘

         ·

        2022-07-07 22:14

        先上效果圖:



        明顯是一個自定義view,先解析svg資源(該資源不嚴謹,請勿在正規(guī)),獲取每個省的path,再用四色算法設置每個省的顏色,先列舉主要方法解析svg文件

         InputStream inputStream = context.getResources().openRawResource(R.raw.china);            proviceItems = new ArrayList<>();            try {                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();                DocumentBuilder builder = null;                builder = factory.newDocumentBuilder();                Document document = builder.parse(inputStream);                Element rootElement = document.getDocumentElement();                NodeList items = rootElement.getElementsByTagName("path");


        items就是每個省份的邊框了,遍歷全部省份確定地圖的最左最右最上最下,從而確定地圖的真正寬高,然后再對比自定義View的寬度,確定畫圖的縮放比例,再定義自定義View的高度

            @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int width = MeasureSpec.getSize(widthMeasureSpec);        if (totalRect != null && width != 0) {            //獲取到地圖的矩形的寬度            double mapWidth = totalRect.width();            //獲取到比例值            scale = (float) (width / mapWidth);            //用寬度重新定義高度            heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (totalRect.height() * scale), MeasureSpec.EXACTLY);        }        super.onMeasure(widthMeasureSpec,heightMeasureSpec);    }

        重寫onDraw方法,把每個省依次華進去,如果有點擊事件,被點擊有變化的話,多數(shù)情況下都是要最后一個話

           @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (proviceItems != null) {            int tatalNum = proviceItems.size();            canvas.save();            canvas.scale(scale, scale);            ProviceItem selsetProviceItem = null;            // 先畫沒被選中的            for (int i = 0; i < tatalNum; i++) {                if (!proviceItems.get(i).isSelect()) {                    proviceItems.get(i).drawItem(canvas, paint);                } else {                    selsetProviceItem = proviceItems.get(i);                }            }            //被選中的最后畫,因為被選中的有陰影            if (selsetProviceItem != null) {                selsetProviceItem.drawItem(canvas, paint);            }        }    }

        把每個省都畫到地圖上的方法

         paint.setStrokeWidth(1);        paint.setColor(drawColor);        paint.setStyle(Paint.Style.FILL_AND_STROKE);        canvas.drawPath(path, paint);        if (isSelect) {            //被選擇設置一下陰影            paint.setShadowLayer(20, 0, 0, Color.WHITE);        } else {            //沒選中去掉陰影            paint.clearShadowLayer();        }        canvas.drawPath(path, paint);

        接下來就是關于顏色的選擇問題了,寫一個獲得顏色的工具類,主要參數(shù)和構造方法

            //存放顏色種類,并非真正的顏色    private int[] colorTypes;    //板塊接壤矩陣,1為接壤    private int[][] isBorder;    //準備填充的顏色列表    private int[] colors;    //顏色多少種類    private int TYPE_SIZE ;    //總共有幾個板塊    private int plateCount;
        public ColorFillUtil(int[][] isBorder, int[] colors) throws Exception{ plateCount = isBorder.length; if (plateCount != isBorder[0].length) {//板塊相鄰關系必須是方陣,不能是矩陣 throw new Exception("colors's length must be equal to isBorder's length!"); } this.colors = colors; TYPE_SIZE = colors.length; this.isBorder = isBorder; }


        思路就是從第一個省份開始慢慢嘗試填充顏色,嘗試方法就是從可選的顏色種類中,依次填充進去,然后再判斷是否和已經(jīng)填充的身份,是否有接壤并且同個顏色的,如果有就換一個顏色,如果最后每個顏色都嘗試了還是不行就說明上一個板塊填充有誤,要回退到上個板塊,如果上板塊還是不行再回退,最后直到每個板塊都設置好顏色,顏色種類如果小于4可能會填充失敗。詳細見后面代碼,以下是自定義省份的been

        public class ProviceItem {    private int index;    private Path path;    //省份顏色    private int drawColor;    //是否被點擊    private boolean isSelect;
        public ProviceItem(Path path) { this.path = path; }
        public void setDrawColor(int drawColor) { this.drawColor = drawColor; }

        public void setIndex(int index) { this.index = index; }
        public boolean isSelect() { return isSelect; }
        public void setSelect(boolean select) { isSelect = select; }
        public void drawItem(Canvas canvas, Paint paint) { paint.setStrokeWidth(1); paint.setColor(drawColor); paint.setStyle(Paint.Style.FILL_AND_STROKE); canvas.drawPath(path, paint); if (isSelect) { //被選擇設置一下陰影 paint.setShadowLayer(20, 0, 0, Color.WHITE); } else { //沒選中去掉陰影 paint.clearShadowLayer(); } canvas.drawPath(path, paint); }
        public boolean isTouch(float x, float y) { //創(chuàng)建一個矩形 RectF rectF = new RectF(); //獲取到當前省份的矩形邊界 path.computeBounds(rectF, true); //創(chuàng)建一個區(qū)域對象 Region region = new Region(); //將path對象放入到Region區(qū)域對象中 region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom)); //返回是否這個區(qū)域包含傳進來的坐標 boolean resule = region.contains((int) x, (int) y); //無法通過代碼確定兩個省份是否接壤所以只能獲取下標,人工構造省份相鄰矩陣,如果 // if (result) { // Log.d("ProviceItemIndex-----", index + ""); // } return result; }
        }


        自定義view的代碼

        public class MapView extends View {    private Paint paint;    private Context context;    //整個地圖所占用的矩形,在重新設配之前    private RectF totalRect;    private List<ProviceItem> proviceItems;    //繪制地圖的顏色    private int[] colorArray = new int[]{0xFF1383f2, 0xFFFFDC00, 0xFFFF3D33, 0xFF4ADE8C};    //適配比例    private float scale = 0;    int[] colors;    //中國省份接壤關系矩陣,劃分34個省份,自治區(qū),市和特別行政區(qū)等,但是多一個顏色表示國外的顏色項目中最后沒有用到    int[][] isBorder = {            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},            {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},            {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},            {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1},            {0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},

        };
        public MapView(Context context) { super(context); }
        public MapView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); }

        public MapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
        private void init(Context context) { this.context = context; paint = new Paint(); paint.setAntiAlias(true); //開線程解析數(shù)據(jù) loadThread.start(); }
        @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); if (totalRect != null && width != 0) { //獲取到地圖的矩形的寬度 double mapWidth = totalRect.width(); //獲取到比例值 scale = (float) (width / mapWidth); //用寬度重新定義高度 heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (totalRect.height() * scale), MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec,heightMeasureSpec);
        }
        @Override protected void onDraw(Canvas canvas) { //如果省份數(shù)據(jù)還沒加載出來,實際縮放比例沒定義出來啥都不用干 if (proviceItems != null||scale==0) { super.onDraw(canvas); int tatalNum = proviceItems.size(); canvas.save(); canvas.scale(scale, scale); ProviceItem selsetProviceItem = null; // 先畫沒被選中的 for (int i = 0; i < tatalNum; i++) { if (!proviceItems.get(i).isSelect()) { proviceItems.get(i).drawItem(canvas, paint); } else { selsetProviceItem = proviceItems.get(i); } } //被選中的最后畫,因為被選中的有陰影 if (selsetProviceItem != null) { selsetProviceItem.drawItem(canvas, paint); } } }
        private Thread loadThread = new Thread(new Runnable() { @Override public void run() { InputStream inputStream = context.getResources().openRawResource(R.raw.china); proviceItems = new ArrayList<>(); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; builder = factory.newDocumentBuilder(); Document document = builder.parse(inputStream); Element rootElement = document.getDocumentElement(); NodeList items = rootElement.getElementsByTagName("path"); //定義一個不可能存在屏幕上的很右邊的點Integer.MAX_VALUE作為最左邊,同理定義一個不能存在屏幕上的很左邊的點-1作為為最左邊 //因為循環(huán)每個省份的最左最右最上最下,左邊下標只會越來越小 float left = Integer.MAX_VALUE; float right = -1; float top = Integer.MAX_VALUE; float bottom = -1; for (int i = 0; i < items.getLength(); i++) { Element element = (Element) items.item(i); String pathData = element.getAttribute("android:pathData"); Path path = PathParser.createPathFromPathData(pathData); ProviceItem proviceItem = new ProviceItem(path); //設置省份下標 //proviceItem.setIndex(i); proviceItems.add(proviceItem); RectF rectF = new RectF(); path.computeBounds(rectF, true); left = Math.min(left, rectF.left); right = Math.max(right, rectF.right); top = Math.min(top, rectF.top); bottom = Math.max(bottom, rectF.bottom); } //創(chuàng)建整個地圖 totalRect = new RectF(left, top, right, bottom); try { if (colors == null) { colors = new ColorFillUtil(isBorder, colorArray).getColors(); int totalNumber = proviceItems.size(); for (int i = 0; i < totalNumber; i++) { proviceItems.get(i).setDrawColor(colors[i]); } handler.sendEmptyMessage(0); } } catch (Exception e) { e.printStackTrace(); }
        } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } });

        private Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { //返回主線程調用以下方法 //重新測量,調用onMeasure requestLayout(); //重新繪圖,系統(tǒng)調用onDraw invalidate(); } };
        @Override public boolean onTouchEvent(MotionEvent event) { //將當前手指觸摸到位置傳過去 判斷當前點擊的區(qū)域 handlerTouch(event.getX(), event.getY()); return super.onTouchEvent(event); }
        /** * 判斷區(qū)域 * * @param x * @param y */ private void handlerTouch(float x, float y) { //判空 if (proviceItems == null || proviceItems.size() == 0) { return; } for (ProviceItem proviceItem : proviceItems) { //入股點擊的是這個省份的范圍之內 就把當前省份的封裝對象繪制的方法 傳一個true proviceItem.setSelect(proviceItem.isTouch(x / scale, y / scale)); } postInvalidate(); }


        顏色選擇工具類

        /** * 板塊顏色填充工具 */public class ColorFillUtil {    //存放顏色種類,并非真正的顏色    private int[] colorTypes;    //板塊接壤矩陣,1為接壤    private int[][] isBorder;    //準備填充的顏色列表    private int[] colors;    //顏色多少種類    private int TYPE_SIZE ;    //總共有幾個板塊    private int plateCount;
        public ColorFillUtil(int[][] isBorder, int[] colors) throws Exception{ plateCount = isBorder.length; if (plateCount != isBorder[0].length) {//板塊相鄰關系必須是方陣,不能是矩陣 throw new Exception("colors's length must be equal to isBorder's length!"); } this.colors = colors; TYPE_SIZE = colors.length; this.isBorder = isBorder; }

        /** * 獲取最后的結果 * @return */ public int[] getColors() {
        colorTypes = new int[plateCount]; int index = 0; int colorType = 0; while (index < plateCount) {
        if (setColor(index, colorType)) { //設置顏色種類暫時成功,接著下一個,直到全部顏色設置完成,設置顏色種類從0開始嘗試 index++; colorType = 0; } else { //找不到合適的顏色要回退,上一個板塊修改顏色 index--; colorType = colorTypes[index] + 1; if (index == 0) //無法求解,可能是是顏色種類太少 return null; }
        } return getRealColors(); }
        /** * 返回真正的顏色列表 * @return */ private int[] getRealColors() { int[] result = new int[plateCount]; for (int i = 0; i < plateCount; i++) { result[i] = colors[colorTypes[i]]; } return result; }
        /** * 嘗試填充顏色 填充成功返回true * @param index 準備填充的板塊下標 * @param colorType 準備填充的顏色種類 * @return */ private boolean setColor(int index, int colorType) { if (colorType >= TYPE_SIZE) return false; while (colorType < TYPE_SIZE) { //是否可以設置顏色種類 boolean canSet = true; //循環(huán)判斷準備填充的顏色與之前的顏色是否沖突 for (int i = 0; i < index; i++) { //isBorder[i][index] == 1 表示之前已經(jīng)填充的第i個板塊和準備填充的板塊是接壤的 //colorType == colorTypes[i] 同時準備填充的顏色種類又是一樣的,則準備填充的顏色要改變,再重新嘗試填充 if (isBorder[i][index] == 1 && colorType == colorTypes[i]) {
        ++colorType; canSet = false; break; } } if (canSet) { colorTypes[index] = colorType; return true; } } //找不到合適的顏色,要回退 return false; }}


        部分資源文件
        https://pan.baidu.com/s/1Tgq84epnaFhmiEBotGeBgw

        瀏覽 54
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            中文字幕伦理片 | 嫩草香蕉在线91一二三区 | 办公室激情大尺度 | 青青草伊人在线 | 黄色视频直接看 | 好大好爽av | 亚洲一卡二卡 | 强h辣文肉各种姿势h | 男人天堂大鸡巴 | 国产麻豆天美果冻无码视频 |