android自定义View,区域热力地图(具备每个省份的点击接口)

来源:互联网 发布:nginx配置stream 编辑:程序博客网 时间:2024/05/17 03:44

需求:需要一个热力地图,全面的显示中国个省份的数据对比


功能:完整的中国地图(可缩放,平移,点击)

           数据颜色区域条(各省颜色按数据所在区间而定)

           各省份颜色可设置

           各省份具备点击事件接口(点击该省份,黑线描出该省边框)


项目地址:https://github.com/AndroidCloud/-china-map-for-android-


最终实现效果:

                                                            

技术路线(简要技术思路,具体实现详见GitHub的Demo):

热力图View技术:

                           1,解析出assest目录下的SVG文件(典型的XML解析),解析出该地图所有的Path(详见GitHub的                                   Demo中util包中的SVG解析类),该Demo中使用的模拟数据在util包中ColorChangeHelp类中

                                 

                    2,将解析出的Path封装到MyMap和Province对象中

           public class MyMap {           private float Max_x;//地图最大横坐标            private float Min_x;//地图最小横坐标            private float Max_y;//地图最大纵坐标            private float Min_y;//地图最小纵坐标            private List<Province> provinceslist;//地图的省份集合            //getset省略

           public class Province {           private String name;//省份名称            private List<Path> listpath;//该省份的Path集合            private int linecolor;//绘制该省份的边框颜色            private int color;//该省份的颜色            private List<Lasso> pathLasso;//Path集合对应的Lasso集合            //getset省略

           public class Lasso {           // polygon coordinates           private float[] mPolyX, mPolyY;           // number of size in polygon           private int mPolySize;           //判断一个点是否在一个不规则的Path            public boolean contains(float x, float y) {             boolean result = false;            for (int i = 0, j = mPolySize - 1; i < mPolySize; j = i++) {            if ((mPolyY[i] < y && mPolyY[j] >= y)                    || (mPolyY[j] < y && mPolyY[i] >= y)) {                if (mPolyX[i] + (y - mPolyY[i]) / (mPolyY[j] - mPolyY[i])                        * (mPolyX[j] - mPolyX[i]) < x) {                    result = !result;                }            }            }           return result;            }

                              3,画View(首先进行大小适配),第一次绘制时,按照取到的该地图的最大横坐标和最大纵坐标先将                                 path中的所有点进行重置,重置规则为最大横坐标按照View的宽度计算出比例,然后View的高度使                             用纵坐标的最大值按照该比例缩放进行设置,然后其他所有的点按照该比例缩放。

                                         

           protected void onDraw(Canvas canvas) {           //第一次绘制,重置所有点的坐标,使得map适应屏幕大小            if (isFirst){           Width=getWidth();           Height=getHeight();           //首先重置所有点的坐标,使得map适应屏幕大小            if (map!=null){            map_scale=Width/map.getMax_x();           }           scalePoints(canvas,map_scale);           isFirst=false;           }else{           //非第一次绘制,重绘缩放和平移后的View           canvas.concat(myMatrix);           canvas.translate(OnMoveX, OnMoveY);           drawMap(canvas);           }           super.onDraw(canvas);           }
           //设置View的大小
           @Override           protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {           super.onMeasure(widthMeasureSpec, heightMeasureSpec);           int width=MeasureSpec.getSize(widthMeasureSpec);           setMeasuredDimension(width, (int) map.getMax_y());           }

                                4,处理该View的事件,包括:

                         点击事件(点击坐标运算和定位,进行重绘,被点击的省份外框黑色,其他省份外框还是默 认灰色)

                         双指缩放事件(对canvas重设缩放后的Matrix)

                          滑动平移事件(对canvas进行平移操作)

                                        

           public boolean onTouchEvent(MotionEvent event) {           switch (event.getAction() & MotionEvent.ACTION_MASK) {            case MotionEvent.ACTION_DOWN:                onTouchDown(event);                break;            case MotionEvent.ACTION_POINTER_DOWN:                // 多点触摸                onPointerDown(event);                break;            case MotionEvent.ACTION_MOVE:                 onTouchMove(event);                break;            case MotionEvent.ACTION_UP:               // 必须是单指移动                if (mode==MOVE){                offX = (event.getX() - downX)/getScale();//X轴移动距离                offY = (event.getY() - downY)/getScale();//y轴移动距离                 if (!criticalflag)  {                UpMoveX=UpMoveX+offX;                UpMoveY=UpMoveY+offY;}                }                mode = NONE;                if (Math.abs(event.getX()-downX)<10&&Math.abs(event.getY()-downY)<10                      ){                    RectF rectF=getMatrixRectF();                    //判断点的是哪个省,然后相应的省变颜色                    changeProvinceColor(event,rectF);                }                break;            // 多点松开             case MotionEvent.ACTION_POINTER_UP:                mode = NONE;                break;                }                return true;                }

                                    5,点击事件接口

                                           

           public interface onProvinceClickLisener{           public void onChose(String provincename);           }  
           //判断点的是哪个省,然后相应的省变颜色           private void changeProvinceColor(MotionEvent event,RectF rectF) {           for (Province province:map.getProvinceslist()){           //province.setColor(Color.BLUE);           province.setLinecolor(Color.GRAY);            }           for (Province p:map.getProvinceslist()){           for (Lasso lasso:p.getPathLasso()){
             //当前点击的坐标进行换算到缩放和平移后的坐标,然后判断点击的Path在哪个省份             PointF pf=new PointF(event.getX() / getScale() - rectF.left / getScale(             )-OnMoveX                    ,event.getY() / getScale() - rectF.top/getScale()-OnMoveY);            if (lasso.contains(pf.x,pf.y)){                provincename=p.getName();                //p.setColor(Color.RED);                p.setLinecolor(Color.BLACK);                invalidate();                //暴露到Activity中的接口,把省的名字传过去                onProvinceClickLisener.onChose(provincename);                break;            }            }            }            }

                                   6,Activity中使用

           <com.example.vmmet.mymapview.view.MyMapView           android:layout_width="match_parent"           android:layout_height="wrap_content"           android:layout_marginLeft="5dp"           android:layout_marginRight="5dp"           android:layout_marginBottom="3dp"           android:layout_marginTop="3dp"           android:id="@+id/view"           />
           //在OnCreate中初始化
           //初始化map           initMap();           //初始化map各省份颜色           ColorChangeHelp.changeMapColors(myMap,ColorChangeHelp.nameStrings[0]);           mapview.chingeMapColors();
           mapview.setOnChoseProvince(new MyMapView.onProvinceClickLisener() {          @Override           public void onChose(String provincename) {           //地图点击省份回调接口,listview滚动到相应省份位置            for (int i = 0; i < list.size(); i++) {            if (list.get(i).contains(provincename)) {                adapter.setPosition(i);                province_listview.setSelection(i);                break;            }            }            }            });


           private void initMap() {           //拿到SVG文件,解析成对象            myMap = new SvgUtil(this).getProvinces();           //设置缩放最大和最小倍数            mapview.setMaxScale(3);           mapview.setMinScale(1);           //传数据            mapview.setMap(myMap);          


渐变色条View技术

                       1,封装MycolorArea对象

            public class MycolorArea {             private int color;//色块颜色              private String text;//对应色块的数值              //getset省略

                         2,绘制ColorView

             public class ColorView extends View{             //部分代码省略              @Override             protected void onDraw(Canvas canvas) {              if (list==null)return;              if (list.size()>0){             //根据色块集合,平均绘制               int width_average=getWidth()/list.size();              for (int i=0;i<list.size();i++){                colorPaint.setColor(list.get(i).getColor());                canvas.drawRect(i * width_average, 0, (i + 1) * width_average, getHe                ight() / 3, colorPaint);                colorPaint.setColor(Color.BLACK);                colorPaint.setTextSize(getHeight()/3);                canvas.drawText(list.get(i).getText(),width_average/2+i * width_aver                age,getHeight()/3*5/2,colorPaint);             }             }             super.onDraw(canvas);             }

                         3,Activity中使用

             <com.example.vmmet.mymapview.view.ColorView              android:layout_width="match_parent"              android:layout_height="33dp"              android:id="@+id/colorView"/>

             //设置颜色渐变条             setColorView();
             private void setColorView() {             colorView_hashmap = new HashMap<>();             for (int i = 0; i < ColorChangeHelp.nameStrings.length; i++) {              String colors[] = ColorChangeHelp.colorStrings[i].split(",");              String texts[] = ColorChangeHelp.textStrings[i].split(",");              List<MycolorArea> list = new ArrayList<>();             for (int j = 0; j < colors.length; j++) {              MycolorArea c = new MycolorArea();              c.setColor(Color.parseColor(colors[j]));              c.setText(texts[j]);              list.add(c);             }             colorView_hashmap.put(ColorChangeHelp.nameStrings[i], list);             }             colorView.setList(colorView_hashmap.get(ColorChangeHelp.nameStrings[0])             );             }


      做开发,需要脚踏实地,日积月累,愿你我共勉

阅读全文
1 0
原创粉丝点击