Android异步加载图片

对于Android中的异步加载图片,自己总结了两种方式,如下:

1.

/*
 * 异步读取图片,需要传递三个参数:Imageview imageView,String imagePath,int maxNumpixels
* @author Bryant
/
public class AsyncLoadImage extends AsyncTask<Object, Object, Void> {
@Override
protected Void doInBackground(Object… params) {

    try {  
        ImageView imageView=(ImageView) params[0];  
        String path=(String) params[1];  
        int maxNumOfPixels = (Integer)params[2];

        Bitmap bm = CompressPicture.compress(path,maxNumOfPixels);

        publishProgress(new Object[] {imageView, bm});  
    }catch (Exception e) {  
        e.printStackTrace();  
    }  

    return null;
}  

protected void onProgressUpdate(Object... progress) {  
    ImageView imageView = (ImageView) progress[0];  
    Bitmap bm = (Bitmap) progress[1];
    if(bm != null){
        imageView.setImageBitmap(bm); 
    }

}  

}

/**
 * 图片异步加载
 * @author Bryant
 *
 */
public class AsyncImageLoader {
    private Map> imageCache=new HashMap>();
    private int maxNumPixels;
    
    public Bitmap loadBitmap(final String imagePath,int maxNumPixels,final ImageCallback callback){
        this.maxNumPixels = maxNumPixels;
        
        if(imageCache.containsKey(imagePath)){
            SoftReference softReference=imageCache.get(imagePath);
            if(softReference.get()!=null){
                return softReference.get();
            }
        }
        final Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                callback.imageLoaded((Bitmap) msg.obj, imagePath);
            }
        };
        new Thread(){
            public void run() {
                Bitmap bitmap=loadImageFromPath(imagePath);
                imageCache.put(imagePath, new SoftReference(bitmap));
                handler.sendMessage(handler.obtainMessage(0,bitmap));
            };
        }.start();
        return null;
    }
    
    protected Bitmap loadImageFromPath(String imagePath) {
        try {
            return CompressPicture.compress(imagePath, maxNumPixels);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public interface ImageCallback{
        public void imageLoaded(Bitmap imageBitmap,String imagePath);
    }
}

经过实际使用,AsyncImageLoader的效率比较高,但是调用比较麻烦。 调用示例:

private AsyncImageLoader imageLoader = new AsyncImageLoader();
imageLoader.loadBitmap(imagePath,maxNumPixels, new ImageCallback() {
    public void imageLoaded(Bitmap imageBitmap, String imagePath) {
        imageView.setImageBitmap(imageBitmap);
    }
}); 

AsyncLoadImage调用较简单,调用示例如下:

new AsyncLoadImage().execute(new Object[]{imageView,imagePath,maxNumPixels});

《七周七语言》读后感

在微博上看到Robbin老大推荐《七周七语言》一书,去豆瓣上查了一下发现大家对这本书的评价也是非常高,于是就想着去读一下。书买了之后由于找工作一直没有来得及看,随着前段时间把工作定了,就着手对这本书的学习了。从书名来看,顾名思义就是讲了七种不同的编程语言。自己浏览了一下目录,心里也大致衡量了一下,对于这七种语言,我觉得我以后会用到的或者说是能去学的应该也就Ruby、Scala两个,所以就计划大体地浏览一下这七门语言,先有个大概的了解就好。这样每天利用上午的两个小时,一共大约两周的时间,大体把这本书看完了。

这本书一共讲了四种编程范型:面向对象编程、原型编程、约束-逻辑编程、函数式编程。面向对象编程有Ruby和Scala;原型编程是Io;约束-逻辑编程是Prolog;函数式编程有Scala、Erlang、Clojure、Haskell。对于这几种编程范型,书里面最后一章进行了很好的总结,在这里也就不重复了。

在阅读这本书的过程中,经常会在看到某个语言的某个特性时让自己有种狂喜的感觉,让自己感到特别兴奋,有一股强烈的冲动去学习它、去运用到实际中。自己在这过程中也产生了一些想法,思考了一些问题。

  • 书的一开始是Ruby那一章。其实几年前就曾听闻Ruby on rails的威名,也知道ItEye就是使用的这个技术,知道Ruby以注重程序员效率为主。之前花了点时间进行了学习,但一直没有一个练手的项目,所以也就停留在似懂非懂的地步。这次看这一章,第一感觉仍然是惊喜于Ruby语法的灵活。写Ruby代码就觉得是在说话一样,而且各种方式都可以,让人非常舒服。另外,Ruby的纯面向对象和动态类型特性都极大地提高了编程效率。对于Ruby在性能、并发方面的弱点,随着现在硬件性能的提高以及Ruby自身的改进,已经越来越不是问题。

  • 对于Io这个语言,自己很陌生,是第一次听说。不过它的原型编程,自己曾在JavaScript中接触过。Io的另一个特色是万事万物皆消息,这个东西确实挺新颖,在处理并发的时候使用actor发送消息、处理消息能达到不错的效果。此外,它的future让我想起了Java中的FutureTask和Future,不知道Java是不是参考Io的。这个机制在多线程环境中也是个很不错的方案。

  • Prolog,也是第一次听说,更是第一次听说声明式编程语言和逻辑编程语言。貌似只要描述一个事实和推论,就能写出解决很多难题的程序来。里面对于数独和八皇后问题的编码解决,让我真的是叹为观止。此外,自己也第一次知道了尾递归优化这个技术,对于递归调用,这个优化的确能够大大节省内存占用。但是真的对于这种语言很不适应,也就没太仔细地看。不过,不得不承认的是DSL语言在特定领域确实是无可比拟的。

  • Scala,一门混合编程语言,算是面向对象语言与函数式语言之间的桥梁。其运行于JVM之上,加上对函数式编程的支持,使得很多基于Java开发的系统可以在之前的基础上进行改进,极大地提高了编程效率。而且其使用val关键字声明值不可变的变量以及其actor使用了线程池的方式都是对并发不错的解决方案。看到这个语言,我强烈地感觉Scala是一个能够取代Java的高级语言,也特别想去学习一下。也有了计划去进一步接触和使用。

  • Erlang,这个语言之前看到过,知道是一个针对并发的编程语言。不过看了书才知道其是基于Prolog而来的。其“就让它崩溃”的错误处理策略显得非常健壮,另外并发方面采用的是开销极小的轻量级进程也是一个优势。最让我惊叹的是它的列表解析功能,一个典型的例子就是能够轻易的向一个列表中加入新的属性。

  • Clojure,又一个陌生的语言,一个JVM上的Lisp实现,也就是一个Lisp方言。对于Lisp自己也是从未接触过。不过从Lisp字面上的意思来看就是一个列表语言,即取列表第一个元素作函数,其余元素做参数。另外就是在Lisp中数据即代码、代码即数据。这里最让我惊异的是其采用事务内存STM和原子来解决并发问题,算是一个亮点。此外,使用延迟计算解决斐波那契数列和阶乘问题以及Clojure-Java互操作对Java语言的扩展也都是很突出的优势。

  • 最后一个语言Haskell是一个纯函数编程语言,也是本书一个让自己看了很久也没搞明白的语言。可能是对于函数式编程自己就是个小白的缘故吧。不过好歹是弄明白了柯里化的意思,也看到了惰性计算的好处。此外,对其中的monad这个为了弥补函数式编程在命令式风格编程方面的缺陷采用的技术,自己看了好几遍还是很迷糊。书上说这是Haskell的精髓,弄懂这个能学到很多东西。自己打算以后有时间再回过头来好好研究一下这个东西。

总之,这本书确实是一本不可多得的好书,值得好好研读一下。强烈推荐!

关于terracotta在tomcat集群中做session共享的问题

手头上的一个项目“陕西省专业技术人员继续教育学习与管理平台”是服务于全陕西省130万专技人员的高并发且事务逻辑较复杂的一个系统,不管是编码上还是系统架构部署上都具有一定的挑战性。在考虑了现有设备以及系统负载的基础上,整个系统架构采用了六台服务器,分别是:WEB门户服务器(WEB门户+Oracle数据库)、学习管理平台服务器、系统核心数据库、资源下载服务器、在线点播服务器、数据库备份服务器。

系统运行了数月,WEB门户采用的apache+mod_jk+memcached+4tomcat能够正常应付目前的访问。管理学习系统虽然由于一些在线课程还未上线,最大的并发还未开始,但目前课程文件的上传、人员的表单录入等对服务器的造成的负载经常使服务器响应缓慢甚至不响应。考虑到开始采用的nginx+4tomcat+ip_hash,ip_hash本身策略的缺陷使负载不能很好地均衡,于是使用memcached做集群的session共享。此时出现了一个到现在还让我搞不清楚的现象:系统session中存入一些timestamp的Date对象,读取的时候,竟然会读成UNIX原始时间戳。一直没办法解决这个问题,只能采用ip_hash的方式运行着,一旦出现服务器不响应的情况就重启tomcat。

这几天上网查资料的时候,发现terracotta可以做tomcat集群的session共享,由于其是jvm级别的cluster解决方案且采用find-gained changes机制,因此在性能要优于memcached,关键是其是jvm堆级别的复制,储存任何值都没任何问题。按照官网的说明把所有东西都配置好了,却发现session无法正常共享。写了个测试的jsp,打印出session id,单独访问某一个tomcat,发现id在不停的换,我一开始怀疑是terracotta没安好的问题,就又重装了一遍,问题依旧。这时突然发现在IE下session是正常的,其他浏览器下session就不正常,总是不停地换,按说没有关闭浏览器是不会重新生成session的。使用fiddler来检测请求和响应的header,发现一个奇怪的问题,chrome请求一个页面时比使用IE时,多了一个对/favicon.ico的请求,而这个请求得到的响应(404)中会产生新的session id使session发生变化。(什么原因,怎么解释?)在网站的根目录下上传一个favicon.ico即可解决此问题。为了证实是favicon.ico不存在造成的问题,我在页面中请求了一个不存在的js文件,结果session id又开始不停地变。把所有不存在的文件的引用都删掉能够解决此问题,但是在没有使用terracotta前根本不存在此问题,我猜想是不是terraccota在检测到404错误的时候,就会重新生成session的缘故啊。查了一下tomcat的文档,发现context有个属性sessionCookiePath,尝试着把这个值设置为了”/“,结果一切正常了,即使再有404错误,session也不会再变了。这是为什么?在文档中对sessionCookiePath的解释是:

The path to be used for all session cookies created for this context. If set, this overrides any path set by the web application. If not set, the value specified by the web application will be used, or the context path used if the web application does not explicitly set one. To configure all web application to use an empty path (this can be useful for portlet specification implementations) set this attribute to / in the global CATALINA_BASE/conf/context.xml file.

不指定此值的时候,是使用的web app中的或者是直接使用context path。我猜测terracotta是接受了此值,但在处理404资源的时候,这个path无法识别,故以为是新的一个会话,造成session改变的缘故。

不管咋样,暂且算是解决了此问题,原因也是猜测的。有空得看一下terracotta的源代码,看看究竟是什么原因造成的。

其实这个项目在部署运行中,出现过各种问题,因此也做过很多架构的变动。到目前还是不能让人满意,只能在后续的维护中继续改进了。

Android中dip、dp、sp、pt和px的区别

Android有以下几个度量单位:

  • dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。

  • px: pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320x480像素,这个用的比较多。

  • pt: point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用;

  • sp: scaled pixels(放大像素). 主要用于字体显示best for textsize。

由此,根据 google 的建议,TextView 的字号最好使用 sp 做单位,而且查看TextView的源码可知Android默认使用sp作为字号单位。

关于换算(以 sp 和 pt 为例) 查看 TextView 等类的源码,可知:

case COMPLEX_UNIT_PX: 
      return value; 
case COMPLEX_UNIT_SP: 
      return value * metrics.scaledDensity; 
case COMPLEX_UNIT_PT: 
      return value * metrics.xdpi * (1.0f/72); 

-------------------------- 
scaledDensity = DENSITY_DEVICE / (float) DENSITY_DEFAULT; 
xdpi = DENSITY_DEVICE; 

-------------------------- 
DENSITY_DEFAULT = DENSITY_MEDIUM = 160; 

============================================ 

所以: 假设 pt 和 sp 取相同的值 1,则可设 1pt 和 1sp 之间系数为 x,

1 * DENSITY_DEVICE / 72 = x * 1 * DENSITY_DEVICE / 160  => 
x = 160 / 72 = 2.2222 

也就是说在 Android 中, 1pt 大概等于 2.22sp

以上供参考,如果 UI 能够以 sp 为单位提供设计是最好的,如果设计中没有 sp 的概念,则开发人员也可以通过适当的换算取近似值。

过去,程序员通常以像素为单位设计计算机用户界面。例如,定义一个宽度为300像素的表单字段,列之间的间距为5个像素,图标大小为16×16像素 等。这样处理的问题在于,如果在一个每英寸点数(dpi)更高的新显示器上运行该程序,则用户界面会显得很小。在有些情况下,用户界面可能会小到难以看清 内容。

与分辨率无关的度量单位可以解决这一问题。Android支持下列所有单位。

  • px(像素):屏幕上的点。

  • in(英寸):长度单位。

  • mm(毫米):长度单位。

  • pt(磅):1/72英寸。

  • dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。

  • dip:与dp相同,多用于android/ophone示例中。

  • sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。

为了使用户界面能够在现在和将来的显示器类型上正常显示,建议大家始终使用sp作为文字大小的单位,将dip作为其他元素的单位。当然,也可以考虑使用矢量图形,而不是用位图。

多activity中退出整个程序

问题

多activity中退出整个程序,例如从A->B->C->D,这时我需要从D直接退出程序。

网上资料

finish()和system(0)都只能退出单个activity。杀进程等的等方式都不行~~~

解决问题

我们知道Android的窗口类提供了历史栈,我们可以通过stack的原理来巧妙的实现,这里我们在D窗口打开A窗口时在Intent中直接加入标志Intent.FLAG_ACTIVITY_CLEAR_TOP,再次开启A时将会清除该进程空间的所有Activity。

在D中使用下面的代码:

Intent intent = new Intent(); 
intent.setClass(D.this, A.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  //注意本行的FLAG设置
startActivity(intent);
finish();//关掉自己

在A中加入代码:

@Override
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
super.onNewIntent(intent);
//退出
        if ((Intent.FLAG_ACTIVITY_CLEAR_TOP & intent.getFlags()) != 0) {
finish();
}
}

A的Manifest.xml配置成android:launchMode=“singleTop”

原理总结

一般A是程序的入口点,从D起一个A的activity,加入标识Intent.FLAG_ACTIVITY_CLEAR_TOP这个过程中会把栈中B,C,都清理掉。因为A是android:launchMode=“singleTop” 不会调用oncreate(),而是响应onNewIntent()这时候判断Intent.FLAG_ACTIVITY_CLEAR_TOP,然后把A finish()掉。 栈中A,B,C,D全部被清理。所以整个程序退出了。

Android中的intent调用代码总结

显示Web网页

Uri uri = Uri.parse("http://www.android123.com.cn");
Intent it = new Intent(Intent.ACTION_VIEW,uri);
startActivity(it);

显示Google地图

Uri uri = Uri.parse("geo:38.899533,-77.036476");
Intent it = new Intent(Intent.Action_VIEW,uri);
startActivity(it);

Maps路径规划

Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en");
Intent it = new Intent(Intent.ACTION_VIEW,URI);
startActivity(it);

拨打电话

Uri uri = Uri.parse("tel:xxxxxx");
Intent it = new Intent(Intent.ACTION_DIAL, uri);   
startActivity(it);  

Uri uri = Uri.parse("tel.xxxxxx");
Intent it =new Intent(Intent.ACTION_CALL,uri);

注意需要权限android.permission.CALL_PHONE

发送SMS/MMS

Intent it = new Intent(Intent.ACTION_VIEW);
it.putExtra("sms_body", "android开发网欢迎您");
it.setType("vnd.android-dir/mms-sms");
startActivity(it);  

发送短信

Uri uri = Uri.parse("smsto:10086");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "10086"); //正文为10086
startActivity(it);  

