学习 Android 的过程中,碰到很多问题,从问题中也学到了很多。
单位
写一个视频播放器的时候,时间尺度的把握让我想了一段时间,进度条的刷新,字幕的刷新都要注意些。
MediaPlayer.getduration() return int/millisecond
SeekBar.getProgress() return int/scecond
static import
javadoc 的 static import 文档在这里。
为了获取静态成员,必须以所在类修饰引它,例如: double r = Math.cos(Math.PI * theta);
为了避开这个问题,把静态成员写入接口并继承它会是一个坏主意,这个主义的名字叫 Constant Interface Antipattern,问题是一个类使用另外一个类的静态成员只不过是执行上的细节,当一个类实现了一个接口,那么它成了这个类的 public API,而执行细节不应该向 public API 泄漏的。static import 可以免去这一麻烦,只要
import static java.lang.Math.PI; // import static CLASS_NAME.MEMBER_NAME;
或者 // Alternatively
import static java.lang.Math.*; // import static CLASS_NAME.*;
一旦导入了静态成员,就可以不加修饰地引用了。 double r = cos(PI * theta);
最后,你必须谨慎使用 static import ,very sparingly 。 当需要频繁引用时,可以使用该特性;滥用会导致你的程序变得不可读不好维护,污染命名空间,
java断言
如何开启 java 断言,问题英文答案在这里,中文的在下面。
导入 JUnit Assert 类,注意不要导入 Junit4 framework,那个是 org.junit 包,在 Android 设备或模拟器上你必须使用 junit.framework 包
import static junit.framework.Assert.*;
然后尽情使用 assertTrue, assertEquals, assertNull 吧!
开启断言有两种方法:
1. 通过 adb shell setprop debug.assert 1 来设置系统属性 debug.assert
2. 给 Dalvik 虚拟机添加命令行参数 --enable-assert 或简写 -ea
指定的APK打开
如果是自己写的播放器,当然知道名字,直接 startActivity(new Intent(HereActivity.this, ThereActivity.class)); 就行了。如果是用其他人写的 apk,就不清楚名字了,怎么办呢?
如果装了多个应用程序,如播放器 RockPlayer, VPlayer 等等, 但是就是想用 RockPlayer 打开。
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.redirectin.rockplayer.android.unified", "com.redirectin.rockplayer.android.OpenRockPlayerActivity"));
// 包名 类名,解压 apk 文件,利用网上流传的 AXMLPrinter.jar 东西 java -jar AXMLPrinter AndroidManifest.xml,
// 查 AndroidManifest.xml 文件内容,当然这样有点小题大做了。
// APK 和 JAR 文件都是以 ZIP 格式压缩的,AXMLPrinter.jar 的下载在这里,下载后去掉扩展名 .zip 就可以用了。
// 简单一点,可以播放视频,选择 RockPlayer 打开,查看 logcat 信息,然后提取里面字符串,cmp 是 ComponentName。
Uri name = Uri.parse("http://192.168.1.122/video.flv");
intent.setData(name);
startActivity(intent);
I/ActivityManager( 1079): Starting activity: Intent { act=android.intent.action.VIEW dat=file:///mnt/sdcard/video/Crysis2DirectX11UltraUpgradeTrailer_H.264%20AVC_1280x720.flv typ=video/* cmp=android/com.android.internal.app.ResolverActivity }
I/ActivityManager( 1079): Displayed activity android/com.android.internal.app.ResolverActivity: 414 ms (total 414 ms)
I/ActivityManager( 1079): Starting activity: Intent { act=android.intent.action.VIEW dat=file:///mnt/sdcard/video/Crysis2DirectX11UltraUpgradeTrailer_H.264%20AVC_1280x720.flv typ=video/* flg=0x3800000 cmp=com.redirectin.rockplayer.android.unified/com.redirectin.rockplayer.android.OpenRockPlayerActivity }
I/ActivityManager( 1079): Starting activity: Intent { cmp=com.redirectin.rockplayer.android.unified/com.redirectin.rockplayer.android.RockPlayer (has extras) }
D/dalvikvm(10505): Trying to load lib /data/data/com.redirectin.rockplayer.android.unified/lib/librockplayer_8.so 0x4a3cbe90
D/dalvikvm(10505): Shared lib '/data/data/com.redirectin.rockplayer.android.unified/lib/librockplayer_8.so' already loaded in same CL 0x4a3cbe90
W/drawable(10505): Bad element under <shape>: background
D/NativeLibName(10505): plugin .so path /data/data/com.redirectin.rockplayer.android.unified/lib/libffmpeg
D/NativeLibName(10505): ARMv7, idx=8 vfpv3d32=false
D/NativeLibName(10505): degressionLevel=0, path is /data/data/com.redirectin.rockplayer.android.unified/lib/libffmpeg_7n.so
D/RockPlayer(10505): open optimized ffmpeg lib: /data/data/com.redirectin.rockplayer.android.unified/lib/libffmpeg_7n.so
D/RockPlayer(10505): open video height = 720,width=1280
D/RockPlayer(10505): Surface hasn't been initialized! NativeSurfaceView.setSurfaceChanged never got called?
D/RockPlayer(10505): NativeSurfaceView.getVideoHeight: 720
D/RockPlayer(10505): NativeSurfaceView.getVideoWidth: 1280
image/*的处理
Intent intent = new Intent();
Uri uri = Uri.parse("http://www.xxx.com/image/20111126/beautiful.jpg");
intent.setDataAndType(uri, "image/*"); // video/* 或者 audio/* 两种试过可以的。
intent.setAction(android.content.Intent.ACTION_GET_CONTENT);
startActivity(intent);
运行的话会抛出异常,logcat 信息:
ERROR/AndroidRuntime(232): android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.GET_CONTENT dat=http://www.xxx.com/image/20111126/beautiful.jpg typ=image/* }
intent.setDataAndType(uri, "image/*");将其改成intent.setData(uri); 会启动浏览器打开图片。
public boolean onItemLongClick(AdapterView<?> parent, View view,int position,long id)
{
// final int pos=position;
@Override
public void onClick(DialogInterface dialog, int which)
{
// do something here..
// st_color.remove(pos);
}
}
内部匿名类访问外部类变量需要添加 final 属性。
参数添加 final 属性不算函数重载,试着将上面的参数修改了成 final int postion,final long id
发现,运行的结果与预期不一致,表现在 lst_color.remove(position); 删除不掉数据
其中,lst_color 为 ArrayList<String> 型数据。
然后关掉注释,即添加 final int pos = position; lst_color.remove(pos); 成功删除。
ArrayList<T>与T[]
ArrayList<T> 与 T[] 之间的转换,用 get() 方法获取然后对数组赋值显得冗余。
ArrayList<T> lst_color;
T[] color = (T[])lst_color.toArray(new String[lst_color.length());
lst_color = Arrays.asList(color);
查阅 javadoc,ArrayLis t提供 public <T> T[] toArray(T[] a) 方法返回一个按照正确的顺序包含此列表中所有元素的数组;返回数组的运行时类型就是指定数组的运行时类型。如果列表能放入指定的数组,则返回放入此列表元素的数组。否则,将根据指定数组的运行时类型和此列表的大小分配一个新的数组。
如果指定的数组能容纳列表并有剩余空间(即数组的元素比列表的多),那么会将数组中紧跟在集合末尾的元素设置为 null。这对确定列表的长度很有用,但只在调用方知道列表中不包含任何 null 元素时才有用。
asList方法返回一个受指定数组支持的固定大小的列表,此方法同 Collection.toArray 一起,充当了基于数组的 API 与基于 collection 的 API 之间的桥梁。返回的列表是可序列化的,并且实现了 RandomAccess。
Activity 与 Service 数据交互
通过 Intent 来传递数据
Activity 使用 intent.putExtras(KEY,value);
Service 使用 public void onStartCommand(Intent data, int startId, int flags),
public void onStart (Intent intent, int startId) This method is deprecated.
APK 的安装与卸载
// 安装程序的 apk 文件路径
String fileName = Environment.getExternalStorageDirectory() + apkName;
Uri uri = Uri.fromFile(new File(fileName));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri, application/vnd.android.package-archive");
startActivity(intent);
// 卸载 apk,通过程序的包名创建 URI
Uri packageURI = Uri.parse("package: poet.android.app.id");
Intent intent = new Intent(Intent.ACTION_DELETE);
startActivity(intent);
获取屏幕的分辨率
DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
不过在 Ice Cream Sandwich,也就是有 Combined bar(开发者请区分 Status/Navigation/Combined Bar)的时候,获取的值是不包括 Combined bar 的高度的。我这里,1280x720 的值会返回 1280x672,即使用下面一行代码设置隐藏属性也不行。 getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
解决方法是利用了 Java 的反射机制调用方法 getRawHeight(), getRawWidth()
Method mGetRawWidth = Display.class.getMethod("getRawWidth"); Method mGetRawHeight = Display.class.getMethod("getRawHeight"); int width = (Integer) mGetRawWidth.invoke(display); int height = (Integer) mGetRawHeight.invoke(display);
不过在 Android 4.2 也就是 API Level>=17(Build.VERSION_CODES.JELLY_BEAN_MR1) 后有原生的方法 Display.getRealMetrics 供调用,返回正确的结果。
修改控件的尺寸大小
这里在代码中动态修改,而不是 XML 文件中修改。
// 这里使用LayoutInflater。
// LayoutInflater inflater= (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// 下面一行与上一行一个效果
// LayoutInflater inflater=this.getLayoutInflater();
View view_background = this.getLayoutInflater().inflate(R.layout.background,null);
LinearLayout.LayoutParams lp_param = (LinearLayout.LayoutParams)view_background.getLayoutParams();
lp_param.height = (int)(dm.heightPixels*0.618); // 仅修改个别参数,这里修改高度
ll_background.setLayoutParams(lp_param);
// 假如layout/background.xml里面有一个TextView的id是id_result.
// 要去当前的view里面查找获取,这里是view_background
TextView tv_result = (TextView)view_background.findViewById(R.id.id_result);
tv_result.setText(R.string.result);
未完待续。。。