首页 文章 精选 留言 我的

精选列表

搜索[android],共10000篇文章
优秀的个人博客,低调大师

Android GIS开发系列-- 入门季(7) 利用GeometryEngine坐标转换、计算距离与面积等

GeometryEngine是Arcgis的重要工具类,利用此工具类,可以计算地图上的距离、面积,将点、线、面转化为Json数据,将Json转化为点线面,坐标转换作用非常强大。 一、坐标转化 将用到方法 GeometryEngine.project(Geometry geometry, SpatialReference inputSR, SpatialReference outputSR),第二个为Geometry的坐标,第三个参数为要转换的坐标。如果将84坐标转换为墨卡托坐标代码如下: Point point1 = new Point(113,23); Point point2 =(Point)GeometryEngine.project(point1,SpatialReference.create(SpatialReference.WKID_WGS84), SpatialReference.create(SpatialReference.WKID_WGS84_WEB_MERCATOR)); 二、GeometryEngine计算两点的距离 用到 GeometryEngine.geodesicDistance(Geometry geometry1, Geometry geometry2, SpatialReference spatialReference, LinearUnit distanceUnit)方法。注意最好不要用GeometryEngine.distance方法,此方法是计算2D长度的,计算不准确。代码如下: Point point1 = new Point(113,23); Point point2 = new Point(113,24); double distance = GeometryEngine.geodesicDistance(point3,point4, SpatialReference.create(SpatialReference.WKID_WGS84), new LinearUnit(LinearUnit.Code.KILOMETER)); Log.i("TAG","distance ==="+distance );此外, 通过GeometryEngine.geodesicArea, GeometryEngine.geodesicLength可分别计算处面积和周长,大家去探索去。我有不想再列举了。 没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚,让创造力自由成长吧! 多花时间,关心他(她)人,正如别人所关心你的。理想的腾飞与实现,没有别人的支持与帮助,是万万不能的。 本文转自wenglabs博客园博客,原文链接:http://www.cnblogs.com/arxive/p/7751954.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android项目:模仿ConvertView原理(ListView的getView方法)对View对象进行回收和复用