发送彩信

Uri uri = Uri.parse("content://media/external/images/media/10"); //该Uri根据实际情况修改,external代表外部存储即sdcard
Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra("sms_body", "android123.com.cn"); 
it.putExtra(Intent.EXTRA_STREAM, uri);
it.setType("image/png");
startActivity(it);

发送Email

Uri uri = Uri.parse("mailto:android123@163.com");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(it);

Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_EMAIL, "android123@163.com");
it.putExtra(Intent.EXTRA_TEXT, "android开发网测试");
it.setType("text/plain");
startActivity(Intent.createChooser(it, "选择一个Email客户端"));  

Intent it=new Intent(Intent.ACTION_SEND);   
String[] tos={"android123@163.com"};     //发送到 
String[] ccs={"ophone123@163.com"};    //抄送
it.putExtra(Intent.EXTRA_EMAIL, tos);   
it.putExtra(Intent.EXTRA_CC, ccs);   
it.putExtra(Intent.EXTRA_TEXT, "正文");   
it.putExtra(Intent.EXTRA_SUBJECT, "标题");   
it.setType("message/rfc822");    //编码类型
startActivity(Intent.createChooser(it, "选择一个Email客户端"));

Email添加附件

Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_SUBJECT, "正文");
it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/nobody.mp3"); //附件为sd卡上的nobody MP3文件
sendIntent.setType("audio/mp3");
startActivity(Intent.createChooser(it, "选择一个Email客户端"));

