Android开发
添加 ANDROID_SDK_HOME 环境变量
在系统变量中,添加名字为ANDROID_SDK_HOME的环境变量, 变量值为保存Android虚拟机文件位置。
Android应用程序目录结构
gen: 包含BuildConfig.java和R.java 不要修改这两个文件
assets:放资源文件,图片音频视频可以放在这里面 不会在R文件中生成资源的ID
bin:应用程序编译后产生的文件,.dex文件是由dx工具生成的,用来放在虚拟机上运行
lib:第三方的jar文件
res:放资源文件,资源文件的格式有严格定义,所放文件都会对应一个ID。
res/value:放置我们自定义的字符串和数组
string.xml可以用来放置字符串
arrays.xml可以用来放置数组
color.xml可以用来定义颜色
styles.xml可以用来定义样式
AndroidManifest.xml:项目的清单文件,所有应用程序组件都需要在这个文件中注册
project.properties:工程属性配置目录
取得上述string.xml中定义的值
getResources().getString(R.string.app_name);
res中没有给出的三个文件夹
res/anim/ 用来存放定义动画的XML文件
res/xml/ 在activity中使用getResources().getXML()读取该目录下的XML资源文件。
res/raw/ 该目录用于存放应用使用的原始文件,如音频文件等。编译软件时,这些数据不会被编译,他们被直接加入到程序安装包里。为了在程序中使用这些资源,你可以调用getResource().openRawResource(ID),参数ID形式:R.raw.somefilename。
读取/assets目录下的资源文件必须指定文件的路径,如:file:///android_assets/xxx.3gp
Android应用启动流程
创建进程,创建主线程,在主线程中实例化activity(操作系统会把应用有关的信息(Context)存放进activity中),调用onCreate()方法,开始一个activity的生命周期
Android开发技术图
linux kernel :提供操作系统需要的驱动
libraries:常见的一些库,一些工具库
android runtime: 核心库(类似于android sdk)和运行需要的虚拟机
application framework:应用程序框架,如果需要使用什么功能,可以使用这些框架,然后自己细化,比直接调用libraries库自己直接写要容易很多。
Dalvik虚拟机主要特征
1、 专有的dex文件格式。dex是Dalvik虚拟机专用的文件格式,而为什么放弃用已有的字节码文件(.class文件)而采用新的格式呢?原因如下:
1) 每个应用会定义很多类,编译完成后即会有很多相应的class文件,class文件中会有大量冗余信息,而dex文件格式会把所有的class文件内容整合到一个文件中。这样减小了整体文件的尺寸和I/O操作,也提供了类的查找速度
2) 增加了对新的操作码的支持
3) 文件结构尽量简洁,使用等长的指令,借以提高解析速度。
4) 尽量扩大只读结构的大小,借以提高跨进程的数据共享。
2、 dex的优化。dex文件的结构是紧凑的,但是如果还想运行时的性能有进一步提高,就需要对dex文件进一步优化。优化主要针对以下几个方面:
1) 调整所有字段的字节序(LITTLE_ENDIAN)和对齐结构中的每一个域
2) 验证DEX文件中的所有类。
3) 对一些特定的类和方法的操作码进行优化。
3、 基于寄存器。相对于基于堆栈实现的虚拟机,基于寄存器实现的虚拟机虽然在硬件、通用性上要差一些,但是它在代码的执行效率上更胜一筹
4、 一个应用,一个虚拟机实例,一个进程。每一个Android应用都运行在一个Dalvik虚拟机实例中个,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制、内存分配和管理、Mutex等的实现都依赖底层操作系统。所有Android应用的线程都对应一个Linux线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制。不同的应用在不同的进程空间里运行,不同来源的用户都使用不同的Linux用户来运行,可以最大程度地保护应用的安全和独立运行。
Android应用程序框架
android.app:提供高层的程序模型和基本的运行环境。
android.content:包含对各种设备上的数据进行访问和发布。
android.database:通过内容提供者浏览和操作数据库。
android.graphics:底层的图形库,包含画布、颜色过滤、点、矩阵、可以将他们直接绘制到屏幕上。
android:location:定位和相关服务的类。
android.media:提供一些类管理多种音频、视频的媒体接口。
android.net:提供帮助网络访问的类,超过通常的java.net.*接口
android.os:提供了系统服务、消息传输和IPC机制
android.opengl:提供OpenGL的工具
android.provider:提供访问android内容提供者的类。
android.telephony:提供与拨打电话相关的API交互。
android.view:提供基础的用户界面接口框架。
android.util:设计工具性的方法,例如时间日期的操作。
android.webkit:默认浏览器操作接口。
android.widget:包含各种UI元素(大部分是可见的)在应用程序的布局中使用
Log输出的等级
Log.v(String tag,String msg); VERBOSE
Log.d(String tag,String msg); DEBUG
Log.i(String tag,String msg); INFO
Log.w(String tag,String msg); WARN
Log.e(String tag,String msg); ERROR
log.v,log.d只存在于开发过程中,最终版本只可包含Log.i、log.w、log.e
打印log方法
private static final String TAG=”mylog”; //定义log标签
Log.d(TAG,”mylogprint”);
启动一个应用程序的流程
android操作系统—读取—>AndroidManifest.xml文件—读取—>主MainActivity—调用–>onCreate()方法–>读取acitivity_main.xml文件
一个应用程序的组成部分
activity:应用程序界面
service:后台完成一些耗时较长的工作,IO操作,网络连接等,没有界面
Content Provider:向外暴露数据的方法,例如:想访问手机电话本里面的数据,就需要访问电话本的Content Provider方法。是一种数据共享组件
BroadcastRecevier:广播接收器,用来监听系统行为。
上述四种组件都应该放在应用所在的主包或者子包下面
Activity生命周期的7个方法
void onCreate(Bundle savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()
三个状态:
运行状态:activity在屏幕的最前端
暂停状态:activity上有另外的activity,但是并没有被完全覆盖,当前还是对用户可见的。调用onPause();
停止状态:另一个activity打开把当前的activity完全覆盖 调用onPause(),onStop();
开始Activity:在这个阶段依次执行3个生命周期的方法,分别是onCreate()、onStart()和onResume()方法。
Activity由暂停到运行态:调用onResume。
Activity由停止到运行态:如果Activity重新获得焦点,会依次执行3个生命周期方法,分别是onRestart,onStart,onResume。
关闭Activity:当Activity被关闭时系统会依次执行3个生命周期方法,分别是onPause,onStop,onDestory
每个生命周期方法的用途
onCreate():创建activity时调用
onStart(): activity变为在屏幕上对用户可见时调用
onResume():activity开始与用户交互时调用,无论是启动还是重新启动一个活动都会调用
onPause():activity被暂停或收回cpu和其他资源时调用,该方法用于保存活动状态的,也是保护现场,压栈吧
onStop():activity被停止并转为不可见阶段及后续的生命周期事件时调用
onRestart():重新启动activity时调用。该活动仍在栈中,而不是启动新的活动
ondestory():activity被完全从系统中移除时调用。
this.finish(); 关闭当前activity
为activity设置主题,可以定制activity的大小
当应用遇到意外情况(如:内存不足,用户按Home键)由系统销毁一个Activity时,onSaveInstanceState()会被调用,另外Activity由运行状态进入暂停状态或停止状态也会调用该方法。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。 通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适用于数据的持久化保存。
另外,当屏幕的方向发生了改变,Activity会被摧毁并且重新创建,如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建后恢复缓存的数据。可以重写Activity的onSaveInstanceState()和onRestoreInstanceState()方法:如下:
public class MainActivity extends Activity{
private String name;
protected void onRestoreInstanceState(Bundle saveInstanceState){
name = saveInstanceState.getString(“name”); //恢复数据
super.onRestoreInstanceState(saveInstanceState);
}
protected void onSaveInstanceState(Bundle outState){
outState.putString(“name”,”liming”); //缓存数据
super.onSaveInstanceState(outState);
}
}
横竖屏幕切换
默认情况下,当”屏幕方向”或”键盘显示隐藏”变化时都会销毁当前Activity,创建新的Activity,如果不希望重新创建Activity实例,可以按如下配置:
<activity
android:name=”.MainActivity”
android:configChanges=” keyboardHidden|orientation”>
上面的android:configChanges属性指定了要捕获”屏幕方向”和”键盘显示隐藏”变化,当捕获到这些变化后会调用Activity的onConfigurationChange()方法
Activity的四种启动模式(LaunchMode)
<activity android:name=”.MainAcitivity” android:launchMode=”standard”>
standard:这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。
singleTop:如果在任务的栈顶正好存在该activity的实例,就重用该实例(会调用实例的onNewIntent()),否则就会创建新的实例并放入栈顶(注:即使栈中已经存在该activity的实例,只要不在栈顶,都会创建实例)。
singleTask:如果在栈中已经有该activity实例,就会重用该实例(会调用实例的onNewIntent())。重用时会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。
singleInstance:在一个新栈中创建该activity的实例,并让多个应用共享该栈中的该activity实例。一旦该模式的activity的实例已经存在于某个栈中,任何应用再激活该activity时都会重用该栈中的实例(会调用实例的onNewIntent()),,其效果相当于多个应用共享一个应用,不管谁激活该activity都会进入同一个应用中。
Broadcast Intent Receiver
可以使用BraodcastReceiver来让应用对一个外部事件做出响应。例如,当电话呼入这个外部事件来到的时候,可以利用BroadcastReceiver进行处理。例如,当下载一个程序成功完成的时候,仍可以利用BroadcastReceiver进行处理。BroadcastReceiver不能生成UI,也就是说对于用户来说是不透明的,用户是看不到的。BroadcastReceiver通过NotificationManager来通知用户这些事情发生了。BroadcastReceivers既可以在AndroidManifest.xml中注册,也可以在运行时的代码中使用Context.registerReceiver()进行注册。只要是注册了,当事件来临时,即使程序没有启动,系统也在需要的时候启动程序。各种应用还可以通过使用Context.sendBroadcast()将他们自己的intent broadcasts广播给其他应用程序。
短信接收原理
当系统收到短信时,会发出一个广播Intent,Intent的action名称为
android.provider.Telephony.SMS_RECEIVED,该Intent存放了系统接收到的短信内容,我们使用名称”pdus”即可从Intent中获取到短信内容。
在清单文件中配置
<application>
<receiver android:name=”.Myreceiver”>
<intent-filter>
<action android:name=” android.provider.Telephony.SMS_RECEIVED”/>
</intent-filter>
</receiver>
</application>
广播的优先级别
<intent-filter android:priority=1000>
-1000 到1000 值越大,优先级越高
也可以通过intentFilter对象的setPriority()进行设置
有序广播,普通广播
普通广播完全是异步的,可以在同一时刻被所有接收者接收到,相对有序广播消息传递的效率比较高,但是缺点是接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播;然而有序广播是按照接收者声明的优先级别,被接收者依次接收广播,有序广播的接收者可以终止广播的传播,广播一旦终止,后面的接收者就无法收到广播,另外有序广播的接收者可以将数据传递给下一个接收者。
Context.senBroadcast(); //发送普通广播
Context.senOrderedBroadcast(); //发送的是有序广播
使用BroadcastReceiver.abortBroadcast();终止广播的传播
setResultExtras(Bundle) 放入数据,传给下一个接收者
Bundle bundle = getResultExtras(true); //取出放入的数据
发送有序广播
sendOrderedBroadcast(broadcastIntent,
”android.permission.PROCESS_OUTGOING_CALLS”,
new OutgoingCallReceiver(),
null,
Activity.RESULT_OK,
number,
null);
第一个参数为广播对象,
第二个参数为接收该广播的权限
第三个参数为必须接收该广播的接收者,如果该广播不是一定被某个接收者接收,该参数可以设置为null
第四个参数为Handle,如果为null,接收者将在Context所在的主线程被调用
第五个参数为用于标识结果数据的结果码
第六个参数为结果数据
第七个参数为附加到广播的额外数据
广播接收者的响应性
在Android中,每次广播消息到来时都会创建BroadcastReceiver实例并执行
onReceive()方法,onReceive()方法执行完后,BroadcastReceiver的实例就会被销毁,当onReceive()方法在10秒内没有执行完毕,Android会认为该程序无响应。所以在BroadcastReceiver里不能做一些比较耗时的操作,否则会弹出ANR(application No Response)错误对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成。这里不能使用子线程来解决,因为BroadcastReceiver的生命周期很短,子线程可能还没有结束BroadcastReceiver就先结束了。BroadcastReceiver一旦结束,此时BroadcastReceiver所在的进程很容易在系统需要内存时被优先杀死,因此它属于空进程(没有任何活动组件的进程)。如果它所在进程被杀死,那么正在工作的子线程也会被杀死。所以采用子线程来解决是不可靠的
接收短信权限
<uses-permission android:name =”android.permission.RECEIVE_SMS”/>
网络访问权限
<uses-permission android:name =”android.permission.INTERNET”/>
取得短消息,短消息是以对象数组存放的,对象是pdu格式的
Object[] pdus = intent.getExtras().get(“pdus”);
pdu格式是一个byte格式
for(Object p : pdus){
byte[] pdu = (byte[])p; //每个对象中存放的就是一个byte数组
SmsMessage message = SmsMessage.createFromPdu(pdu); //得到短消息对象
String content = message.getMessageBody(); //取得短信的内容
Date data = new Date (message.getTimestampMillis()); //取得短信接收时间
SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
String receiveTime = format.format(data); //格式化后的接收时间
String senderNumber = message.getOriginatingAddress(); //发送者号码
//把数据发送给窃听者,1使用短信发送 2使用web进行发送
}
外拨电话拦截
public void onReceive(Context context,Intent intent){
Strng number = getResultDate(); //获取电话号数据
setResultDate(“12593”+number); 修改数据后放回
}
外拨电话意图
<intent-filter >
<action android:name=”android.intent.action.NEW_OUTGOING_CALL”>
</intent-filter>
外拨电话权限
<uses-permission android:name=”android.permission.PROCESS_OUTGOING_CALLS”>
android4.2强制访问网络的代码
if(android.os.Build.VERSION.SDK_INT > 9){
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
开机启动广播
android.intent.action.BOOT_COMPLETED
Service
实现服务:
1、 继承Service类
public class MyService extends Service{}
2、 在AndroidManifest.xml文件中的<application>
节点里对服务进行配置:
<service android:name=”.MyService”>
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动服务,但是他们的使用场合有所不同。使用startService()方法启动服务,访问者与服务之间没有关联,即使访问者退出了,服务仍然运行。使用bindService()方法启动服务,访问者与服务绑定在了一起,访问者一旦退出,服务也就终止,大有”不求同生,必求同死”的特点。
采用startService()方法启动服务,只能调用Context.stopService()方法结束服务,服务结束会调用onDestory()方法。
//获取电话管理服务
TelephonyManager tm =
(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
//获得电话呼叫状态
tm.listen(new PhoneListener(),PhoneStateListener.LISTEN_CALL_STATE);
private final class PhoneLister extends PhoneStateListener{
public void onCallStateChanged(int state,String incomingNumber){
switch(state){
case TelephonyManager.CALL_STATE_RINGING: //来电状态
case TelephonyManager.CALL_STATE_OFFHOOK: //接通状态
case TelephonyManager.CALL_STATE_IDLE: //挂断后回归空闲状态
}
}
}
音频录制类
MediaRecorder media = new MediaRecorder();
media.setAudioSource(MediaRecorder.AudioSource.MIC); //设置声音采集来源
media.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)//设置输出格式
media.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//设置编码方式
media.setOutputFile(file.getAbsolutePath()); //设置存放文件 file文件路径
media.prepare(); //录音前期准备
media.start(); //开始
media.stop(); //停止
media.release();//释放录音对象
//电话状态的监听权限
<uses-permission android:name=”android.permission.READ_PHONE_STATE”>
//刻录声音的权限
<uses-permission android:name=”android.permission.RECORD_AUDIO”>
建立能与访问者进行相互通信的本地服务(服务和访问者是同一个进程)
startService()访问者不能与service进行数据交互
bindService()访问者可以与service进行数据交互
服务给访问者返回数据
返回的是一个IBinder 对象,IBinder是一个接口,我们需要继承Binder类,这个类已经实现了这个接口,然后返回我们的对象就行了。访问者就可以使用这个对象。
在访问者中调用服务的方法
private StudentManager student ;
private ServiceConnection conn = new StudentServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);
private class StudentServiceConnection implements ServiceConnection{
public void onServiceConnected(ComponentName name, IBinder ibinder) {
student = (StudentManager) ibinder;
}
public void onServiceDisconnected(ComponentName arg0) {
student = null;
}
}
使用AIDL(Android Interface Definition Language)实现进程通信
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。
编写Aidl文件时,需要注意下面几点:
1、 接口名和aidl文件名相同
2、 接口和方法前不用加访问权限修饰符public,private,protected,final,static
3、 Aidl默认支持的类型包括java基本类型(int、long、boolean等) 和(String、List、Map、CharSequence),使用这些类型时不需要import声明。对于List和Map中的元素类型必须是Aidl支持的类型。如果使用自定义类型作为参数或者返回值,自定义类型必须实现Parcelable接口
4、 自定义类型和Aidl生成的其他接口类型在aidl描述文件中,应该显示import,即便在该类型和定义的包在同一个包中。
5、 在aidl文件中所有非java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数
6、 java原始类型默认的标记为in,不能为其他标记
服务的生命周期
1、当采用startService()方法启动服务,与之有关的生命周期方法
onCreate() -> onStart() -> onDestroy()
onCreate()方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onStart() 只有采用startService()方法启动服务时才会回调该方法。多次调用startService()方法尽管不会多次创建服务,但onStart()方法会被多次调用。
onDestroy()该方法在服务被终止时调用
3、 当采用bindService()方法启动服务:
onCreate() -> onBind() -> onUnbind() -> onDestroy()
onBind()只有采用bindService()方法启动服务时才会回调该方法。多次调用bindService()方法并不会导致该方法被多次调用
onUnbind()只有采用bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
音乐播放器
用MediaPlayer类来播放
视频播放器
使用SurfaceView控件来显示视频画面
使用SurfaceView还可以用来画圆,矩形,是用来画图的
用MediaPlayer类来进行播放
SurfaceView sv ;
sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //取得空对象后 设置类型,把输送给sv的视频画面,直接显示到屏幕上,不要维护它自身的缓冲区
sv.getHolder().setFixedSize(400,800);//设置视频的分辨率
sv.getHolder().setKeepScreenOn(true); //保持高亮,不要锁屏
mediaPlayer.setDisplay(sv.getHolder()); //向sv中输出视频
mediaPlayer.prepare(); //缓冲视频
path = new File(filename).getAbsolutePath();
mediaPlayer.reset(); //重置方法
mediaPlayer.setDataSource(path); //设置播放文件路径
//创建缓冲监听对象,缓冲完成调用
mediaPlayer.setOnPrepareLister(new PrepareListener(){
public void onPrepared(MediaPlayer mp){
mediaPlayer.start(); //缓冲完成播放视频
}
});
mediaPlayer.pause(); //暂停
mediaPlayer.seekTo(0); //重播 设置播放点为0
mediaPlayer.stop(); //停止播放
当SurfaceView所在的Activity离开了前台,SurfaceView会被摧毁,当Activity又重新回到前台时,SurfaceView会被重新创建,SurfaceView是在onResume()方法之后被创建。
所以视频播放,当Activity被覆盖又回到前台,会出现黑屏,解决方法是创建SurfaceView的监听
surfaceView.getHolder.addCallBack( new CallBack(){
//三个方法
public void surfaceCreated(SurfaceHolder holder){
//在这里实现播放
}
public void surfaceDestroyed(SurfaceHolder holder){
//在这里停止播放
}
});
使用手机自带的摄像头
<activity
android:screenOrientation=”landscape”> //设置屏幕横屏
设置activity全屏可以通过主题或者编码来实现
使用编码实现activity全屏
requestWindowFeature(Window.FEATURE_NO_TITLE); //设置无标题
getWindow().setFlage(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); //设置窗口全屏
setContentView(R.layout.main); //再设置显示xml
SurfaceView sv;
//把摄像头捕捉到的画面直接输出到SurfaceView上,不维护自身缓冲区
sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//设置分辨率
sv.getHolder().setFixedSize(400,800);
//保持屏幕高亮
sv.getHolder().setKeepScreen(true);
//设置sv创建和摧毁的监听:
sv.getHolder().addCallback(new Callback(){
public void surfaceCreated(SurfaceHolder holder){
//创建时打开摄像头
Camera camera = Camera.open();
//得到摄像头的参数对象
Camera.Parameters params = camera.getParameters();
//不同厂商的摄像头的参数是不同的
//先打印出可以设置的参数
Log.i(TAG,parameters.flatten());
//设置从摄像头中捕获预览照片的大小
params.setPreviewSize(800,480);
//设置每秒从摄像头捕获多少画面
params.setPreviewFrameRate(5);
//照片默认输出格式 默认jpeg
//设置照片大小
params.setPictureSize(1024,768);
//设置照片质量
params.setJpegQuality(80);
camera.setParameters(params);
//上面参数如果不设置也会有默认值
camera.setPreviewDisplay(holder); //把摄像头画面传给surfaceView
camera.startPreview(); //开始预览
}
public void surfaceDestroyed(SurfaceHolder holder){
if(camera != null){
//关闭摄像头
camera.release();
camera = null;
}
}
});
在按钮的监听事件中添加拍照方法
camera.takePicture(null,null,new MyPictureCallback());
//摄像头对焦
camera.autoFocus(null );
//拍完照片的回调类
private final class MyPictureCallback implements PictureCallback{
public void onPictureTaken(byte[] data,Camera camera){
try{
File jpgFile = new File(“Picturena.jpg”);
FileOutputStream out = new FileOutputStream(jpgFile);
out.write(data);
out.close();
//拍完照片后重新预览,要不然照完后画面就不动了
camera.startPreview();
}catch(Exception e){
//
}
}
}
//activity中的触摸屏幕事件
public boolean onTouchEvent(MotionEvent event){
}
摄像头访问权限
<uses-permission android:name=”android.permission.CAMERA”>
视频刻录
使用媒体刻录者类
MediaRecorder mr= new MediaRecorder();
//设置声音来源
mr.setAudioSource(MediaRecorder.AudioSource.MIC);
//指定视频采集来源
mr.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//设置输出格式
mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//设置视频大小
mr.setVideoSize(320,240);
//设置每秒获得画面
mr.setVideoFrameRate(5);
//设置声音和视频编码
mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mr.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
//设置文件输出位置
Fine file = new File(“文件路径”,”文件名”);
mr.setOutputFile(file.getAbsolutePath());
//设置预览输出是否显示
mr.setPreviewDisplay(surfaceView.getHolder().getSurface());
mr.prepare(); //缓冲
mr.start(); //开始录制
mr.stop();//停止刻录
mr.release(); //释放
mr = null;
刻录权限
<uses-permission android:name=”android.permission.RECORD_AUDIO”>
监听器
是一种对象,监控着控件对象的变化,一个控件可以有几个监听器,例如,控件被点击可以让监听器1监听并作出相应反应,当控件被长点击可以通知监听器2
监听器的使用:
1、 获取代表控件的对象
2、 定义一个类,实现监听器接口
3、 生成监听器对象
4、 为控件绑定监听器对象
一个例子:
1、textView = (TextView)findViewById(R.id.textView);
2、 class ButtonListener implements OnClickListener{
@Override
public void onClick(View v) {
count++;
textView.setText(count + "");
}
}
注意:这是一个内部类
3、ButtonListener buttonListener = new ButtonListener();
4、button.setOnClickListener(buttonListener);
OnRatingBarChangeListener 监听器接口
电话拨号器
调用系统自带的拨号activity,传递电话号过去
想要调用系统自带的activity必须实现一个意图过滤器,意图过滤器参数必须匹配系统自带的拨号activity
获得打电话权限
<uses-permission android:name="android.permission.CALL_PHONE"/>
系统自带的拨号activity的意图过滤器源码部分
<intent-filter>
<action android:name = “android.intent.action.CALL”>
<category android:name = “android.intent.category.DEFAULT”>
<data android:scheme =”tel”>
</intent-filter>
代码实现部分
String number = new editText().getText().toString();
Intent intent = new Intent();
intent.setAction(“android.intent.action.CALL”);
intent.setData(Uri.parse(tel:+number));
startActivity(intent);
//方法内部会自动为Intent添加类别:android.intent.category.DEFAULT
短信发送器
获取发送短信权限
<uses-permission android:name="android.permission.SEND_SMS"/>
获取默认短信管理器
SmsManager manager = SmsManager.getDefault();
拆分过长的短信内容
ArrayList<String> texts=manager.divideMassage(String string);
发送短信
for(String text:texts){
manager.sendTextMessage(number,sdAddress,text,sentIntent,deliveryIntent);
短信接收者号码,短信中心地址(默认使用null),短信内容,用于得到发送的状态,得到对方是否收到短信的状态
}
Toast.makeText(context,resId,duration).show();
上下文信息:就是与应用有关的信息,在应用整个范围内都可以访问到,getApplicationContext() 或者是传入当前的主Activiy类对象,例如:MainActivity.this
要传入的文字:
吐丝在屏幕的显示时间:Toast.Length_long;和Toast.Length_short
Android通知方式
单元测试
在实际开发中,开发Android软件的过程需要不断地进行测试。使用Junit测试框架,是正规Android开发的技术,在Junit中可以得到组件,可以模拟发送事件和检测程序处理的正确性。
第一种方法:
第一步首先在AndroidManifest.xml文件中加入
<application>
<uses-library android:name=”android.test.runner”>
</application>
<instrumentation android:name=”android.test.InstrumentationTestRunner”
android:targetPackage=”cn.itacast.action” android:label=”Tests for my App”>
上面targetPackage指定的包要和引用的package相同,当前测试的包名
第二步:编写单元测试代码,选择要测试的方法,右键点击”Run As”->Android Junit Test”
第二种方法:
也可以通过new->project->android test project 来建立单元测试,可以针对某个项目进行测试
数据的存储和访问
Android提供的五种存储方式
1、 文件
2、 SharedPreferences(参数)
3、 SQLite数据库
4、 内容提供者(Content provider)
5、 网络
Context.openFileOutput(filename,mode); 用来获取一个输出流,FileoutputStream
Context.MODE_PRIVATE:为默认操作模式,代表文件是私有的,只能被应用本身访问。在该模式下,写入的内容会覆盖原文件内容。
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和MODE_WORLD_WRITABLE:用来控制其他应用是否有权限读写该文件。注意只可以读或可以写。
权限之间用+号连接
使用上面方法创建的文件会默认保存在/data/data/<package name>/file
目录下
一个例子:
使用应用程序向文件中写入内容
public void save(String filename,String fileContext) throws Exception{
FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(fileContext.getBytes());
fos.close();
}
使用应用程序从文件中读取内容
public String read(String filename) throws Exception{
FileInputStream fis = context.openFileInput(filename);
//定义一个对象,把读到的内容输入到内存
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len = fis.read(buffer)) != -1){
bos.write(buffer, 0, len);
}
fis.close();
bos.close();
return bos.toString();
}
Activity提供的两个实用方法
getCacheDir() 方法用于获取 /data/data/<package name>/cache目录
getFilesDir()方法用于获取/data/data/<package name>/files目录
SD卡中的文件操作
如果文件被保存到SD中,文件可以被任何应用读写
在AndroidManifest.xml中加入访问SDCard的权限:
在SDCard中创建与删除文件权限
<uses-permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
往SDCard写入数据权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
获取SD卡读写状态
Environment.getExternalStorageState()
Environment.MEDIA_MOUNTED 表示SD卡已经加载的宏
获取SD卡路径
Environment.getExternalStorageDirectory();
一个例子:
往SD卡中写入文件
public void saveToSD(String filename,String fileContext) throws Exception{
File file = new File(Environment.getExternalStorageDirectory(),fileContext);
FileOutputStream fos = new FileOutputStream(file);
fos.write(fileContext.getBytes());
fos.close();
}
注意SD卡操作要使用输入输出流
读取xml文件
sax(simple api for xml)
dom
pull解析器
使用pull解析器来读取xml文件
1、 首先得到pull解析器对象
XmlPullParser pullParser = Xml.newPullParser();
2、 为pull解析器设置要解析的xml数据
pullParser.setInput(InputStream xml,inputEncoding);
3、 获取产生的事件,开始文档事件
int event = pullParser.getEventType();
4、 读取下一个节点信息或下一个节点事件
pullParser.next();
5、事件类型
XmlPullParser.START_DOCUMENT; //文档开始
XmlPullParser.START_TAG; //节点标签开始
XmlPullParser.END_TAG; //节点标签结束
XmlPullParser.END_DOCUMENT; //文档结束
6、 获取节点的名称
pullParser.getName();
7、 取得节点的属性值
pullParser.getAttributeValue(0); //第一个属性0,第二个属性1。。。
8、取得节点后面text值
pullParser.nextText();
使用Pull解析器把数据存放到xml中
1、 得到序列化器对象
XmlSerializer serializer = Xml.newSerializer();
2、 设置内容的输出方向
serializer.setOutput(OutputStream os ,String encoding);
3、 输出开始文档和结束文档
serialize.startDocument(“UTF-8”,true);
serialize.endDocument();
4、 输出开始元素节点和结束元素节点
serialize.startTag(null,”persons”);
serialize.startTag(null,”persons”);
5、 输出数据元素集合
for(Person person : persons){
serialize.startTag(null,”person”);
serialize.attribute(null,”id”,person.getId().toString());
serialize.startTag(null,”name”);
serialize.text(person.getName());
serialize.startTag(null,”name”);
serialize.startTag(null,”person”);
}
6、 刷出数据关闭,输出流
out.flush();
out.close();
SharedPreferences(偏好参数保存)
用来保存软件配置参数的
保存参数到xml文件
1、 获得SharedPreferences对象
SharedPreferences preferences = context.getSharedPreferences(“xml文件名不带后缀”,Context.MODE_PRIVATE);
2、 取得编辑器对象
Editor editor = preferences.edit();
3、 放入参数
editor.putString(“name”,name);
editor.putInt(“age”,age);
4、 提交方法,提交参数到文件
editor.commit();
从xml文件获得参数
SharedPreferences preferences = context.getSharedPreferences(“xml文件名不带后缀”,Context.MODE_PRIVATE);
preferences.getString(“name”,””); 第二个参数是当没有值时的默认值
preferences.getInt(“age”,0);
SQLite数据库
1、创建数据库
使用SQLiteOpenHelper类,有两个方法可以用来创建数据库
.getReadableDatebase() 或 .getWritableDatabase()
注:此类是一个抽象类,需要继承才能使用
数据库文件默认会保存在应用所在包的database文件夹
SQLiteDatabase此类中封装了对数据库的操作方法,以及连接数据库,创建此类的对象需要使用上面的两个方法。
事务的使用
SQLiteDatabase db;
开始事务
db.beginTransaction();
设置事务标识为true
db.setTransactionSuccessful();
结束事务
db.endTransaction();
使用Android提供的方法进行增删改查操作
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
ContentValues values = new ContentValues(); //类似于Map
values.put(“name”,person.getName());
values.put(“phone”,person.getPhone());
db.insert(“person(表名),”name”,values);
第二个参数的作用:当第三个参数传入的值为null的时候,实现:
insert into person(name) values(null)
db.delete(“表名”,”删除条件(不需要where)(personId=?)”,”前面?的参数(new String[]{id.toString()})”);
db.update(“表名”,values,”条件”,”条件中?的参数”);
db.query(“table”, //表名
new String[]{“personId”,”name”,”phone”}, //用于组拼需要查询字段的数组,如果传入null会查询所有字段
“personId=?” //查询条件
“new String[]{id.toString()}” //前面?的参数
groupBy, //不要带关键字
having, //不要带关键字
orderBy //不要带关键字
);
Adapter使用
SimpleAdapter
SimpleAdapter adapter = new SimpleAdapter(this, data, R.layout.item, new String[]{"name,phone,count"}, new int[]{R.id.name,R.id.phone,R.id.count} );
SimpleCursorAdapter
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item, cursor, new String[]{"name,phone,count"}, new int[]{R.id.name,R.id.phone,R.id.count} );
自定义adapter一个例子:
package com.example.sqlitehelper;
import java.util.List;
import com.domain.Persion;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class PersionAdapter extends BaseAdapter {
private int source;
private List<Persion> persions;
private LayoutInflater inflater; //布局填充器 作用:可以使用一个xml文件来生成一个view对象
public PersionAdapter(Context context,List<Persion> persions,int source){
this.persions = persions;
this.source = source;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
// TODO Auto-generated method stub
return persions.size();
}
public Object getItem(int position) {
// TODO Auto-generated method stub
return persions.get(position);
}
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
public View getView(int position, View view, ViewGroup parent) {
// TODO Auto-generated method stub
if(view == null){
view = inflater.inflate(source, null);
}
TextView nameView = (TextView)view.findViewById(R.id.name);
TextView phoneView = (TextView)view.findViewById(R.id.phone);
TextView countView = (TextView)view.findViewById(R.id.count);
Persion persion = persions.get(position);
nameView.setText(persion.getName());
phoneView.setText(persion.getPhone());
countView.setText(persion.getCount()+"");
return view;
}
}
上面函数一个性能好一点的实现
public View getView(int position, View view, ViewGroup parent) {
// TODO Auto-generated method stub
TextView nameView;
TextView phoneView;
TextView countView;
if(view == null){
view = inflater.inflate(source, null);
nameView = (TextView)view.findViewById(R.id.name);
phoneView = (TextView)view.findViewById(R.id.phone);
countView = (TextView)view.findViewById(R.id.count);
ViewCache cache = new ViewCache();
cache.nameView = nameView;
cache.phoneView = phoneView;
cache.countView = countView;
view.setTag(cache);
}else{
ViewCache cache = (ViewCache)view.getTag();
nameView = cache.nameView;
phoneView = cache.phoneView;
countView = cache.countView;
}
public final class ViewCache{
public TextView countView;
public TextView phoneView;
public TextView nameView;
}
为listview设置条目的点击事件函数
listview.setOnItemClickListener(OnItemClickListener ol);
一个实现类
public class OnItemClick implements OnItemClickListener{
public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
ListView listview = (ListView)parent;
Persion persion = (Persion)listview.getItemAtPosition(position);
Toast.makeText(getApplicationContext(), persion.getName(), 1).show();
}
}
第一个参数代表当前的listview
第二个参数 view代表当前所点击的view对象
第三个参数position代表当前所点击的view 数据在数据集合中的索引值
Content Provicer
数据在Android中是私有的,这些数据包括文件数据和数据库数据。一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说,一个程序可一次通过实现一个ContentProvider的抽象接口将自己的数据暴露出去。外接根本看不到,也不用看到这个应用暴露的数据在应用中是如何存储的,即用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准的而同一的接口和程序里的数据打交道。可以读取程序的数据,也可以删除程序的数据,当然会涉及一些权限的问题。
Content provider
对外共享数据,统一了数据的访问方式,内容提供者是一种组件,需要在清单文件中完成组件的配置,必须放在应用所在包或它的子包下面
<application>
<provider
android:name=”.PersonProvider” android:authorities=”cn.itcast.provides.persionprovider” />
</application>
类名
唯一标识
Uri代表要操作的数据,Uri主要包含了两部分信息:
1) 需要操作的ContentProvider
2) 对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
content:// cn.itcast.provider.personprovier/person/10
scheme/主机名或authority/路径
scheme为Android固定为content://
如果要把字符串转换成Uri,可以使用Uri类中的parse()方法
Uri uri = Uri.parse(“content:// cn.itcast.provider.personprovier/person/10
”);
向Uri路径中追加ID
Uri uri = ContentUris.withAppendedId(contentUri,id);
Uri判断类,判断Uri是否合法
UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
NO_MATCH: 当URI不匹配时,返回的错误码
添加一个要被匹配的路径
static{
MATCHER.addUri(“content:// cn.itcast.provider.personprovier”,”person”,”成功时的匹配码”);
}
MATCHER.match(uri); 进行匹配,成功返回成功匹配码
访问内容提供者
ContentResolver resolver = this.getContext().getContentResolver();
例如访问内容提供者的insert方法
ContentValues values = new ContentValues();
values.put(“name”,”laoli”);
values.put(“phone”,”3332222”);
values.put(“count”,”500000”);
Uri uri = resolver.insert(uri,values);
得到传入Uri中后面的数字部分
long rowid = ContentUris.parseId(uri);
内容提供者发出数据变化通知
this.getContext().getContentResolver().notifyChange(uri,null);
内容使用者监听数据变化
this.getContentResolver().registerContentObserver(uri,true,new PersonContentObserver(new Handler()));
private class PersonContentObserver extends ContentObserver{
public void onChange(boolean selefChange){
在这个函数中实现内容变化后需要操作的方法;
}
}
监听按钮点击事件的另一个实现方法
<Button
android:onClick=”insert”/>
在对应的activity类中实现方法
public void insert(View view){}
通讯录
reference\android\provider\ContactsContract.RawContacts.html
常用的三张表
raw_contacts
mimetypes
data
通讯录内容提供者的唯一标识
android:authorities = “contacts;com.android.contacts”
提供了两个标识
一个获取通讯录的例子,查询电话本中的联系人名字,电话,电子邮件:
private static final String TAG = "ContactsTest";
public void testContacts() throws Exception{
Uri uri = Uri.parse("content://com.android.contacts/contacts");
ContentResolver resolver = getContext().getContentResolver();
Cursor cursor = resolver.query(uri, new String[]{"_id"}, null, null, null);
while(cursor.moveToNext()){
int contact_id = cursor.getInt(0);
StringBuffer sb = new StringBuffer("contacts=");
sb.append(contact_id);
uri = Uri.parse("content://com.android.contacts/contacts/"+contact_id+"/data");
Cursor cursorData = resolver.query(uri, new String[]{"mimetype","data1","data2"}, null, null, null);
while(cursorData.moveToNext()){
String data = cursorData.getString(cursorData.getColumnIndex("data1"));
String mimetype = cursorData.getString(cursorData.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/name".equals(mimetype)){
sb.append(",name = "+data);
}else if("vnd.android.cursor.item/email_v2".equals(mimetype)){
sb.append(",email = "+data);
}else if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
sb.append(",phone = "+data);
}
}
Log.i(TAG,sb.toString());
}
使用电话号码查询联系人姓名的一个例子:
public void testContactNameByNumber() throws Exception{
String number = "15600005218";
Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/"+number);
ContentResolver resolver = getContext().getContentResolver();
Cursor cursor = resolver.query(uri, new String[]{"display_name"}, null, null, null);
if(cursor.moveToFirst()){
String name = cursor.getString(0);
Log.i(TAG, "name = "+name);
}
cursor.close();
}
一个添加联系人的例子:
public void testAddContact() throws Exception{
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
long contact_id = ContentUris.parseId(resolver.insert(uri, values));
//添加姓名
uri = Uri.parse("content://com.android.contacts/data");
values.put("raw_contact_id", contact_id);
values.put("data2", "范冰冰");
values.put("mimetype", "vnd.android.cursor.item/name");
resolver.insert(uri, values);
//添加电话
values.clear();
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data2", "2");
values.put("data1", "15600000000");
resolver.insert(uri, values);
//添加电子邮件
values.clear();
values.put("raw_contact_id", contact_id);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data2", "2");
values.put("data1", "fanbingbing@126.com");
resolver.insert(uri, values);
}
上面的代码没有使用事务,无法保证一起成功一起失败
一个有事务功能的例子:
public void testAddContact2() throws Exception{
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
ContentResolver resolver = getContext().getContentResolver();
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
ContentProviderOperation op1= ContentProviderOperation.newInsert(uri)
.withValue("account_name",null)
.build();
operations.add(op1);
uri = Uri.parse("content://com.android.contacts/data");
ContentProviderOperation op2 = ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/name")
.withValue("data2", "李小龙")
.build();
operations.add(op2);
ContentProviderOperation op3 = ContentProviderOperation.newInsert(uri)
.withValueBackReference("raw_contact_id", 0)
.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
.withValue("data1", "15600000000")
.withValue("data2", "2")
.build();
operations.add(op3);
resolver.applyBatch("com.android.contacts", operations);
}
访问网络
显示图片
//获取显示图片的imageView
ImageView imageView;
//获取图片的二进制数据
byte[] data;
//根据图片的二进制数据获取图片的bitmap格式
//后面两个数据是获取图片的二进制数据长度
Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
//显示图片
imageView.setImageBitmap(bitmap);
把浏览器的路径转化为url对象
URL url = new URL(“路径的字符串”);
使用url得到基于http协议的连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
设置基本属性
conn .setConnectionTimeOut(5000); //设置超时时间5秒
conn .setRequestMethod(“GET”); //设置数据请求方式为get
获取网络数据的输入流
InputStream in = conn.getInputStream();
获取服务器的响应码,用于确认是否获得正确数据
conn.getResponseCode()
获取联网权限
<uses-permission android:name="android.permission.INTERNET"/>
使TextView可以上下拖动
<ScrollView height=”” width=””>
<TextView />
</ScrollView>
只要在外面加一层ScrollView就可以了
JSON数据格式
[{id:1,title:”美羊羊与灰太狼”,time:10},{id:2,title:”喜羊羊与灰太狼”,time:20},{id:3,title:”大灰狼与灰太狼”,time:30}]
android解析json要比解析xml文件快
json使用的一个例子:
private static List<News> parseJson(InputStream inStream) throws Exception {
String json = new String(StreamTool.read(inStream));
List<News> news = new ArrayList<News>();
JSONArray jsonarray = new JSONArray(json);
for(int i=0;i<jsonarray.length();i++){
JSONObject jsonObject = (JSONObject) jsonarray.get(i);
news.add(new News(jsonObject.getInt("id"), jsonObject.getString("title"), jsonObject.getInt("time")));
}
return news;
}
使用增强for循环迭代list集合
List<News> data = new ArrayList<News>();
for(News news :data){
}
使用增强for循环迭代Map集合
Map<String,String> params = new HashMap<String,String>();
for(Map.Entry<String,String> entry : params.entrySet()) {
}
手机客户端提交给tomcat服务器中文产生乱码
1. 在提交参数时,没有对中文进行url编码
2. tomcat服务器默认采用的是ISO8859-1编码得到的参数值
方法一
1、URLEncoder.encode(“String”,”ecoding”); 客户端
2、title = new String(title.getBytes(“ISO8859-1”),”UTF-8”); 服务器
方法二
使用filter过滤器
每一次请求到来的时候都会经过
doFilter()方法
使用post方法向服务器发送数据
第一种方法:
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("POST");
conn.setDoOutput(true); //设置允许对外输出数据
conn.setRequestProperty(“Content-Type”,”application/x-www-form-urlencoded”);
conn.setRequestProperty(“Content-Length”,String.valueOf(neirong.length));
OutputStream outStream = conn.getOutputStream();
outStream.write(neirong); //把数据写入内部缓存中
if(conn.getResponseCode == 200){ //把数据发送给服务器
}
注意:只有当真正请求http协议返回的某个数据的时候,数据才真正发送给服务器,例如上面的请求就收码
第二种方法:使用httpClient
使用NameValuePair对来存放请求参数
List<NameValuePair> pairs = new ArrayList<NameValuePair>();
NameValuePair pair = new BasicNameValuePair(String,value);
构造要传送的数据串 // title=xxx&timeLength=90
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pair,”UTF-8”);
path = “http://192.168.2.103:9090/Web/MangerServlet”;
HttpPost http = new httpPost(path);
httpPost.setEntity(entity);
DefaultHttpClient client = new DefaultHttpClient();
HttpResponse res = client.execute(httpPost); // 发送请求
if(response.getStatusLine().getStatusCode() == 200){
return true;
}
HTTP/1.1 200 OK //状态行
文件上传
通过HTTP协议实现文件上传
<form method=”post” action=http://192.168.1.10:8080/video/manage.do
enctype=”multipart/form-data”>
<input type=”text” name =”name”/>
<input type=”file” name=”imagefile/>”
</form>
发送xml数据到服务器的一个例子
public void testSendXml() throws Exception{
InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("persons.xml");
byte[] data = StreamTool.read(inStream);
String path = "http://192.168.2.103:9090/Web/XmlServlet";
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length",String.valueOf(data.length));
OutputStream out = conn.getOutputStream();
out.write(data);
if(200 == conn.getResponseCode() ){
System.out.println("发送成功");
}
}
webservice服务
网址 www.webxml.com.cn
多线程下载
多线程下载原理
1) 首先获取网络文件的长度,然后在Android客户端中生成一个与网络文件长度相等的本地文件
2) 开启N条线程下载文件,计算每条线程负责下载的数据量,公式如下:
int block = 文件长度%N ==0 ? 文件长度/N : 文件长度/N+1
3) 开启多条线程分别从网络文件的不同位置下载数据,并从本地文件相同的位置写入数据,要计算出每条线程从网络文件的什么位置开始下载数据,到什么位置结束。
获取网络文件与在本地创建与网络文件相同大小的临时文件
一个从网络下载数据的例子
package com.test.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class ThreadDownload {
public static void main(String[] args) {
String path = "http://192.168.2.103:9090/Web/QQWubiSetup.exe";
int threadCount = 3;
new ThreadDownload().download(path,threadCount);
}
public void download(String path,int threadCount){
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if(200 == conn.getResponseCode()){
int length = conn.getContentLength(); //获取网络文件的长度
File file = new File(getName(path)); //建立本地文件
RandomAccessFile accessFile = new RandomAccessFile(file,"rwd"); //建立操作本地文件对象
accessFile.setLength(length);
accessFile.close();
//建立每条线程负责下载的数据量
int block = length % threadCount == 0 ? length / threadCount : length / threadCount + 1;
for(int threadid = 0 ; threadid < threadCount ; threadid++){
new DownloadThread(threadid,url,block,file).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public class DownloadThread extends Thread{
private int threadid;
private URL url;
private int block;
private File file;
public DownloadThread(int threadid,URL url,int block,File file){
this.threadid = threadid;
this.url = url;
this.block = block;
this.file = file;
}
public void run(){
int startposition = threadid * block;//从网络文件的什么位置开始下载数据
int endposition = (threadid+1) * block - 1;//下载到网络文件的什么位置结束
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
RandomAccessFile handler = new RandomAccessFile(file,"rwd");
handler.seek(startposition);
conn.setRequestProperty("Range", "bytes="+ startposition+ "-"+ endposition); //指定从网络下载数据的区域
InputStream in = conn.getInputStream();
byte[] data = new byte[1024];
int len = 0;
while((len = in.read(data)) != -1){
handler.write(data, 0, len);
}
handler.close();
in.close();
System.out.println("第"+ (threadid+1)+ "线程下载完成");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String getName(String path){
return path.substring(path.lastIndexOf("/")+1);
}
}
多线程断点下载
UI控件的画面重绘是由主线程负责处理的,如果在子线程中更新UI控件的值,更新的值不会重绘到屏幕上
解决方法:使用Handler
handler的作用是用于往创建hander对象所在的线程所绑定的消息队列发送消息
主线程中操作
private Handler hanler = new Handler();
private final class UIHandler extends Handler{
public void handleMessage(Message msg){
int size = msg.getData().getInt(“size”);
progressBar.setProgress(size);
}
}
//子线程中操作
Message msg = new Message(); //或者 handler.botainMessage(1);来创建消息对象
msg.getDate().putInt(“size”,size); //取得用于存放数据的Bunder对象同时放入数据
handler.sendMessage(msg); //和主线程同一个handler
在主线程中有消息处理器负责处理接收的msg
定义消息ID
msg.what = 1;
为应用添加多个Activity
Intent 意图过滤器,可以激活组件,附带参数
实现activity中的跳转
方法一:
Intent intent = new Intent();
intent.setClass(this,targatActivity.class);
startActivity(intent);
方法二:
intent.setClassName(this,”cn.itcast.activitys.targatActivity”);
startActivity(intent);
方法三:
intent.setComponent(new ComponentName(this,targatActivity.class));
startActivity(intent);
方法四:
Intent intent = new Intent(this,targatActivity.class);
startActivity(intent);
方法五:
跨应用激活组件
intent.setClassName(“com.test.mainActivity”,”cn.itcast.activitys.targatActivity”);
startActivity(intent);
使用参数传递
方法一:
intent.putExtra(“company”,”传智播客”);
intent.putExtra(“company1”,”传智播客1”);
方法二:
Bundle bundle = new bundle(); //相当于Map
bundle.putString(“company”,”传智播客”);
bundle.putExtra(“company1”,”传智播客1”);
intent.putExtras(bundle); //批量添加
在其他activity中接收参数
方法一:
Intent intent = getIntent(); //这个意图对象就是调用本activity的intent
String company = intent.getStringExtra(“company”);
方法二:
Bundle bundle = intent.getExtras();
String company = bundle.getString(“company1”);
在本activity中得到打开的activity返回的数据
startActivityForResult(intent, int code); //code 自己设定
在本类中获取数据需要重写本activity的一个方法
protected void onActivityResult(int requestCode,int resultCode,Intent data){
//data 为返回的数据
}
在打开的activity中返回数据
Intent intent = new Intent();
intent.putExtra(“company”,”传智播客”);
setResult(resultCode,intent); //返回数据结果码resultCode 给调用它的activity
finish()关闭当前activity
Intent意图过滤器
使用Intent 实现activity的跳转
Intent in = new Intent(A.this,B.class);
startActivity(in);
如果说Intent是一个有效请求,一个Intent Filters则用于描述一个Activity能够操作哪些Intent。一个Activity如果要显示一个人的联系方式时,需要声明一个Intent Fileters,这个Intent Filters要知道怎么去处理VIEW动作和表示一个人的URI。Intent Filter一般在AndroidManifest.xml中定义。
Android基本的设计理念是鼓励减少组件间的耦合,因此Android提供了intent,intent提供了一种通用的消息系统,它允许在你的应用程序与其他的应用程序间传递intent来执行动作和产生事件。使用intent可以激活android应用三种类型的核心组件:活动、服务、广播接收者。
Intent可以划分成显式意图和隐式意图。
显式意图:调用intent.setComponent() 或intent.setClassName()或intent.setClass()方法明确指定了组件名的intent为显式意图,显式意图明确指定了要激活的组件是哪个组件。
隐式意图:没有明确指定组件名的intent为隐式意图。android系统会根据隐式意图中设置的动作(action)、类别(category)、数据(URL和数据类型)找到最合适的组件来处理这个意图。
一个隐式例子
系统自带的拨号activity的意图过滤器源码部分
<intent-filter>
<action android:name = “android.intent.action.CALL”>
<category android:name = “android.intent.category.DEFAULT”>
<data android:scheme =”tel”>
</intent-filter>
代码实现部分
String number = new editText().getText().toString();
Intent intent = new Intent();
intent.setAction(“android.intent.action.CALL”);
intent.setData(Uri.parse(http://www.baidu.com)); //Uri.parse(); 把一个字符串转换成uri格式
intent.addCategory(“类别”);//添加类别
intent.setType(“image/jpg”); //设置图像类型,但是会清除前面设置的setData()
intent.setDataAndType(“数据”,”类别”); 使用这个方法解决上面的问题
startActivity(intent); //方法内部会自动为Intent添加类别:android.intent.category.DEFAULT
只要意图中的action和category都出现在intent-filter中,就能与之匹配,否则,匹配不成功(前提是没设置数据参数的情况下)。
<intent-filter>
<data android:scheme=”http” android:host=www.baidu.com/>
</intent-filter>
data数据格式是uri格式 如:http://www.baidu.com
<intent-filter>
<data android:mimeType=”image/*” />
</intent-filter>
定义数据类型 例如上面的匹配所有的图像文件
文字国际化
只需要创建不同的values文件夹就可以
values-zh中文文件夹
values-en英文文件夹
values-en-rUS 美国英文文件夹
图片国际化
只需要创建不同的文件夹就可以
drawable-en 英文图片
drawable-en-hdpi 高分辨率图片
drawable-zh 中文图片
屏幕适配
根据不同的屏幕大小显示不同的屏幕界面
layout-480x320 建立不同的layout文件夹
layout-320x240
样式
android中的样式和CSS样式作用相似,都是用于为界面元素定义显示风格,它是一个包含一个或者多个view控件属性的集合。如:定义字体的颜色和大小。
在CSS中是这样定义的:
<style>
.itcast{COLOR : #0000CC;font-size:18px;}
</style>
<div class=”itcast”>内容</div>
在Android中可以这样定义样式:
在res/values/styles.xml文件中添加以下内容
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<style name=”itcast”> <!—为样式定义一个全局唯一的名字-->
<item name=”android:textSize”> 18px</item>
<item name=”android:textColor”> #0000CC</item>
</style>
<style name=”itcast2” parent=”itcast”> <!—继承样式-->
<item name=”android:layout_width”> fill_parent</item>
<item name=”android:layout_height”> fill_parent</item>
</style>
<style name=” itcast.itcast3” > <!—也可以这样继承样式-->
<item name=”android:layout_width”> fill_parent</item>
<item name=”android:layout_height”> fill_parent</item>
</style>
</resources>
在layout文件中可以像下面这样使用
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout >
<TextView style=”@style/itcast”>
</LinearLayout>
主题
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<style name=”itcast”> <!—为样式定义一个全局唯一的名字-->
<item name=”android:textSize”> 18px</item>
<item name=”android:textColor”> #0000CC</item>
</style>
<style name=”itcast2” parent=”@style/itcast”> <!—继承样式-->
<item name=”android:layout_width”> fill_parent</item>
<item name=”android:layout_height”> fill_parent</item>
</style>
<style name=”itcast3”> <!—定义主题-->
<item name=”android:windowNoTitle”>true</item> //没标题
<item name=”android:windowFullscreen”>?android:windowNoTitle</item>
</style>
</resources>
上面”?android:windowNoTitle”z中的问号用于引用在当前主题中定义过的资源的值。
在AndroiManifest.xml中设置主题
<application android:icon=”” android:label=””
android:theme= “@style/ itcast3”>
除了可以在AndroidManifest.xml中设置主题,同样也可以在代码中设置主题,如下:
setTheme(R.style.itcast3);
尽管在定义上,样式和主题基本相同,但是它们使用的地方不同。样式用在单独的View,如:EditText、TextView等;主题通过AndroidManifest.xml中的<application>和<activity>
用在整个应用或者某个activity,主题对整个应用或某个activity进行全局影响。如果一个应用使用了主题,同时应用下的view也使用了样式,那么当主题和样式属性发生冲突时,样式优先级高于主题。
另外android系统也定义了一些主题,
例如:<activity android:theme=”@android:style/Theme.Dialog”>
,该主题可以让activity看起来像一个对话框,还有透明主题:@android:style/Theme.Translucent.
如果需要查阅这些主题,可以在文档的reference->androi->R.style中查看。
编码实现软件界面
setContentView(View view);
//布局参数
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, //定义布局宽度
ViewGroup.LayoutParams.MATCH_PARENT); //定义布局高度
LinearLayout layout = new LinearLayout(this);
TextView text = new TextView(this);
layout.addView(text,params); //添加布局控件同时指定宽高
xml布局文件制作成view对象
LayoutInflater inflater
= (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.part,null);
发送状态栏通知
通知对象
Notification nf = new Notification(icon,Text,when);
1、 通知图标
2、通知的概要
3、通知时间
int icon = android.R.drawable.stat_noftify_chat
String Text = ”hello”;
when = System.currentTimeMillis(); //当前时间
Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse(“tel:110”));
PendingIntent
pendingintent = PendingIntent.getActivity(this,requestCode(随便给定),intent(意图对象),0);
//通知的内容
nf .setLatestEventInfo(this,title,content,pendingIntent);
2、 通知标题
3、 内容
4、 指定通知点击后激活的组件
//设置通知声音
nf.defaults = nf.DEFAULT_SOUND; //设置默认声音
nf.flags = nf.FLAG_AUTO_CANCEL; //点击通知后取消通知
//取得通知服务
NotificationManager manager =( NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
//发送通知
manager.notify(id(为通知定义ID),nf);
使用网页设计软件界面
制作一张网页放在assets文件夹下面
浏览器显示控件
<WebView>
//加载网页到webview
webView.loadUrl(“file:///android_asset/index.html”);
//设置允许执行js代码
webView.getSettings().setJavaScriptEnabled(true);
//向浏览器添加自定义的js对象
webView.addJavascriptInterface(new JSObject(),”objname”);
private final class JSObject{
public void showcontacts(){
webView.loadUrl(“javascript:show(“params”);”); //调用js方法
}
}
用来激活打电话的Intent
Intent intent = new Intent(Intent.ACTION_CALL,Uri.parse(tel:+phone));
startActivity(intent);
也可以将网页放在互联网上
实现动画
android提供了两种动画
1、 Tween动画 通过对View的内容进行一系列的图形变换(包括平移、缩放、改变透明度)来实现动画效果,动画效果的定义可以采用xml来做也可以采用编码来做。
Tween动画有四种类型 在res下面建立文件夹anim自定义不同的xml文件
渐变透明度动画效果 配置节点 AlphaAnimation实现类
Xml文件配置
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<alpha
android:fromAlpha="1"
android:toAlpha="0"
android:duration="5000"/>
</set>
渐变尺寸缩放画效果<scale/>
配置节点 ScaleAnimation实现类
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="5.0" //放大缩小的倍数
android:toYScale="5.0"
android:duration="5000"/>
</set>
画面位置移动动画效果 <translate/>
配置节点 TranslateAnimation实现类
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="200"
android:toYDelta="200"
android:duration="5000"/>
</set>
画面旋转动画效果 <rotate/>
配置节点 RotateAnimation实现类
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%" //旋转中心点以自身为中心
android:pivotY="50%"
android:duration="5000"/>
</set>
Java文件
public class MainActivity extends Activity {
ImageView imageview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageview = (ImageView) findViewById(R.id.imageview);
//Animation animation = AnimationUtils.loadAnimation(this, R.anim.alpha);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.translate);
imageview.startAnimation(animation);
animation.setFillAfter(true);
}
}
不使用xml文件用代码来完成旋转动画效果
public class MainActivity extends Activity {
ImageView imageview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageview = (ImageView) findViewById(R.id.imageview);
Animation animation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animation.setDuration(5000);
imageview.startAnimation(animation);
animation.setFillAfter(true);
}
}
2、 Frame动画,即顺序播放事先做好的图像,跟放胶片电影类似,开发步骤
1) 把准备好的图片放进项目res/drawable下
2) 在项目的res目录下创建文件夹anim,然后在anim文件夹下面定义动画xml文件,文件名称可以自定义。当然也可以采用编码方式定义动画效果(AnimationDrawable类)。
3) 为View控件绑定动画效果。调用代表动画得AnimationDrawable的start()方法开始动画。
ImageView imageView;
//使用我们自定义的文件生成动画效果对象
Animation animation = AnimationUtils.locaAnimation(this,R.anim.alpha(动画文件));
imageView.startAnimation(animation);//启动动画效果
//动画结束时候的状态
animation.setFillAfter(true); //设置true会停留在动画结束点位置
//旋转动画效果需要使用到的类
Animation animation = new RotateAnimation(,,,,);
一个frame动画的例子:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView textView = (TextView) this.findViewById(R.id.textView);
textView.setBackgroundResource(R.drawable.frame);//绑定Frame动画图形
final AnimationDrawable drawable =
(AnimationDrawable) textView.getBackground();
getMainLooper().myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
public boolean queueIdle() {
drawable.start();//启动动画
return false;
}
});
}
}
Activity切换动画
Intent intent = new Intent(this,OtherAcitvity.class);
startActivity(intent);
//定义切换效果
this.overridePendingTransition(
(进入activity动画文件),
(退出activit动画文件)
);
界面切换动画
<ViewFlipper > 该控件主要用于实现界面切换
<LinearLayout>
第一页
</LinearLayout>
<LinearLayout>
第二页
</LinearLayout>
</ViewFlipper>
//实现页面切换,默认显示第一页
ViewFlipper viewFlipper;
Animation in = AnimationUtils.locaAnimation(this,R.anim.in(进入动画文件));
Animation out = AnimationUtils.locaAnimation(this,R.anim.out(出去动画文件));
viewFlipper.setInAnimation(); //设置进入动画
viewFlipper.setOutAnimation(); //设置退出动画
viewFlipper.showNext(); //显示下一页
viewFlipper.showPrevious(); //显示上一页
使用手机自带的传感器
传感器类型:方向、重力、光线、磁场、距离、温度等
方向:Sensor.TYPE_ORIENTATION
重力:Sensor.TYPE_ACCELEROMETER
光线:Sensor.TYPE_LIGHT
磁场:Sensor.TYPE_MAGNETIC_FIELD
距离:Sensor.TYPE_PROXIMITY
温度:Sensor.TYPE_TEMPERATURE
//获取某种类型的传感器
Sensor sensor =sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
//注册监听,获取传感器变化值
sensorManager.registerLister(listener,sensor,SensorManager.SENSOR_DELAY_GAME);
上面第三个参数为采样率:最快、游戏、普通、用户界面。当应用程序请求特定的采样率时,其实只是对传感器子系统的一个建议,不保证特定的采样率可用。
最快:SensorManager.SENSOR_DELAY_FASTEST
最低延迟,一般不是特别敏感的处理不推荐使用,该中模式可能造成手机电量大量消耗,由于传递的为原始数据,算法不处理将会影响游戏逻辑和UI的性能。
游戏:SensorManager.SENSOR_DELAY_GAME
游戏延迟,一般绝大多数的实时性较高的游戏都使用该级别。
普通:SensorManager.SENSOR_DELAY_NORMAL
标准延迟,对于一般的益智类或EASY级别的游戏可以使用,但过低的采样率可能对一些赛车类游戏有跳帧现象。
用户界面:SensorManager.SENSOR_DELAY_UI
一般对于屏幕方向自动旋转使用,相对节省电能和逻辑处理,一般游戏开发中我们不使用。
//得到传感器管理对象
SensorManager manager=
( SensorManager )getSystemService(Context.SENSOR_SERVICE);
//得到传感器
Sensor sensor =manager.getDefaultSensor(Sensor.TYPE_LIGHT);
manager.registerLister(listener,sensor,SensorManager.SENSOR_DELAY_GAME);
SensorListener listener = new SensorListener();
public final class SensorListerner extentds SensorEventListener{
public void onSensorChanged(SensorEvent event){
float degree = event.values[0]; //存放了方向值
}
}
//取消注册
manager.unregisterListener(listener);
手势识别
1、 建立手势库
使用SDK自带例子GestureBuilder建立手势库
(位置:android-sdk-windows\samples\android-8\GestureBuilder)。
使用GestureBuilder之前,你需要恢复其到开发环境,然后进行编译并部署到手机上。此时,就可以使用GestureBuilder建立手势库,生成的手势库文件在SDCard上,默认文件名称为:gestures
2、 在应用中添加手势库文件,然后开发手势识别代码。
把手势库文件gestures文件拷贝到项目的res/raw目录下。然后在布局文件中添加用于手势绘制的View:
<android.gesture.GestureOverlayView
android:id=”@+id/gestures”
android:layout_width=”fill_parent”
android:layout_weight=”1.0”>
为View添加手势监听事件(只针对单笔手势):
GestureOverlayView overlayView;
overlayView.gestureOverlayView.addOnGesturePerformedListener(listener);
支持多笔手势的监听事件
overlayView.addOnGestureListener(listener);
得到手势库:
GestureLibrary mLibrary=GestureLibraries.fromRawResource(this,R.raw.gestures);
加载手势库:mLibrary.load();
//从手势库中查询匹配的内容,匹配的结果可能包括多个相似的内容,匹配度高//的结果放在最前面
List<Prediction> predictions = mLibrary.recognize(gesture);
大多数情况下,手势都是通过一笔完成。然而有一些特别的需求就需要通过多个笔画来实现,这是可以使用gestureStrokeType属性进行设置:
android:gestureStrokeType = “multiple”
overlayView.clear(true); //清除手势
当应用不再使用时,通常需要关闭应用,可以使用以下三种方法关闭android应用:
第一种方法:首先获取当前进程的id,然后杀死该进程(建议使用)。
android.os.Process.killProcess(android.os.Process.myPid());
第二种方法:终止当前正在运行的java虚拟机,导致程序终止
System.exit(0);
第三种方法:强制关闭与该包有关联的一切执行
ActivityManager manager =
(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
manager.restartPackage(getPackageName());
<users-permission android:name=”android.permission.RESTART_PACKAGES”>
拖拉功能与多点触摸
ImageView imageView;
//设置触摸监听事件
imageView.setOnTouchListener(
new OnTouchListener(){
//定义一个按下点
private PointF startPoint = new PointF();
//矩阵对象
private Matrix matrix = new Matrix();
//存放照片当前位置
private Matrix current = new Matrix();
public boolean onTouch(View v,MotionEvent event){
//v 当前触摸的空间,event触摸事件
//只有低8为存放的才是事件
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN: //手指按下
current.set(imageView.getImageMatrix());//记录当前位置
startPoint.set(event.getX(),event.getY()); //记录开始坐标
case MotionEvent.ACTION_MOVE: //手指移动,事件不断出发
float dx = event.getX() – startPoint.x; //得到在x轴的移动距离
float dy = event.getY() – startPoint.y; //得到在y轴的移动距离
matrix.set(current);// 在上一次的移动位置基础上移动
matrix.postTranslate(dx,dy); //实现位置移动
case MotionEvent.ACTION_UP: //手指离开
case MotionEvent.ACTION_POINTER_DOWN://当屏幕已经有触点,再有一个手指按下屏幕
case MotionEvent.ACTION_POINTER_UP://有触电离开,但屏幕还有触电
}
imageView.setImageMatrix(matrix); //可以移动位置了
}
return true;
});
martix.postScale(scale,scalse,px,py);
x轴缩放倍数,y轴缩放倍数,缩放中心点坐标x,缩放中心点坐标y
图形组件
NinePatch图片
9Path是一种PNG图片格式,它可以在特定区域随文字大小进行缩放
该工具在SDK安装路径的tools目录下。
Layer List类型图形
layer list类型图形可以使用一组图片合成一张照片
/res/drawable/layerlist.xml
<layer-list>
<item android:drawable=”@drawable/faceback”> <!—第一张图片-->
<item android:id=”@+id/userimage” android:drawable=”@drawable/user”
android:left=”18dp” android:top=”68.0dp” android:right=”18dp”
android:bottom=”22dp”> <!—第二张图片-->
</layer-list>
<ImageView android:src=”@drawable/layerlist”>
可以根据Item的ID更换不同图片,代码如下:
LayerDrawable layerDrawable=
(LayerDrawable)getResources().getDrawable(R.drawable.layerlist);
layerDrawable.setDrawableByLayerId(R.id.userface,getResources().getDrawable(R.drawable.icon));
imageView.setImageDrawable(layerDrawable);
getResources().getDrawable(); //取得res下的资源对象
State List状态列表图形
显示控件会随着按压,点击,离开显示不同的照片
<selector>
<item android:state_pressed=”true” android:drawable=”@drawable/b_presssed”/>
<item android:state_pressed=”true” android:drawable=”@drawable/b_focused”/>
<item android:drawable=”@drawable/button_normal”> //默认图片,放在最后
</selector>
Level List级别列表图形
用于管理一组可进行切换的图片
<level-list >
<item android:drawable=”@drawable/i1”
android:minLevel=”0” android:maxLevel=”10”>
<item android:drawable=”@drawable/i7”
android:minLevel=”11” android:maxLevel=”20”>
</level-list>
<ImageView android:src=”@drawable/levellist”>
使用setLevel方法可进行图片切换,Android会根据level的值自动选择对应的图片,手机显示剩余电量就是用Level List图形来显示不同图片
imageView.getDrawable.setLevel(8); //显示第一个item图片
imageView.getDrawable.setLevel(15); //显示第二个item图片
//也可以在代码里面设置
LevelListDrawable level;
level.setLevel(8);
Transition Drawable过度图形
<transition>
<item android:drawabl=”@drawable/first”/>
<item android:drawabl=”@drawable/second”/>
</transition>
<ImageView android:src=”@drawable/transition”>
使用下面代码实现图片过渡:
TransitionDrawable drawable = (TransitionDrawable)imageView.getDrawable();
drawable.startTransition(500); //设置过渡时间
button.getBackground(); //得到背景图形
Clip drawable 裁剪图形
/res/drawable/clip.xml
<clip android:drawable=”@drawable/user”
android:clipOrientation=”horizontal” //裁剪方向
android:gravity=”left” //从左边开始裁剪
>
<ImageView android:src=”@drawable/clip”
android:id=”@+id/imageView/”>
由于默认裁剪级别为0(全部裁剪,导致图片看不见)。当级别为10000时,不裁剪图片,图片全部可见。下面代码可以设置裁剪级别
ImageView imageView = (ImageView)this.findViewById(R.id.imageView);
ClipDrawable drawable = (ClipDrawable)imageView.getDrawable();
drawable.setLevel(drawable.getLevel()+1000);
Scale Drawable 缩放图形
Shape Drawable形状图形
/res/drawable/shape.xml
<meta-data>节点的使用
用来为组件提供额外的参数数据
<activity>
<meta-data android:name=”cn.ista.name” android:value=”liming”>
<meta-data android:name=”cn.ista.age” android:value=”5”>
</activity>
//取得组件的信息
ActivityInfo activityInfo = this.getPackageManager().getActivityInfo(
new ComponentName(this,MainActivity.class),PackageManager.GET_META_DATA);
//取得meta-data
Bundle bundle = activityInfo.metaData;
String name = bundle.getString(“cn.ista.name”);
Widgets 窗口小部件
自定义窗口标题
使用自定义标题文件进行设置
title.xml
<TextView
android:text=”自定义标题”/>
在onCreate()方法中添加设置
//设置自定义标题
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.main);
//设置标题
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,R.layout.title);
系统窗口标题文件在android系统源码
core\res\layout位置下screen_custom_title.xml
PopupWindow弹出窗口
ListView数据异步加载与数据缓存AsyncTask
启动一个新的线程
线程池:可以限定线程运行的数量
由于主线程(也叫做UI线程)负责处理用户输入事件(点击按钮、触摸屏幕、按键等),如果主线程被堵塞,应用就会包ANR错误。为了不阻塞主线程,我们需要在子线程中处理耗时的操作,在处理耗时操作的过程中,子线程可能需要更新UI控件的显示,由于控件的更新重绘是由主线程负责的,所以子线程需要通过Hnadler发送消息到主线程的消息队列中,由运行在主线程的消息处理代码接收到消息后更新UI控件的显示。
采用线程+Handler实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比较大。另外,如果耗时操作执行的时间比较长,就有可能同时运行着许多线程,系统将不堪重负。为了提供性能,我们可以使用AsyncTask实现异步处理,事实上其内部也是采用线程+Handler来实现异步处理的,只不过是其内部使用了线程池技术,有效地降低了线程创建数量及限定了同时运行的线程数。
public class UpdataTask extends AsyncTask<String,Integer ,String>{
protected void onPreExecute(){ //运行在UI线程
progressBar.setMax(100);
}
protected String dolnBackground(String…params){//运行在子线程
return “hello world”;
}
protected void onPostExecute(String result){ //运行在UI线程
}
protected void onProgressUpdate(Integer…values){ //运行在UI线程
progressBar.setProgress(values[10]);
}
}
数据分批加载
View footer = getLayoutInflater().inflate(R.layout.footer,null); //根据layout.xml获取view对象 返回的是一个view对象,如果想找到里面的textView,还需要使用footer.findViewById
listView.addFooterView(footer); //为listview添加页脚
一定要在setAdapter()之前添加
用TabHost实现标签页
TabHost是标签页的集合,TabSpec代表标签页,得到TabHost对象后,可以调用addTab方法添加多个TabSpec标签页对象。
TabHost tableHost;
tabHost.setup();//查找获取ID为:@android:id/tabs的TabWidget和ID为:@android:id/tab content的FrameLayout,TabWidget用于显示标签导航,FrameLayout用于放置所有标签页面
TabSpec tabSpec1 = tabHost.newTabSpec(“tab1”); //创建标签,并指定标识
TraceView性能测试与Android应用性能优化方案
TraceView是android平台下的性能测试工具,它可以通过图形化的方式让我们了解我们要跟踪的程序性能,并且具体到method。它包含了两部分,一部分用于程序执行过程中采集数据,另一部分用于程序执行完毕后对数据进行分析。
第一步采集数据
用户可以选择激活或者取消实时数据采集功能。当该功能激活以后Traceview进程就会记录下用户程序每一次的函数调用和耗时,并将数据保存到SDCard上的一个二进制文件中。
在程序中调用android.os.Debug.startMethodTracing方法启动实时数据采集,如下:
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Debug.startMethodTracing(“myTest”); //启动实时采集数据
}
protected void onDestroy(){
Debug.stopMethodTracing(); //停止数据采集
super.onDestroy();
}
第二部分析数据
把SDCard上的二进制数据文件导出到你的电脑上,在Dos窗口上进入android SDK的tools目录,然后执行命令:
traceview c://myTest.trace
执行命令后将打开图形化分析界面
性能优化方案
1、 避免创建不必要的对象
2、 如果方法用不到成员变量,可以把方法声明为static,性能会提高15%到20%
3、 避免使用getters/setters存放Field,可以把Field声明为public,直接访问
4、 static的变量如果不需要修改,应使用static final修饰符定义为常量
5、 使用增强for循环for(:)
6、 私有内部类要访问外部类的Field或方法,可以把外部类的Field或方法声明为包访问权限
7、 合理利用浮点数,浮点数比整型慢两倍