在项目优化过程中,通过MAT监控发现存在一处内存泄露,反复进入某个页面,内存占用越来越大。后分析找到了泄露原因,原来是在自定义列表中,将行布局的layout文件inflate成view对象的时候,每加载一次列表就要new出一组新的view对象。因为没有对这些布局一致的view进行复用,又没法及时释放,导致了列表的行布局对象越积越多,造成内存泄露。 解决这个oom问题,首先想到了listview加载中对convertview的回收和复用的方法。于是模仿convertview的原理写了个对view对象进行回收和复用的类,此处类名为ViewRecycler,使用后有效的解决了view对象的复用,远离这个棘手的oom问题。由于项目中需要复用的view对象布局都是一样的,此方法只考虑了复用同一布局的情况。同时,项目中的列表已有获取显示行与隐藏行的相应接口,此方法仅主要从回收与复用的逻辑层面加以实现,并未涉及任何底层代码部分。 一、以下是具体的实现过程: 1.首先ViewRecycler类需要两个容器分别用来保存活动的View对象和回收可复用的View对象。一个map用来保存回收的View(无序添加),一个数组用来记录当前显示的行号以及对应的View(有序添加)。 如下: 1 2 private SparseArray<View> recycleViews; // 废弃的view private View[] activeViews = new View[ 0 ]; //正在使用的view 注:key为int类型的HashMap用SparseArray代替,会有更好的性能. 2.每次列表刷新或变化,就更新一次activeViews的大小。即activeViews的数组长度与当前列表的总行数一致。在刷新列表或者加载更多时,调用getCount()方法更新activeViews数组长度。 如下: 1 2 3 4 5 6 // 加载完毕 private void loadComplete() { //..... mViewRecycler.getCount(mDataList.size()); } ViewRecycler类里的getCount方法: 1 2 3 4 5 6 7 8 9 10 11 12 /** 获取活动view的总数 */ public void getCount( int count) { final int length = this .activeViews.length; if (count > length) { final View[] activeViews = this .activeViews; this .activeViews = Arrays.copyOf(activeViews, count); // Log.e("getCount", "activeViews[" + (count - 1) + "]=" + this.activeViews[count-1]); } } 3.列表每新增显示一行,就先获取是否有可复用的View对象。先判断recycleViews是否已存有该行号对应的View,没有则获取最新回收的View。再结合setTag与getTag便可实现对回收View对象的复用了。如果recycleViews没有可复用的View,则inflate生成新的View。 如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public void convertFromView( final ListJson listJson, final int n) { ViewHolder holder = null ; // 判断是否有可重复利用的view View resycleView = mViewRecycler.getRecycleView(n); if (resycleView == null ) { resycleView = LayoutInflater.from(getActivity()).inflate(R.layout.list_item, null ); holder = new ViewHolder(); //..... holder.iv_main = (ImageView) resycleView.findViewById(R.id.item_iv_main); resycleView.setTag(holder); } else { holder = (ViewHolder) resycleView.getTag(); } //保存新增活动的View对象 mViewRecycler.addActiveView(n, resycleView); // 加载行布局数据 holder.tv_title.setText(lison.getName()); holder.tv_price.setText( "待定" ); //..... // 下载图片 ImageLoadingListener listener = new ImageLoadingListener() { @Override public void onLoadingStarted(String arg0, View arg1) { } @Override public void onLoadingComplete(String arg0, View arg1, Bitmap bitmap) { //获取view对象 View bitmapView = mViewRecycler.getActiveView(n); if (bitmapView != null ) { ViewHolder holder = (ViewHolder) bitmapView.getTag(); if (holder != null ) { // 加载图片 holder.iv_main.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.bg_item)); } } } @Override public void onLoadingFailed(String arg0, View arg1, FailReason arg2) { } @Override public void onLoadingCancelled(String arg0, View arg1) { } }; imageLoader.loadImage(listJson.getStatusPic(), listener); } ViewRecycler类里的对应方法: (1)将行号与对应的View填充到activeViews数组里保存。添加前对行号与activeViews的长度进行校正,避免越界。 1 2 3 4 5 6 7 8 9 10 11 12 /** 添加记录当前活动的view */ public void addActiveView( int position, View view) { final int length = this .activeViews.length; if (position > length - 1 ) { getCount(position + 1 ); } this .activeViews[position] = view; // Log.e("addActiveView", "activeViews.size() = " + Arrays.toString(activeViews)); } (2)根据行号获取对应的View对象。 1 2 3 4 5 6 7 8 9 10 /** 获取某个活动view */ public View getActiveView( int position) { final int length = this .activeViews.length; if (position > length - 1 ) { getCount(position + 1 ); } return activeViews[position]; } (3)获取已回收的View对象。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /** 获取回收的view */ View getRecycleView( int position) { return retrieveFromRecycle(recycleViews, position); } /** 检索回收的view */ static View retrieveFromRecycle(SparseArray<View> recycleViews, int position) { int size = recycleViews.size(); if (size > 0 ) { // See if we still have a view for this position. for ( int i = 0 ; i < size; i++) { int fromPosition = recycleViews.keyAt(i); View view = recycleViews.get(fromPosition); if (fromPosition == position) { recycleViews.remove(fromPosition); return view; } } int index = size - 1 ; View r = recycleViews.valueAt(index); recycleViews.remove(recycleViews.keyAt(index)); return r; } else { return null ; } } 4.列表每隐藏一行,将消失的行布局对应的View对象回收,添加到recycleViews容器里,同时移除activeViews里的这个View。然后再进行比较并清除recycleViews容器里的回收对象,保证回收对象总数不多于活动view容器的总长度。 如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 隐藏 @Override public void onInvalidateItem( int id) { super .onInvalidateItem(id); // ..... // 回收view对象 View recycleView = mViewRecycler.getActiveView(id); if (recycleView != null ) { mViewRecycler.addRecycleView(id, recycleView); } } ViewRecycler类里的对应方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 /** 添加废弃的view,无序添加 */ void addRecycleView( int position, View scrap) { recycleViews.put(position, scrap); final int length = this .activeViews.length; if (position < length) { this .activeViews[position] = null ; } pruneRecycleViews(); // Log.e("Recycle", "Recycle.size() = " +recycleViews.size()); } 1 2 3 4 5 6 7 8 9 10 11 12 /** 确保废弃的view总数不多于活动的view容器的长度(此方法可再改进为不多于当前活动的View对象数量) */ private void pruneRecycleViews() { final int maxViews = activeViews.length; int size = recycleViews.size(); final int extras = size - maxViews; size--; for ( int j = 0 ; j < extras; j++) { recycleViews.remove(recycleViews.keyAt(size--)); } } 5.退出时,清除所有View对象及其引用。 以下方法则是根据项目需求,将所有的活动view移到recycleViews里,再整理recycleViews。 如下: 1 2 3 4 5 6 7 @Override public void onDestroy() { super .onDestroy(); //销毁所有view对象 mViewRecycler.recycleAllActiveViews(); } ViewRecycler类里的对应方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /** 将所有剩余的活动view移到废弃view里 */ void recycleAllActiveViews() { final View[] activeViews = this .activeViews; SparseArray<View> recycleViews = this .recycleViews; final int count = activeViews.length; for ( int i = count - 1 ; i >= 0 ; i--) { final View victim = activeViews[i]; if (victim != null ) { activeViews[i] = null ; recycleViews.put(i, victim); } } pruneRecycleViews(); } 二、调试结果 滑动时,请求加载行数据。每次添加一个行布局对象,当达到10行后开始执行View对象回收。每次将栈底的View回收并复用到栈顶的行布局里。如下图: 三、最后附上完整的ViewRecycler类 如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 public class ViewRecycler { private SparseArray<View> recycleViews; // 废弃的view private View[] activeViews = new View[ 0 ]; //正在使用的view public ViewRecycler() { recycleViews = new SparseArray<View>(); } /** 获取活动view的总数 */ public void getCount( int count) { final int length = this .activeViews.length; if (count > length) { final View[] activeViews = this .activeViews; this .activeViews = Arrays.copyOf(activeViews, count); // Log.e("getCount", "activeViews[" + (count - 1) + "]=" + this.activeViews[count-1]); } } /** 添加记录当前活动的view */ public void addActiveView( int position, View view) { final int length = this .activeViews.length; if (position > length - 1 ) { getCount(position + 1 ); } this .activeViews[position] = view; // Log.e("addActiveView", "activeViews.size() = " + Arrays.toString(activeViews)); } /** 获取某个活动view */ public View getActiveView( int position) { final int length = this .activeViews.length; if (position > length - 1 ) { getCount(position + 1 ); } return activeViews[position]; } /** 获取废弃的view */ View getRecycleView( int position) { return retrieveFromRecycle(recycleViews, position); } /** 检索废弃的view */ static View retrieveFromRecycle(SparseArray<View> recycleViews, int position) { int size = recycleViews.size(); if (size > 0 ) { // See if we still have a view for this position. for ( int i = 0 ; i < size; i++) { int fromPosition = recycleViews.keyAt(i); View view = recycleViews.get(fromPosition); if (fromPosition == position) { recycleViews.remove(fromPosition); return view; } } int index = size - 1 ; View r = recycleViews.valueAt(index); recycleViews.remove(recycleViews.keyAt(index)); return r; } else { return null ; } } /** 添加废弃的view,无序添加 */ void addRecycleView( int position, View scrap) { recycleViews.put(position, scrap); final int length = this .activeViews.length; if (position < length) { this .activeViews[position] = null ; } pruneRecycleViews(); // Log.e("Recycle", "Recycle.size() = " +recycleViews.size()); } /** 将所有剩余的活动view移到废弃view里 */ void recycleAllActiveViews() { final View[] activeViews = this .activeViews; SparseArray<View> recycleViews = this .recycleViews; final int count = activeViews.length; for ( int i = count - 1 ; i >= 0 ; i--) { final View victim = activeViews[i]; if (victim != null ) { activeViews[i] = null ; recycleViews.put(i, victim); } } pruneRecycleViews(); } /** 确保废弃的view不多于活动的view容器的总数量 */ private void pruneRecycleViews() { final int maxViews = activeViews.length; int size = recycleViews.size(); final int extras = size - maxViews; size--; for ( int j = 0 ; j < extras; j++) { recycleViews.remove(recycleViews.keyAt(size--)); } } } 方法仅供参考,具体实现过程还需根据项目实际需求进行修改或优化。欢迎各路高手不吝赐教,多多交流! 本文转自 glblong 51CTO博客,原文链接:http://blog.51cto.com/glblong/1366319,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android 针对继承BaseAdapter的自定义适配器应注意的几个地方