播放多媒体

Intent it = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse("file:///sdcard/nobody.mp3");
it.setDataAndType(uri, "audio/mp3");
startActivity(it);

Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1"); //从系统内部的MediaProvider索引中调用播放
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);  

Uninstall卸载程序

Uri uri = Uri.fromParts("package", packageName, null); //packageName为包名,比如com.android123.apkInstaller
Intent it = new Intent(Intent.ACTION_DELETE, uri);
startActivity(it);

进入联系人界面

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);

查看某个联系人,当然这里是ACTION_VIEW,如果为选择并返回action改为ACTION_PICK,当然处理intent时返回需要用到startActivityforResult

Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, ID);//最后的ID参数为联系人Provider中的数据库BaseID,即哪一行
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);

选择一个图片

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);  
intent.addCategory(Intent.CATEGORY_OPENABLE);  
intent.setType("image/*");//此处->audio/*对应选择音频 video/*对应选择视频
startActivityForResult(intent, 0);

调用Android设备的照相机,并设置拍照后存放位置

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/cwj", android123 + ".jpg"))); //存放位置为sdcard卡上cwj文件夹,文件名为android123.jpg格式
startActivityForResult(intent, 0);

搜索指定package name在market上

Uri uri = Uri.parse("market://search?q=pname:com.android123.cwj");   
Intent intent = new Intent(Intent.ACTION_VIEW, uri);   
startActivity(intent); 

