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. 同步器:
    • CountDownLatch:利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能。
    • CyclicBarrier:字面意思为回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier。
    • Semaphore:信号量,Semaphore可以控制同时访问的线程个数,通过acquire()获取一个许可,如果没有就等待。通过release()释放一个许可。
  38. 四个基本的抽象流类:InputStream OutputStream Reader Writer(这些对象由其他方法返回)
  39. 注意流过滤器的运用
  40. 对象序列化:ObjectOutputstream ObjectInputStream writeObject() readObject() 类必须实现Serializable 接口
  41. Java使用SHA编码的前8字节作为类的指纹 在序列化的过程中内存地址会被替换为序列号(唯一)
  42. 流类关注的是文件内容,而File类关注的是文件在磁盘上的存储