首页 文章 精选 留言 我的

精选列表

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

Android开发笔记——常见BUG类型之内存泄露与线程安全

一、内存泄露 1、很抱歉,”XXX”已停止运行。OOM? 怎样才能让app报OOM呢?最简单的办法如下: Bitmapbt1=BitmapFactory.decodeResource(this.getResources(),R.drawable.image); Bitmapbt2=BitmapFactory.decodeResource(this.getResources(),R.drawable.image); Bitmapbtn=... 2、查看内存占用 命令行:adb shell dumpsys meminfopackageName 通过Android Studio的Memory Monitor查看内存中Dalvik Heap的实时变化 3、发生内存泄露的条件 首先,每个app有最大内存限制。 ActivityManageractivityManager=(ActivityManager)context.getSystemServiceContext.ACTIVITY_SERVICE); activityManager.getMemoryClass(); getMemoryClass()取到的是最大内存资源。Android中的堆内存分为NativeHeap和DalvikHeap。C/C++申请的内存空间在NativeHeap中,Java申请的内存空间则在DalvikHeap中。对于head堆的大小限制,可以查看/system/build.prop文件: dalvik.vm.heapstartsize=8m dalvik.vm.heapgrowthlimit=96m dalvik.vm.heapsize=256m 注意: heapsize参数表示单个进程heap可用的最大内存,但如果存在以下参数”dalvik.vm.headgrowthlimit =96m”表示单个进程heap内存被限定在96m,即程序运行过程实际只能使用96m内存。 如果申请的内存资源超过上述限制,系统就会抛出OOM错误。 4、常见避免OOM的措施 以下主要从四个方面总结常见的措施:1)减小对象的内存占用;2)内存对象的重复利用;3)避免对象的内存泄露;4)内存使用策略优化。 4.1 减小对象的内存占用 使用ArrayMap/SparseArray而不是HashMap等传统数据结构。 参考链接:Android内存优化(使用SparseArray和ArrayMap代替HashMap) 在Android中避免使用枚举。 减小Bitmap对象的内存占用。inSampleSize和decode format。 4.2 内存对象的重复利用 ListView/GridView等出现大量重复子组件的视图里面对ConvertView的复用 使用LRU机制缓存Bitmap 避免在onDraw方法里面执行对象的创建 使用StringBuilder来替代频繁的”+” 4.3 避免对象的内存泄露 4.1和4.2都是比较常规的措施,4.3需要重点关注。 1)Activity泄露 导致Activity泄露的原因较多,下面列举一些比较常见的。从原理上主要分为两类:i)静态对象;ii)this$0。 Activity被static变量引用。这段代码来自于我们的Crash上传 privatestaticMap<ComponentName,ExceptionHandler>configMap= newHashMap<ComponentName,ExceptionHandler>();publicstaticvoidsetActivity(finalActivityactivity,booleansend2Server){ Log.d(TAG,"bindexceptionhandler:"+activity.getComponentName().getClassName());//上下文初始化SDKContext.init(activity.getApplication()); init(activity.getApplication()); ExceptionHandlerexceptionHandler=newExceptionHandler( activity,send2Server,Thread.getDefaultUncaughtExceptionHandler()); configMap.put(activity.getComponentName(),exceptionHandler); Thread.setDefaultUncaughtExceptionHandler(exceptionHandler); } 下面是通过MAT分析一个Activity泄露的截图: 内部类引用导致Activity的泄漏 最典型的场景是Handler导致的Activity泄漏,如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏。此时的引用关系链是Looper -> MessageQueue -> Message -> Handler -> Activity。为了解决这个问题,可以在UI退出之前,执行remove Handler消息队列中的消息与runnable对象。或者是使用Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。 可参考链接:线程通信 2)考虑使用Application Context而不是Activity Context 对于大部分非必须使用Activity Context的情况(Dialog的Context就必须是Activity Context),我们都可以考虑使用Application Context而不是Activity的Context,这样可以避免不经意的Activity泄露。 3)注意临时Bitmap对象的及时回收 虽然在大多数情况下,我们会对Bitmap增加缓存机制,但是在某些时候,部分Bitmap是需要及时回收的。例如临时创建的某个相对比较大的bitmap对象,在经过变换得到新的bitmap对象之后,应该尽快回收原始的bitmap,这样能够更快释放原始bitmap所占用的空间。 4)内存占用监控 通过Runtime获取maxMemory,而maxMemory-totalMemory即为剩余可使用的dalvik内存。定期检查这个值,达到80%就去释放各种cache资源(bitmap的cache)。 /** *Returnsthemaximumnumberofbytestheheapcanexpandto.See{@link#totalMemory}forthe *currentnumberofbytestakenbytheheap,and{@link#freeMemory}forthecurrentnumberof *thosebytesactuallyusedbyliveobjects.*/intmaxMemory=Runtime.getRuntime().maxMemory());//应用程序最大可用内存/** *Returnsthenumberofbytestakenbytheheapatitscurrentsize.Theheapmayexpandor *contractovertime,asthenumberofliveobjectsincreasesordecreases.See *{@link#maxMemory}forthemaximumheapsize,and{@link#freeMemory}foranideaofhowmuch *theheapcouldcurrentlycontract.*/longtotalMemory=Runtime.getRuntime().totalMemory());//应用程序已获得内存/** *Returnsthenumberofbytescurrentlyavailableontheheapwithoutexpandingtheheap.See *{@link#totalMemory}fortheheap'scurrentsize.Whenthesebytesareexhausted,theheap *mayexpand.See{@link#maxMemory}forthatlimit.*/longfreeMemory=Runtime.getRuntime().freeMemory());//应用程序已获得内存中未使用内存 5)注意Cursor对象是否及时关闭 在程序中我们经常会进行查询数据库的操作,但时常会存在不小心使用Cursor之后没有及时关闭的情况。这些Cursor的泄露,反复多次出现的话会对内存管理产生很大的负面影响,我们需要谨记对Cursor对象的及时关闭。 4.4 内存使用策略优化 谨慎使用large heap 综合考虑设备内存阈值与其他因素设计合适的缓存大小 onLowMemory()/onTrimMemory(int) 资源文件需要选择合适的文件夹进行存放 Try catch某些大内存分配的操作 谨慎使用static对象 优化布局层次,减少内存消耗 谨慎使用多进程 谨慎使用依赖注入框架 使用ProGuard来剔除不需要的代码 谨慎使用第三方libraries 考虑不同的实现方式来优化内存占用 二、线程安全 1、下面的方法是线程安全的吗? 怎样使上述方法线程安全? 2、Java中的线程安全 怎样保持在多线程环境下的数据一致性,Java提供了多种方法实现: synchronized java.util.concurrent.atomic java.util.concurrent.locks thread safe collection(ConcurrentHashMap) volatile 2.1 synchronized JVM保证被synchronized关键字修饰的代码段在同一时间只能被一个线程访问,内部通过对对象或类加锁来实现的。当方法被synchronized修饰时,锁加在对象上;当方法同时为static时,锁加在类上。从性能的角度来讲,一般不建议直接将锁加在类上,这样会使得类的所有对象的该方法均为synchronized的。 从之前扫描的问题来看,在编写synchronized程序时主要有两点需要注意: synchronized需要创建基于对象或者类的锁,所以不能在构造器或者变量上加锁。 synchronized造成死锁。 1) 锁加在哪里? List<ResultPoint>currentPossible=possibleResultPoints; List<ResultPoint>currentLast=lastPossibleResultPoints;intframeLeft=frame.left;intframeTop=frame.top;if(currentPossible.isEmpty()){ lastPossibleResultPoints=null; }else{ possibleResultPoints=newArrayList<>(5); lastPossibleResultPoints=currentPossible; paint.setAlpha(CURRENT_POINT_OPACITY); paint.setColor(resultPointColor);synchronized(currentPossible){for(ResultPointpoint:currentPossible){ canvas.drawCircle(frameLeft+(int)(point.getX()*scaleX),frameTop+(int)(point.getY()*scaleY),POINT_SIZE, paint); } } } 上述方法中,possibleResultPoints的创建没有采用同步措施,需要使用Collections.synchronizedXxx。 List<MyType>list=Collections.synchronizedList(newArrayList(<MyType>)); ...synchronized(list){for(MyTypem:list){ foo(m); m.doSomething(); } } 一般比较推荐创建一个虚拟的对象专门用于获取锁。 //dummyobjectvariableforsynchronizationprivateObjectmutex=newObject(); ...//usingsynchronizedblocktoread,incrementandupdatecountvaluesynchronouslysynchronized(mutex){ count++; } PS:直接在方法上加synchronized可能DoS攻击喔,举个栗子: publicclassMyObject{//Locksontheobject'smonitor publicsynchronizedvoiddoSomething(){ //...} }//黑客的代码MyObjectmyObject=newMyObject();synchronized(myObject){while(true){//IndefinitelydelaymyObjectThread.sleep(Integer.MAX_VALUE); } } 黑客的代码获取了MyObject对象的锁,导致doSomething死锁,从而引发Denial of Service。 publicclassMyObject{//locksontheclassobject'smonitor publicstaticsynchronizedvoiddoSomething(){ //...} }//黑客的代码synchronized(MyObject.class){while(true){ Thread.sleep(Integer.MAX_VALUE);//IndefinitelydelayMyObject} } 2) 死锁。 publicclassThreadDeadlock{publicstaticvoidmain(String[]args)throwsInterruptedException{ Objectobj1=newObject(); Objectobj2=newObject(); Objectobj3=newObject(); Threadt1=newThread(newSyncThread(obj1,obj2),"t1"); Threadt2=newThread(newSyncThread(obj2,obj3),"t2"); Threadt3=newThread(newSyncThread(obj3,obj1),"t3"); t1.start(); Thread.sleep(5000); t2.start(); Thread.sleep(5000); t3.start(); } }classSyncThreadimplementsRunnable{privateObjectobj1;privateObjectobj2;publicSyncThread(Objecto1,Objecto2){this.obj1=o1;this.obj2=o2; } @Overridepublicvoidrun(){ Stringname=Thread.currentThread().getName(); System.out.println(name+"acquiringlockon"+obj1);synchronized(obj1){ System.out.println(name+"acquiredlockon"+obj1); work(); System.out.println(name+"acquiringlockon"+obj2);synchronized(obj2){ System.out.println(name+"acquiredlockon"+obj2); work(); } System.out.println(name+"releasedlockon"+obj2); } System.out.println(name+"releasedlockon"+obj1); System.out.println(name+"finishedexecution."); }privatevoidwork(){try{ Thread.sleep(30000); }catch(InterruptedExceptione){ e.printStackTrace(); } } } 本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1929765 ,如需转载请自行联系原作者

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

Android GIS开发系列-- 入门季(6)GraphicsLayer添加文字与图片标签

一、GraphicsLayer添加图片 GraphicLayer添加图片Graphic,要用到PictureMarkerSymbol,也是样式的一种。添加代码如下: Drawable drawable = getResources().getDrawable(R.mipmap.ic_launcher); PictureMarkerSymbol pictureMarkerSymbol = new PictureMarkerSymbol(this, drawable); Graphic graphic = new Graphic(new Point(113,22), pictureMarkerSymbol); graphicsLayer.addGraphic(graphic); 添加图片效果如下: 二、GraphicsLayer添加文字 GraphicLayer添加文字,要利用TextSymbol。代码如下 TextSymbol textSymbol = new TextSymbol(20,"我是文字" , Color.RED); Graphic graphic = new Graphic(new Point(113,22), textSymbol ); graphicsLayer.addGraphic(graphic); 上面的代码没有设置使用哪种字体,Arcgis会默认的字体。如果 我们想添加中文,要设置textSymbol.setFontFamily("DroidSansFallback.ttf");, 为textSymbol设置字体,arcgis会到system\fonts下找到此字体。但有时我们设置此代码时中文并没有出现,可能会有乱码等,是因为系统的fon ts下面并没有此字体,解决的办法有,将手机root,将DroidSansFallback.ttf字体添加到system\fonts下。华为的手机是有此字体的,而三星5.0以 上则没有此字体,在华为手机上还有一款中文DroidSansChinese.ttf字体arcgis也是支持的。而三星5.0以上的中文字体好像不是ttf字体,而是otf字体 ,Arcgis好像不支持,本人也没有找到好的解决办法。 没有整理与归纳的知识,一文不值!高度概括与梳理的知识,才是自己真正的知识与技能。 永远不要让自己的自由、好奇、充满创造力的想法被现实的框架所束缚,让创造力自由成长吧! 多花时间,关心他(她)人,正如别人所关心你的。理想的腾飞与实现,没有别人的支持与帮助,是万万不能的。 本文转自wenglabs博客园博客,原文链接:http://www.cnblogs.com/arxive/p/7751888.html ,如需转载请自行联系原作者

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

打开键盘遮住View的问题解决方法-IOS开发

5.0 以后键盘的高度貌似不是216了,不过不要紧,我们调整调整就是了: iPhone ipad 竖屏(portrait) 216 264 横屏(landScape) 140 352 我们采取的方法就是在textField(有可能是其他控件)接收到弹出键盘事件时把self.view整体上移216px了(我们就以iPhone竖屏为例了)。 有关View的frame,origin,size之类的知识点不懂的请参看我的另一篇博文: <<有关View的几个基础知识点>> 首先我们要设置textField的代理,我们就设为当前控制器了。 textField,delegate=self; 然后我们在当前控制器实现下面三个委托方法: [java]view plaincopy print? -(void)textFieldDidBeginEditing:(UITextField*)textField {//当点触textField内部,开始编辑都会调用这个方法。textField将成为firstresponder NSTimeIntervalanimationDuration=0.30f; CGRectframe=self.view.frame; frame.origin.y-=216; frame.size.height+=216; self.view.frame=frame; [UIViewbeginAnimations:@"ResizeView"context:nil]; [UIViewsetAnimationDuration:animationDuration]; self.view.frame=frame; [UIViewcommitAnimations]; } [java]view plaincopy print? -(BOOL)textFieldShouldReturn:(UITextField*)textField {//当用户按下ruturn,把焦点从textField移开那么键盘就会消失了 NSTimeIntervalanimationDuration=0.30f; CGRectframe=self.view.frame; frame.origin.y+=216; frame.size.height-=216; self.view.frame=frame; //self.view移回原位置 [UIViewbeginAnimations:@"ResizeView"context:nil]; [UIViewsetAnimationDuration:animationDuration]; self.view.frame=frame; [UIViewcommitAnimations]; [textFieldresignFirstResponder]; } 本文转自 卓行天下 51CTO博客,原文链接:http://blog.51cto.com/9951038/1772565,如需转载请自行联系原作者

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

Android Ap 开发 设计模式第一篇:迭代器模式

Iterator Pattern迭代器模式 场景猜想 经常编写代码都会碰到遍历一个数组,使用for循环得到数组下标之后去做进一步操作。例如下文代码: int []array = new int [ 5 ]; for ( int i = 0 ;i < array.length;i ++ ){ System.out.println( "" + i); } 或: int []array = new int [ 5 ]; for ( int i:array){ System.out.println( "" + i); } 当然,还有更多的作法,利用将 i 将下标推移的方式遍历数组元素。 使用设计模式来编写的时候,可以将 i 的行为,抽象化为迭代器,这种模式我们称之为迭代器模式 。迭代器模式可以用来作为遍历一个集合体。java 亦提供了一个工具类:java.util.Iterator<E> ,与其类似。 场景需求 书架上有5本技术书籍,要求将书籍名称一性遍历出来,将显示在Andriod 的 EditText 上面。以上面的需求为例,可以将具体需求具体到下面的示意图: Aggregate接口 Aggregate 接口是一个执行递增的“聚合”。实现此接口的类就变成类似数组、集合的“聚合”。表示己聚合的接口。 Iterator接口 Iterator 接口执行元素递增,具有类似循环变量的功能。表示执行递增、遍历的接口。 Book 类 Book 表示书籍,主要是为了获取书的详细。 BookShelf类 BookShelf 表示书架,此类实现Aggregate接口从而将其作为聚合处理。 BookShelfIterator类 BookShelfIterator表示扫描书架的类,实现了Iterator 的接口功能。 ----------------------------------------- IteratorPatternsActivity 类 IteratorPatternsActivity 为Android 的主界面,用来显示和测试上面编写的代码 ----------------------------------------- 代码实现: Aggregate接口 /** *声明一个Iterator方法,这是为了建立一个可对应聚合的Iterator *如欲递增、遍历或逐一检查某一个聚合时,利用iterator方法即可建立一个 *实现Iterator接口的类对象实例。 * @author terry * */ public interface Aggregate{ public abstract Iteratoriterator(); } Iterator 接口 public interface Iterator{ public abstract boolean hasNext(); // 存在一条则返回true public abstract Objectnext(); // 移到下一条记录 } next 方法是返回聚合当中的1个元素。不过,next 方法的功能并非仅仅如此。事实上它还会悄悄地先要在内部状态进入下一步,以便下次调用 next 方法时能确实返回下一个元素。而Iterator 只描述了有两个方法,具体这两个方法如何实现还要看BookShelfIterator内部的实现。 Book 类 /** *表示书籍的类 * @author terry * */ public class Book{ private Stringname = "" ; public Book(Stringname){ this .name = name; } public StringgetName(){ return this .name; } } BookShelf 类 /** *表示书架意图的类 *要实现Aggreagate接口才能把这个类当作聚合来处理。 * @author terry * */ public class BookShelf implements Aggregate{ private Book[]books; private int last = 0 ; public BookShelf( int maxSize){ this .books = new Book[maxSize]; } public BookgetBookAt( int index){ return books[index]; } public BookShelfappendBook(Bookbook){ this .books[last] = book; last ++ ; return this ; } public int getLength(){ return this .last; } @Override public Iteratoriterator(){ // TODOAuto-generatedmethodstub return new BookShelfIterator( this ); } } BookShelf实现了Aggregate接口 ,该方法产生并返回BookShelfIterator类的对象实例,即我们发一个命令说要对书架进行扫描时,就将得到的“聚合”返回出去。 BookShelfIterator 类 1 /** 2 *执行扫描书架用的“聚合”类 3 * @author Administrator 4 * 5 */ 6 public class BookShelfIterator implements Iterator{ 7 8 private BookShelfbookShelf; 9 private int index; 10 11 public BookShelfIterator(BookShelfbookShelf){ 12 this .bookShelf = bookShelf; 13 this .index = 0 ; 14 } 15 16 17 @Override 18 public boolean hasNext(){ 19 // TODOAuto-generatedmethodstub 20 if (index < bookShelf.getLength()){ 21 return true ; 22 } else { 23 24 return false ; 25 } 26 27 } 28 29 @Override 30 public Objectnext(){ 31 // TODOAuto-generatedmethodstub 32 Bookbook = bookShelf.getBookAt(index); 33 index ++ ; 34 35 return book; 36 } 37 38 } 做完上面一系列的代码编写之后,我们就可以在Android 的主界面上使用上面的Iterator 遍历出上面给出的需求。 IteratorPatternsActivity类 public class IteratorPatternsActivity extends Activity{ /** Calledwhentheactivityisfirstcreated. */ @Override public void onCreate(BundlesavedInstanceState){ super .onCreate(savedInstanceState); setContentView(R.layout.main); EditTextet = (EditText)findViewById(R.id.EditText01); BookShelfbookShelf = new BookShelf( 5 ); bookShelf.appendBook( new Book( " androiddeveloper " )) .appendBook( new Book( " WindowsPhone7developer " )) .appendBook( new Book( " IOSdeveloper " )) .appendBook( new Book( " BADAdeveloper " )) .appendBook( new Book( " Liondeveloper " )); Iteratoriterator = bookShelf.iterator(); while (iterator.hasNext()){ Bookbook = (Book)iterator.next(); et.setText(et.getText() + " \n " + book.getName()); } } } 上面执行的结果: 更多的功能扩展 上面只是讲述了两个功能,hasNext()和Next()方法,想想如果要在这个基础上面扩展将会是多么方便,例如: 由后往前的反向遍历 由前往后、由后往前的双向遍历(Next 、Previous......) 指定一个下标立即得到 等等 系列学习文章第一篇到此结束。下次学习Adapter Pattern (适配器模式) 本文转自 terry_龙 51CTO博客,原文链接:http://blog.51cto.com/terryblog/603456,如需转载请自行联系原作者

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

【移动开发】Android相机、相册获取图片显示并保存到SD卡

做过类似需求的同学都知道,在Activity中通过如下代码可以启动相机,然后在重写的onActivityResult方法中可以获取到返回的照片数据: 1 2 IntentopenCameraIntent= new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(openCameraIntent,TAKE_PICTURE); 在onActivityResult方法里通过Intent的getData方法获取的数据转换成bitmap并显示在界面上,有时候会有取不到数据,或者显示的bitmap会非常小,如果将bitmap保存到sd卡后会发现,图片的分辨率很低,并且图片大小也是经过压缩的,不管将相机的像素设置多高,最后通过这种方式返回的bitmap总是经过压缩了的。如果想获得理想的照片大小和分辨率改如何处理呢? 大家都知道,现在手机像素少则500W或800W,多则4KW(某亚),就拿常见的800W像素的相机拍出来的照片来说,分辨率大概在3200*2400左右,照片大小在2M左右。试想一下,在Android系统中bitmap占用4个字节,3200*2400*4=?,结果大家自己算算,如果为了一张图片,耗用这么大的内存,肯定是不合理的,并且,官方文档中有说明,Android系统分配给每个应用的最大内存是16M,所以,系统为了防止应用内存占用过大,对于在应用内通过相机拍摄的图片最终返回来的结果进行了压缩,压缩后的图片变得很小,通过之前说的getData的方式只能满足比如显示个头像这样的需求。 如果要显示大图,就会出现模糊的情况。那如何获取清晰的大图呢?我的解决思路如下: 1.拍照时,将拍得的照片先保存在本地,通过修改之前的代码如下: 1 UriimageUri=Uri.fromFile( new File(Environment.getExternalStorageDirectory(), "image.jpg" )); //指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换 1 openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); 如何调取相机拍照,代码如下: 1 2 3 4 5 6 7 8 9 10 11 /**拍照获取相片**/ private void doTakePhoto(){ Intentintent= new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //调用系统相机 UriimageUri=Uri.fromFile( new File(Environment.getExternalStorageDirectory(), "image.jpg" )); //指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); //直接使用,没有缩小 startActivityForResult(intent,PHOTO_WITH_CAMERA); //用户点击了从相机获取 } 2.在onActivityResult方法中再将图片取出,并经过缩小处理再显示在界面上或上传给服务器(压缩比例自定义) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override protected void onActivityResult( int requestCode, int resultCode,Intentdata){ super .onActivityResult(requestCode,resultCode,data); if (resultCode==RESULT_OK){ switch (requestCode){ case TAKE_PICTURE: //将保存在本地的图片取出并缩小后显示在界面上 Bitmapbitmap=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+ "/image.jpg" ); BitmapnewBitmap=zoomBitmap(bitmap,bitmap.getWidth()/SCALE,bitmap.getHeight()/SCALE); //由于Bitmap内存占用较大,这里需要回收内存,否则会报outofmemory异常 bitmap.recycle(); //将处理过的图片显示在界面上,并保存到本地 iv_image.setImageBitmap(newBitmap); savePhotoToSDCard(newBitmap,Environment.getExternalStorageDirectory().getAbsolutePath(), String .valueOf(System.currentTimeMillis())); break ; default : break ; } } } 由于Android给bitmap分配的内存最大不超过8M,所以对使用完的较大的Bitmap要释放内存,调用其recycle()方法即可。然后将缩小后的bitmap显示在界面上或保存到SD卡,至于之前保存的原图,可以删掉,也可以放在那,下次拍照时,这张原图就会被下一张照片覆盖,所以SD卡上使用只有一张临时图片,占用也不是很大。 以上讲的是拍照获取图片,如果是从相册中获取图片又如何处理呢,我的方法如下: 1.打开相册选取图片: 1 2 3 IntentopenAlbumIntent= new Intent(Intent.ACTION_GET_CONTENT); openAlbumIntent.setType( "image/*" ); startActivityForResult(openAlbumIntent,CHOOSE_PICTURE); 2.在onActivity方法中处理获取到的图片,思路和之前类似 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 @Override protected void onActivityResult( int requestCode, int resultCode,Intentdata){ super .onActivityResult(requestCode,resultCode,data); if (resultCode==RESULT_OK){ switch (requestCode){ case CHOOSE_PICTURE: ContentResolverresolver=getContentResolver(); //照片的原始资源地址 UrioriginalUri=data.getData(); try { //使用ContentProvider通过URI获取原始图片 Bitmapphoto=MediaStore.Images.Media.getBitmap(resolver,originalUri); if (photo!= null ){ //为防止原始图片过大导致内存溢出,这里先缩小原图显示,然后释放原始Bitmap占用的内存 BitmapsmallBitmap=zoomBitmap(photo,photo.getWidth()/SCALE,photo.getHeight()/SCALE); //释放原始图片占用的内存,防止outofmemory异常发生 photo.recycle(); iv_image.setImageBitmap(smallBitmap); } } catch (FileNotFoundExceptione){ e.printStackTrace(); } catch (IOExceptione){ e.printStackTrace(); } break ; default : break ; } } } 还有一个方法 zoomBitmap(),代码如下: 1 2 3 4 5 6 7 8 9 10 11 /**缩放Bitmap图片**/ public BitmapzoomBitmap(Bitmapbitmap, int width, int height){ int w=bitmap.getWidth(); int h=bitmap.getHeight(); Matrixmatrix= new Matrix(); floatscaleWidth=((float)width/w); floatscaleHeight=((float)height/h); matrix.postScale(scaleWidth,scaleHeight); //利用矩阵进行缩放不会造成内存溢出 Bitmapnewbmp=Bitmap.createBitmap(bitmap, 0 , 0 ,w,h,matrix, true ); return newbmp; } 至此,功能已实现。 下面是本人项目中所实现的功能在这里总结一下: 1.要想对从图库选择的照片进行裁剪: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /**从相册获取图片**/ private IntentdoPickPhotoFromGallery(){ Intentintent= new Intent(); intent.setType( "image/*" ); //开启Pictures画面Type设定为image intent.setAction(Intent.ACTION_GET_CONTENT); //使用Intent.ACTION_GET_CONTENT这个Action //实现对图片的裁剪,必须要设置图片的属性和大小 intent.setType( "image/*" ); //获取任意图片类型 intent.putExtra( "crop" , "true" ); //滑动选中图片区域 intent.putExtra( "aspectX" , 1 ); //裁剪框比例1:1 intent.putExtra( "aspectY" , 1 ); intent.putExtra( "outputX" , 300 ); //输出图片大小 intent.putExtra( "outputY" , 300 ); intent.putExtra( "return-data" , true ); //有返回值 return intent; } 调用此方法处: 1 2 Intentintent2=doPickPhotoFromGallery(); startActivityForResult(intent2,PHOTO_WITH_DATA); ActivityForResult中和上面一样 2.项目中要拍多少张 就保存多少张,显示图片列表: A.将拍照的照片或者图库选择的图片,保存到本地 创建图片名,不能重复哦! 1 2 3 4 5 6 7 8 9 /**为图片创建不同的名称用于保存,避免覆盖**/ public static String createFileName(){ String fileName= "" ; Date date= new Date (System.currentTimeMillis()); //系统当前时间 SimpleDateFormatdateFormat= new SimpleDateFormat( "'IMG'_yyyyMMdd_HHmmss" ); fileName=dateFormat.format(date)+ ".jpg" ; return fileName; } 保存图片到SD卡 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 /**SaveimagetotheSDcard**/ public static void savePhotoToSDCard( String path, String photoName, BitmapphotoBitmap){ if (android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)){ Filedir= new File(path); if (!dir.exists()){ dir.mkdirs(); } FilephotoFile= new File(path,photoName); //在指定路径下创建文件 FileOutputStreamfileOutputStream= null ; try { fileOutputStream= new FileOutputStream(photoFile); if (photoBitmap!= null ){ if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100 , fileOutputStream)){ fileOutputStream.flush(); } } } catch (FileNotFoundExceptione){ photoFile. delete (); e.printStackTrace(); } catch (IOExceptione){ photoFile. delete (); e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOExceptione){ e.printStackTrace(); } } } } B.最后就是显示图片列表,因为我们要用到listView,自然少不了Adapter了,我们将保存到SD卡上的图片名获取到集合中,在自定义的适配器中根据名字加载图片喽! 自定义图片列表适配器代码: 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 /** *插入图片列表适配器 *@authorZHF * */ public class ImagesListAdapter extends BaseAdapter{ private Contextcontext; private List< String >imagesList; //各个图片的路径 public ImagesListAdapter(Contextcontext,List< String >imagesList){ this .context=context; this .imagesList=imagesList; } /**得到总的数量**/ @Override public int getCount(){ //TODOAuto-generatedmethodstub return imagesList.size(); } /**根据ListView位置返回View**/ @Override public Object getItem( int position){ return imagesList. get (position); //返回当前选中的item图片的路径 } /**根据ListView位置得到List中的ID**/ @Override public longgetItemId( int position){ //TODOAuto-generatedmethodstub return position; //返回当前选中项的Id } /**根据位置得到View对象**/ @Override public ViewgetView( int position,ViewconvertView,ViewGroupparent){ if (convertView== null ){ convertView=LayoutInflater.from(context).inflate(R.layout.newwrite_image_item, null ); } ImageViewimg=(ImageView)convertView.findViewById(R.id.newwrite_et_content_image); //图片列表项 if (!imagesList. get (position).equals( "" )){ //没有图片 BitmaptempBitmap=BitmapFactory.decodeFile(imagesList. get (position)); //根据路径显示对应的图片 BitmapnewBitmap= new ImageManager(context).zoomBitmap(tempBitmap,tempBitmap.getWidth(),tempBitmap.getHeight()/ 3 ); img.setImageBitmap(newBitmap); //对应的行上显示对应的图片 } return convertView; } } ok!完了,在显示图片的时候大家可能会碰到OOM(OutOfMemory)异常,在下一篇博客中我会具体解决了一下~ Demo已上传!下载附件即可! 本文转自zhf651555765 51CTO博客,原文链接:http://blog.51cto.com/smallwoniu/1248695,如需转载请自行联系原作者

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

Android开发22——广播接收者BroadcastReceiver的原理和注册方式

一、广播机制的基本概念 当某个事件产生时(如一条短信发来或一个电话打来),android操作系统会把这个事件广播给所有注册的广播接收者,需要处理这个事件的广播接收者进行处理。其实这就是日常生活中的广播。发生一个新闻后,广播电台会广播这个新闻给打开收音机的人,对这个新闻感兴趣的人会关注,可能会拿笔记下。新闻就是事件,广播电台就是android系统,打开收音机的人就是广播接收者,感兴趣的人就是需要处理该事件的广播接收者,拿笔记下就是对该事件进行的操作。 二、广播的分类——普通广播和有序广播 ①普通广播:完全异步,逻辑上可以被任何广播接收者接收到。优点是效率较高。缺点是一个接收者不能将处理结果传递给下一个接收者,并无法终止广播intent的传播。 ②有序广播:按照被接收者的优先级顺序,在被接收者中一次传播。比如有三个广播接收者A,B,C,优先级是A > B > C。那这个消息先传给A,再传给B,最后传给C。每个接收者有权中终止广播,比如B终止广播,C就无法接收到。此外A接收到广播后可以对结果对象进行操作,当广播传给B时,B可以从结果对象中取得A存入的数据。如系统收到短信发出的广播就是有序广播。 三、注册广播接收者的两种方式 ①在AndroidManifest.xml中注册 在配置文件中注册的接收者的特点是即使应用程序已被关闭,该接收者依然可接受它感兴趣的广播,比如手机电池电量的广播接收者,没有必要将某个程序开启。下面的例子1、2广播接收者会接收到拨打电话的广播。 <applicationandroid:icon="@drawable/icon"android:label="@string/app_name"> <activityandroid:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!--广播接收者1--> <receiverandroid:name=".BroadcastReceiver1"> <intent-filter> <actionandroid:name="android.intent.action.CALL"></action> </intent-filter> </receiver> <!--广播接收者2--> <receiverandroid:name=".BroadcastReceiver2"> <intent-filter> <actionandroid:name="android.intent.action.CALL"></action> </intent-filter> </receiver> <!--广播接收者3--> <receiverandroid:name=".BroadcastReceiver3"> <intent-filter> <actionandroid:name="android.intent.action.PICK"></action> </intent-filter> </receiver> </application> /** *模拟拨打电话广播 * *@author徐越 * */ publicclassMainActivityextendsActivity { @Override publicvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Intentintent=newIntent(); intent.setAction("android.intent.action.CALL"); this.sendBroadcast(intent); } } /** *每次接收广播都会生成新的BroadcastReceiver1,当处理完onReceive方法后就不会再被使用 *再次接收就在生成新的BroadcastReceiver1对象 * *@author徐越 * */ publicclassBroadcastReceiver1extendsandroid.content.BroadcastReceiver { publicBroadcastReceiver1() { Log.i("xy_Receiver","construtor1"); } @Override publicvoidonReceive(Contextcontext,Intentintent) { Log.i("xy_Receiver","onReceive1"); } } /** *广播接收者2 * *@author徐越 * */ publicclassBroadcastReceiver2extendsandroid.content.BroadcastReceiver { publicBroadcastReceiver2() { Log.i("xy_Receiver","construtor2"); } @Override publicvoidonReceive(Contextcontext,Intentintent) { Log.i("xy_Receiver","onReceive2"); } } /** *广播接收者3 * *@author徐越 * */ publicclassBroadcastReceiver3extendsandroid.content.BroadcastReceiver { publicBroadcastReceiver3() { Log.i("xy_Receiver","construtor3"); } @Override publicvoidonReceive(Contextcontext,Intentintent) { Log.i("xy_Receiver","onReceive3"); } } ②在Activity中注册 在Activity中绑定接收者必须依附该应用程序存在,或者一个BroadcastReceiver用于更新UI,就没有必要再程序关闭时接收者还运行,故无需在AndroidManifest.xml中注册而可以放在Activity中注册。 /** *Activity中注册广播接收者 * *@author徐越 * */ publicclassMainActivityextendsActivity { privateBroadcastReceiverreceiver; privatestaticfinalStringCALL_ACTION="android.intent.action.CALL"; @Override publicvoidonCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } /** *模拟发送一个电话的广播 * *@paramv */ publicvoidsendBroadCast(Viewv) { Intentintent=newIntent(); intent.setAction("android.intent.action.CALL"); this.sendBroadcast(intent); } publicvoidbindReceiver(Viewv) { receiver=newBroadcastReceiver(); IntentFilterintentFilter=newIntentFilter(); intentFilter.addAction(CALL_ACTION); this.registerReceiver(receiver,intentFilter); } publicvoidunBindReceiver(Viewv) { this.unregisterReceiver(receiver); } } publicclassBroadcastReceiverextendsandroid.content.BroadcastReceiver { @Override publicvoidonReceive(Contextcontext,Intentintent) { Log.i("xy","receiver"); } } 本文转自IT徐胖子的专栏博客51CTO博客,原文链接http://blog.51cto.com/woshixy/1097197如需转载请自行联系原作者 woshixuye111

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

Android UI开发第十六篇——分享一个popuwindow实例

PopupWindow在android.widget包下,弹出窗口的形式展示。官方文档对该控件的描述是:“一个弹出窗口控件,可以用来显示任意视图(View),而且会浮动在当前 活动(activity)的顶部”。PopupWindow可以让我们实现多种自定义控件,例如:menu、alertdialog等弹窗似的View。 实现中使用的PopupWindow。这里做了简单封装,其中有三个类组成:PopuItem、PopuJar、PopupWindows。 publicclassPopuItem{ privateDrawableicon; privateBitmapthumb; privateStringtitle; privateintactionId=-1; privatebooleanselected; privatebooleansticky; /** *Constructor * *@paramactionIdActionidforcasestatements *@paramtitleTitle *@paramiconIcontouse */ publicPopuItem(intactionId,Stringtitle,Drawableicon){ this.title=title; this.icon=icon; this.actionId=actionId; } /** *Constructor */ publicPopuItem(){ this(-1,null,null); } /** *Constructor * *@paramactionIdActionidoftheitem *@paramtitleTexttoshowfortheitem */ publicPopuItem(intactionId,Stringtitle){ this(actionId,title,null); } /** *Constructor * *@paramicon{@linkDrawable}actionicon */ publicPopuItem(Drawableicon){ this(-1,null,icon); } /** *Constructor * *@paramactionIdActionIDofitem *@paramicon{@linkDrawable}actionicon */ publicPopuItem(intactionId,Drawableicon){ this(actionId,null,icon); } /** *Setactiontitle * *@paramtitleactiontitle */ publicvoidsetTitle(Stringtitle){ this.title=title; } /** *Getactiontitle * *@returnactiontitle */ publicStringgetTitle(){ returnthis.title; } /** *Setactionicon * *@paramicon{@linkDrawable}actionicon */ publicvoidsetIcon(Drawableicon){ this.icon=icon; } /** *Getactionicon *@return{@linkDrawable}actionicon */ publicDrawablegetIcon(){ returnthis.icon; } /** *Setactionid * *@paramactionIdActionidforthisaction */ publicvoidsetActionId(intactionId){ this.actionId=actionId; } /** *@returnOuractionid */ publicintgetActionId(){ returnactionId; } /** *Setstickystatusofbutton * *@paramstickytrueforsticky,popupsendseventbutdoesnotdisappear */ publicvoidsetSticky(booleansticky){ this.sticky=sticky; } /** *@returntrueifbuttonissticky,menustaysvisibleafterpress */ publicbooleanisSticky(){ returnsticky; } /** *Setselectedflag; * *@paramselectedFlagtoindicatetheitemisselected */ publicvoidsetSelected(booleanselected){ this.selected=selected; } /** *Checkifitemisselected * *@returntrueorfalse */ publicbooleanisSelected(){ returnthis.selected; } /** *Setthumb * *@paramthumbThumbimage */ publicvoidsetThumb(Bitmapthumb){ this.thumb=thumb; } /** *Getthumbimage * *@returnThumbimage */ publicBitmapgetThumb(){ returnthis.thumb; } } public class PopuJar extends PopupWindows implements OnDismissListener { private View mRootView; private ImageView mArrowUp; private ImageView mArrowDown; private LayoutInflater mInflater; private ViewGroup mTrack; private ScrollView mScroller; private OnPopuItemClickListener mItemClickListener; private OnDismissListener mDismissListener; private List<PopuItem> PopuItems = new ArrayList<PopuItem>(); private boolean mDidAction; private int mChildPos; private int mInsertPos; private int mAnimStyle; private int mOrientation; private int rootWidth=0; public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; public static final int ANIM_GROW_FROM_LEFT = 1; public static final int ANIM_GROW_FROM_RIGHT = 2; public static final int ANIM_GROW_FROM_CENTER = 3; public static final int ANIM_REFLECT = 4; public static final int ANIM_AUTO = 5; /** * Constructor for default vertical layout * * @param context Context */ public PopuJar(Context context) { this(context, VERTICAL); } /** * Constructor allowing orientation override * * @param context Context * @param orientation Layout orientation, can be vartical or horizontal */ public PopuJar(Context context, int orientation) { super(context); mOrientation = orientation; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (mOrientation == HORIZONTAL) { setRootViewId(R.layout.popup_horizontal); } else { setRootViewId(R.layout.popup_vertical); } mAnimStyle = ANIM_AUTO; mChildPos = 0; } /** * Get action item at an index * * @param index Index of item (position from callback) * * @return Action Item at the position */ public PopuItem getPopuItem(int index) { return PopuItems.get(index); } /** * Set root view. * * @param id Layout resource id */ public void setRootViewId(int id) { mRootView = (ViewGroup) mInflater.inflate(id, null); mTrack = (ViewGroup) mRootView.findViewById(R.id.tracks); mArrowDown = (ImageView) mRootView.findViewById(R.id.arrow_down); mArrowUp = (ImageView) mRootView.findViewById(R.id.arrow_up); mScroller = (ScrollView) mRootView.findViewById(R.id.scroller); //This was previously defined on show() method, moved here to prevent force close that occured //when tapping fastly on a view to show quickaction dialog. //Thanx to zammbi (github.com/zammbi) mRootView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); setContentView(mRootView); } /** * Set animation style * * @param mAnimStyle animation style, default is set to ANIM_AUTO */ public void setAnimStyle(int mAnimStyle) { this.mAnimStyle = mAnimStyle; } /** * Set listener for action item clicked. * * @param listener Listener */ public void setOnPopuItemClickListener(OnPopuItemClickListener listener) { mItemClickListener = listener; } /** * Add action item * * @param action {@link PopuItem} */ public void addPopuItem(PopuItem action) { PopuItems.add(action); String title = action.getTitle(); Drawable icon = action.getIcon(); View container; if (mOrientation == HORIZONTAL) { container = mInflater.inflate(R.layout.action_item_horizontal, null); } else { container = mInflater.inflate(R.layout.action_item_vertical, null); } ImageView img = (ImageView) container.findViewById(R.id.iv_icon); TextView text = (TextView) container.findViewById(R.id.tv_title); if (icon != null) { img.setImageDrawable(icon); } else { img.setVisibility(View.GONE); } if (title != null) { text.setText(title); } else { text.setVisibility(View.GONE); } final int pos = mChildPos; final int actionId = action.getActionId(); container.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mItemClickListener != null) { mItemClickListener.onItemClick(PopuJar.this, pos, actionId); } if (!getPopuItem(pos).isSticky()) { mDidAction = true; dismiss(); } } }); container.setFocusable(true); container.setClickable(true); if (mOrientation == HORIZONTAL && mChildPos != 0) { View separator = mInflater.inflate(R.layout.horiz_separator, null); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); separator.setLayoutParams(params); separator.setPadding(5, 0, 5, 0); mTrack.addView(separator, mInsertPos); mInsertPos++; } mTrack.addView(container, mInsertPos); mChildPos++; mInsertPos++; } /** * Show quickaction popup. Popup is automatically positioned, on top or bottom of anchor view. * */ public void show (View anchor) { preShow(); int xPos, yPos, arrowPos; mDidAction = false; int[] location = new int[2]; anchor.getLocationOnScreen(location); Rect anchorRect = new Rect(location[0], location[1], location[0] + anchor.getWidth(), location[1] + anchor.getHeight()); //mRootView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); mRootView.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); int rootHeight = mRootView.getMeasuredHeight(); if (rootWidth == 0) { rootWidth = mRootView.getMeasuredWidth(); } int screenWidth = mWindowManager.getDefaultDisplay().getWidth(); int screenHeight = mWindowManager.getDefaultDisplay().getHeight(); //automatically get X coord of popup (top left) if ((anchorRect.left + rootWidth) > screenWidth) { xPos = anchorRect.left - (rootWidth-anchor.getWidth()); xPos = (xPos < 0) ? 0 : xPos; arrowPos = anchorRect.centerX()-xPos; } else { if (anchor.getWidth() > rootWidth) { xPos = anchorRect.centerX() - (rootWidth/2); } else { xPos = anchorRect.left; } arrowPos = anchorRect.centerX()-xPos; } int dyTop = anchorRect.top; int dyBottom = screenHeight - anchorRect.bottom; boolean onTop = (dyTop > dyBottom) ? true : false; if (onTop) { if (rootHeight > dyTop) { yPos = 15; LayoutParams l = mScroller.getLayoutParams(); l.height = dyTop - anchor.getHeight(); } else { yPos = anchorRect.top - rootHeight; } } else { yPos = anchorRect.bottom; if (rootHeight > dyBottom) { LayoutParams l = mScroller.getLayoutParams(); l.height = dyBottom; } } showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), arrowPos); setAnimationStyle(screenWidth, anchorRect.centerX(), onTop); mWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, xPos, yPos); } /** * Set animation style * * @param screenWidth screen width * @param requestedX distance from left edge * @param onTop flag to indicate where the popup should be displayed. Set TRUE if displayed on top of anchor view * and vice versa */ private void setAnimationStyle(int screenWidth, int requestedX, boolean onTop) { int arrowPos = requestedX - mArrowUp.getMeasuredWidth()/2; switch (mAnimStyle) { case ANIM_GROW_FROM_LEFT: mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left); break; case ANIM_GROW_FROM_RIGHT: mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right); break; case ANIM_GROW_FROM_CENTER: mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center); break; case ANIM_REFLECT: mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect : R.style.Animations_PopDownMenu_Reflect); break; case ANIM_AUTO: if (arrowPos <= screenWidth/4) { mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left); } else if (arrowPos > screenWidth/4 && arrowPos < 3 * (screenWidth/4)) { mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center); } else { mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right); } break; } } /** * Show arrow * * @param whichArrow arrow type resource id * @param requestedX distance from left screen */ private void showArrow(int whichArrow, int requestedX) { final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown; final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp; final int arrowWidth = mArrowUp.getMeasuredWidth(); showArrow.setVisibility(View.VISIBLE); ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams)showArrow.getLayoutParams(); param.leftMargin = requestedX - arrowWidth / 2; hideArrow.setVisibility(View.INVISIBLE); } /** * Set listener for window dismissed. This listener will only be fired if the quicakction dialog is dismissed * by clicking outside the dialog or clicking on sticky item. */ public void setOnDismissListener(PopuJar.OnDismissListener listener) { setOnDismissListener(this); mDismissListener = listener; } @Override public void onDismiss() { if (!mDidAction && mDismissListener != null) { mDismissListener.onDismiss(); } } /** * Listener for item click * */ public interface OnPopuItemClickListener { public abstract void onItemClick(PopuJar source, int pos, int actionId); } /** * Listener for window dismiss * */ public interface OnDismissListener { public abstract void onDismiss(); } } public class PopupWindows { protected Context mContext; protected PopupWindow mWindow; protected View mRootView; protected Drawable mBackground = null; protected WindowManager mWindowManager; /** * Constructor. * * @param context Context */ public PopupWindows(Context context) { mContext = context; mWindow = new PopupWindow(context); mWindow.setTouchInterceptor(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { mWindow.dismiss(); return true; } return false; } }); mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } /** * On dismiss */ protected void onDismiss() { } /** * On show */ protected void onShow() { } /** * On pre show */ protected void preShow() { if (mRootView == null) throw new IllegalStateException("setContentView was not called with a view to display."); onShow(); if (mBackground == null) mWindow.setBackgroundDrawable(new BitmapDrawable()); else mWindow.setBackgroundDrawable(mBackground); mWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT); mWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); mWindow.setTouchable(true); mWindow.setFocusable(true); mWindow.setOutsideTouchable(true); mWindow.setContentView(mRootView); } /** * Set background drawable. * * @param background Background drawable */ public void setBackgroundDrawable(Drawable background) { mBackground = background; } /** * Set content view. * * @param root Root view */ public void setContentView(View root) { mRootView = root; mWindow.setContentView(root); } /** * Set content view. * * @param layoutResID Resource id */ public void setContentView(int layoutResID) { LayoutInflater inflator = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); setContentView(inflator.inflate(layoutResID, null)); } /** * Set listener on window dismissed. * * @param listener */ public void setOnDismissListener(PopupWindow.OnDismissListener listener) { mWindow.setOnDismissListener(listener); } /** * Dismiss the popup window. */ public void dismiss() { mWindow.dismiss(); } } 显示popu: 参考: http://code.google.com/p/simple-quickactions/ 本文转自xyz_lmn51CTO博客,原文链接:http://blog.51cto.com/xyzlmn/817276,如需转载请自行联系原作者

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

Android Ap 开发 设计模式第四篇:工厂方法模式

Factory Method Pattern 模板模式的衍生品? 以Template Method Pattern 架构获取产生对象实例的工厂就是Factory Method Pattern。 工厂方法模式在父类规定对象的创建方法,但并没有深入到较具体的类名。所有具体的完整内容 都放在子类。根据这个原则,我们可以大致分成产生对象实例的大纲(框架)和实际生产对象实例的类两方面。 场景模拟 以一个工厂进厂打工为原型,工人们进厂须先登记信息,由所登记的信息进入创建工卡,工人们每天进厂必须使用工卡打卡,开始一天的劳动。以此为例,进行编程,设计UML图如下: 程序实现 抽象类Product定义抽象方法create ()表示创建用工card 信息和use () 表示用户使用card 抽象类Factory实现方法create 的抽象类,这里就是我们上文所说的为什么是模板方法模式衍生品。和定义抽象方法createProduct()、抽象方法registerProduct() 交由子类负责完成 。 类IDcard 继承自Product 实现方法use 和create 类IDCardFactory 继承 自Factory 实现方法createProduct、registerProduct Product 抽象类 public abstract class Product{ public abstract Stringcreate(); public abstract Stringuse(); } Factory 抽象类 public abstract class Factory{ public finalProductcreate(Stringowner){ Productp = createProduct(owner); registerProduct(p); return p; } protected abstract ProductcreateProduct(Stringowner); protected abstract void registerProduct(Productproduct); } IDCard 类 public class IDCardextendsProduct{ private Stringowner; public IDCard(Stringowner){ // TODOAuto-generatedconstructorstub this .owner = owner; } @Override public Stringuse(){ // TODOAuto-generatedmethodstub return " 使用 " + owner + " 的卡 " ; } public StringgetOwner(){ return owner; } @Override public Stringcreate(){ // TODOAuto-generatedmethodstub return " 建立 " + owner + " 的卡 " ; } } IDCardFactory 类 public class IDCardFactoryextendsFactory{ private Vector < String > owners = new Vector < String > (); @Override protected ProductcreateProduct(Stringowner){ // TODOAuto-generatedmethodstub return new IDCard(owner); } @Override protected void registerProduct(Productproduct){ // TODOAuto-generatedmethodstub owners.add(((IDCard)product).getOwner()); } public Vector < String > getOwners(){ return owners; } } 界面代码实现 FatoryMethodActivity: public class FatoryMethodActivityextendsActivityimplementsOnClickListener{ /* *Calledwhentheactivityisfirstcreated. */ @Override public void onCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); ((Button)findViewById(R.id.Button01)).setOnClickListener( this ); } @Override public void onClick(Viewv){ // TODOAuto-generatedmethodstub Factoryfactory = new IDCardFactory(); Productcard1 = factory.create( " terry " ); Productcard2 = factory.create( " paul " ); Productcard3 = factory.create( " jim " ); ((EditText)findViewById(R.id.EditText01)).setText(card1.create() + " , " + card1.use() + " \n " + card2.create() + " , " + card2.use() + " \n " + card3.create() + " , " + card3.use()); } } 最终实现效果: 代码下载: 工厂方法模式 本文转自 terry_龙 51CTO博客,原文链接:http://blog.51cto.com/terryblog/609318,如需转载请自行联系原作者

资源下载

更多资源
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等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册