Android位图总结

由于项目中牵扯到了对位图(android.graphics.Bitmap)的操作,于是对照Android的参考文档详细地研究了一下Android提供的位图相关功能。

一、对位图的获取

在Android的SDK中提供了一个BitmapFactory 类。采用此类的几个方法能够从一个文件路径或者输入流中得到位图。

  • 包:android.graphics
  • 类:BitmapFactory
  • Android SDK中的简介:Creates Bitmap objects from various sources, including files, streams, and byte-arrays.

此类其提供了以下方法来获取Bitmap,这些方法都是静态的:

  1. decodeByteArray(byte[] data, int offset, int length)从一个字节数组中得到数据转换为位图。
  2. decodeFile(String pathName)由一个图像文件路径的得到位图(支持jpg、png、bmp格式)
  3. decodeFileDescriptor(FileDescriptor fd)由一个文件描述符得到位图。
  4. decodeResource(Resources res, int id)由资源ID获取位图
  5. decodeStream(InputStream is)由图片输入流获取位图。

这里,由于Android系统本身对apk程序的限制,当加载大图片时会产生OutOfMemoryError。可以采取两种办法解决:

  • 先使用android.provider.MediaStore.Images.Media类的query(ContentResolver cr, Uri uri, String[] projection, String where, String orderBy)方法获得对应的图片所对应的id,然后使用android.provider.MediaStore.Images.Thumbnails类的queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection)获得图片对应的缩略图的路径,再使用BitmapFactory的decodeFile(String pathname)获取到图片的缩略图,这样即可避免内存溢出的发生。
  • 采取压缩图片的方法对图片进行压缩。这里主要还是使用BitmapFactory类,对于上面提到过的能够获取位图的几个方法,都对应着另一个多了一个参数的方法,例如decodeFile(String pathName)对应着decodeFile(String pathName, BitmapFactory.Options opts)这个方法,其他的可查阅参考文档。其中,通过引入opts这个参数,对其进行相关的设置,最后能得到一个被压缩的图片。