通常我们写一个继承BaseAdapter的自定义适配器的时候,主要想做的大概有两件事: 1、在适配的组件里(如ListView,GridView)定义自己想要的布局(Layout)。 2、为布局里的组件(如Button)添加触发响应的事件(如Click)。 当我们写自定义Adapter的时候需要override以下几个方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Overridepublic int getCount(){ return 0 ; } @Overridepublic ObjectgetItem( int position){ return null ; } @Overridepublic long getItemId( int position){ return 0 ; } @Overridepublic ViewgetView( int position,ViewconvertView,ViewGroupparent){ return null ; } 这里我们需要了解getView方法不是在页面生成的时候只被调用一次,而是根据你当前页面能显示的List里的条目数,被调用数次。什么意思呢?说白了,我们生成的这个自定义的页面(通常是放在ListView里的)看起来好像是一个整体,实际上是由许多个convertView拼装成的,每个convertView就相当于一个Item,然后这些Item被塞到ListView中去,理解不了,可以看下图: 这是一个activity的layout布局。 这是一个Item的layout布局。 convertView里我们就可以随意的加些小组件,比如按钮啊,然后为按钮添加一些事件什么的。 getView里的具体实现我就不缀述了,我想强调的是返回值,一定要return最新生成的convertView,否则功亏一篑。这里我栽过小2次。正确的就是这样: 1 2 3 4 @Overridepublic ViewgetView( int position,ViewconvertView,ViewGroupparent){ //这里就是给获取组件,进行一些赋值操作或者添加事件 return convertView; } 然后,这里还有另外一个坑,就是getCount方法。默认情况下就如同上面写的一样,看似好像可以不用管它,直接return 0;就好,不过经过我这个试坑专员的验证,不管它是绝对不行的。当我们用 listview去setAdapter的时候,程序先走的是你自定义Adapter的构造函数,然后就会走getCount方法,之后才会进入getView方法,如果getCount返回0了,那么后面的事情就不用多想了,不会调用。这个坑我记得是把我整的挺惨,希望大家别掉下去。 本文转自 我不会抽烟 51CTO博客,原文链接:http://blog.51cto.com/zhouhongyu1989/1417226,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册