二、对位图信息的获取

要获取位图信息,比如位图大小、是否包含透明度、颜色格式等,获取得到Bitmap就迎刃而解了,这些信息在Bitmap的函数中可以轻松获取到。

Android SDK中对Bitmap有详细说明,这里辅助说明以下2点:

  1. 在Bitmap中对RGB颜色格式使用Bitmap.Config定义,仅包括ALPHA_8、ARGB_4444、ARGB_8888、RGB_565,缺少了一些其他的,比如说RGB_555,在开发中可能需要注意这个小问题;
  2. Bitmap还提供了compress()接口来压缩图片,不过Android SDK只支持PNG、JPG格式的压缩;其他格式的需要Android开发人员自己补充了。

三、位图的显示

位图的显示,一般可以在界面中放置ImageView控件,然后调用android.widget.ImageView类的setImageBitmap(Bitmap bm)方法将位图显示出来。另外,可以使用核心类android.graphics.Canvas的drawBirmap()方法显示位图,或者借助于BitmapDrawable来将Bitmap绘制到Canvas。

四、位图的缩放

位图的缩放,在Android SDK中提供了2种方法:

  1. 将一个位图按照需求重画一遍,画后的位图就是我们需要的了,与位图的显示几乎一样:drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
  2. 在原有位图的基础上,缩放原位图,创建一个新的位图: createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) 归结到底,位图缩放的本质就是将原始位图按照需求显示出来,创造一张新的位图。

五、位图的旋转

位图的旋转,离不开Matrix。Matrix在线性代数中都学习过,Android SDK提供了Matrix类,可以通过各种接口来设置矩阵。例子如下“

Matrix matrix = new Matrix();matrix.setRotate(90,120,130);canvas.drawBitmap(mbmpTest, matrix, mPaint);

除了这种方法之外,我们也可以在使用Bitmap提供的函数如下:

public static Bitmap createBitmap (Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)

在原有位图旋转的基础上,创建新位图。

六、位图的截取

对于位图的规则形状如矩形的截取,很容易通过Matrix进行实现。例子如下:

Bitmap croppedImage;
// 取得裁剪的矩形
Rect r = mCrop.getCropRect(); 
if(r == null) return;
int width = r.width();
int height = r.height();
// 生成裁剪的图片
croppedImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(croppedImage);
Rect dstRect = new Rect(0, 0, width, height);
canvas.drawBitmap(mBitmap, r, dstRect, null);

而对于位图的不规则形状的截取,找不到很好的解决办法。曾想过直接对像素进行操作,最终因为算法的的复杂而放弃。一次偶然的机会,在阅读一篇博文时,我发现了使用android.graphics.Canvas的clipPath方法不仅仅可以达到高亮显示路径的目的,也能够达到任意形状截取图片的目的,从而解决了此问题。

以上就是对Android的位图功能进行的总结,以及一些自己实践中得出的想法和结论。更深入的内容需要仔细地去阅读研究Android SDK文档并付之于实践。

Java学习笔记

  1. 代码点与代码单元 codePoint codeUnit 这里当字符串中有辅助字符时,代码点是两个代码单元,需要以下遍历:

     int cp = sentence.codePointAt(i);
     if(Character.isSupplementaryCodePoint(cp)){
          i += 2;
     }else{
          i++;
     }
     

  2. String类对象->不可变字符串 不能修改字符串中的字符:编译器可以将字符串共享,以此带来高效率。

  3. 当将一个字符串与一个非字符串的值进行拼接时,后者将被转换成字符串。
  4. 任何一个对象都可以转换成字符串。
  5. 检测两字符串是否相等,而不区分大小写,可用equalsIgnoreCase()方法
  6. Java中允许一个数组长度为0,不同于null。
  7. Java中的多维数组实际上是数组的数组。
  8. 一个对象变量实际上是一个对象指针。
  9. 用clone()方法获得对象的完整拷贝。
  10. 不能编写返回引用可变对象的访问器方法。
  11. 一个方法可以访问所属类的所有对象的私有数据。
  12. Sytem类中有一个setOut方法是一个本地方法,不是用Java语言实现的,可以绕过Java的存取控制机制(out是final的)。
  13. 每个类都可以有一个main方法,可用来进行单元测试。
  14. 一个方法不能修改一个基本数据; 一个方法可以改变一个对象参数的状态; 一个方法不能让对象参数引用一个新的对象。
  15. 方法签名:方法名、参数类型。
  16. 初始化块机制 首先运行初始化快,然后才运行构造器的主体部分 静态域初始化块:

    static{
    }
    
  17. 只能使用*导入一个包,如:

    import java.util.*;
    
  18. 静态导入:静态方法、静态域

    import static java.lang.System.*;
    
  19. 如果没有指定public或private,那么这个部分(类、方法或者变量)可被同一个包中的所有方法访问

  20. 包作用域:默认情况下是包可见的
  21. 包密封:(package sealing) jar文件密封包
  22. /*…/->文档注释 javadoc 标记+自由格式,其中标记由”@”开始
  23. 类设计技巧:
    • 一定要将数据设为私有;
    • 一定要对数据初始化;
    • 不要在类中使用过多的基本数据类型;
    • 不是所有的域都需要独立的域访问器和域更改器;
    • 使用标准格式进行类的定义;
    • 将职责过多的类进行分解;
    • 类名和方法名要能够体现它们的职责。
  24. Java中的所有继承都是公有继承:超类和子类
  25. 覆盖(Override)子类使用super调用超类的方法以及构造器
  26. 一个对象变量可以引用多种实际类型的现象叫做多态(Polymorphism) 运行能够自动地选择调用的适当方法的现象称为动态绑定
  27. 虚拟机预先为各个类创建了一个方法表
  28. 在覆盖一个方法时,子类方法不能低于超类方法的可见性
  29. 阻止继承的两种方法:final类的方法自动成为final方法(不包括域),final方法
  30. 内联的概念:如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理。
  31. 强制类型转换: 只能在继承层次内进行类型转换; 在将超类转换成子类的前应使用instanceof进行检查
  32. 抽象类可以包含非抽象方法,也可以不包含抽象方法。抽象方法充当着占位的角色,具体实现在子类中。 抽象类不能被实例化 只有子类实现了所有抽象方法,子类才不是抽象的了
  33. 子类不能访问超类的私有域
  34. hashcode: 由对象导出的一个整型值 每个对象都有一个默认的散列码,其值为对象的存储地址
  35. 两个相等的对象要求必须返回一个相等的散列码
  36. 自动打包规范要求boolean byte char=0:x;
  37. 四个基本的抽象流类:InputStream OutputStream Reader Writer(这些对象由其他方法返回)
  38. 注意流过滤器的运用
  39. 对象序列化:ObjectOutputstream ObjectInputStream writeObject() readObject() 类必须实现Serializable 接口
  40. Java使用SHA编码的前8字节作为类的指纹 在序列化的过程中内存地址会被替换为序列号(唯一)
  41. 流类关注的是文件内容,而File类关注的是文件在磁盘上的存储