【网学网提醒】:网学会员为大家收集整理了android例子精华版提供大家参考,希望对大家有所帮助!
Android提高第一篇之MediaPlayer
本文介绍MediaPlayer的使用。MediaPlayer可以播放音频和视频,另外也可以通过VideoView来播放视频,虽然VideoView比MediaPlayer简单易用,但定制性不如用MediaPlayer,要视情况选择了。MediaPlayer播放音频比较简单,但是要播放视频就需要SurfaceView。SurfaceView比普通的自定义View更有绘图上的优势,它支持完全的OpenGLES库。
android:layout_width="fill_parent"android:layout_height="fill_parent"
xmlns:android="schemas.android/apk/res/android"
android:orientation="vertical">
android:layout_width="fill_parent">
android:layout_width="wrap_content"android:layout_height="wrap_content">
android:layout_height="wrap_content"android:text="播放音频">
android:layout_height="wrap_content"android:text="停止播放">
android:layout_width="fill_parent">
android:layout_width="fill_parent"android:layout_height="250px">
android:layout_width="wrap_content"android:layout_height="wrap_content">
android:layout_height="wrap_content"android:id="@+id/Button03"
android:text="播放视频">
android:layout_height="wrap_content"android:text="停止播放"android:id="@+id/Button04">
packagecom.testMedia;
importjava.io.IOException;
importjava.util.Timer;
importjava.util.TimerTask;
importandroid.app.Activity;
importandroid.media.AudioManager;
importandroid.media.MediaPlayer;
importandroid.os.Bundle;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceView;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.SeekBar;
importandroid.widget.Toast;
publicclasstestMediaextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
privateSeekBarskb_audio=null;
privateButtonbtn_start_audio=null;
privateButtonbtn_stop_audio=null;
privateSeekBarskb_video=null;
privateButtonbtn_start_video=null;
privateButtonbtn_stop_video=null;
privateSurfaceViewsurfaceView;
privateSurfaceHoldersurfaceHolder;
privateMediaPlayerm=null;
privateTimermTimer;
privateTimerTaskmTimerTask;
privatebooleanisChanging=false;//互斥变量,防止定时器与SeekBar拖动时进度冲突
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//----------Media控件设置---------//
m=newMediaPlayer();
//播放结束之后弹出提示
m.setOnCompletionListener(newMediaPlayer.OnCompletionListener(){
@Override
publicvoidonCompletion(MediaPlayerarg0){
Toast.makeText(testMedia.this,"结束",1000).show();
m.release();
}
});
//----------定时器记录播放进度---------//
mTimer=newTimer();
mTimerTask=newTimerTask(){
@Override
publicvoidrun(){
if(isChanging==true)
return;
if(m.getVideoHeight()==0)
skb_audio.setProgress(m.getCurrentPosition());
else
skb_video.setProgress(m.getCurrentPosition());
}
};
mTimer.schedule(mTimerTask,0,10);
btn_start_audio=(Button)this.findViewById(R.id.Button01);
btn_stop_audio=(Button)this.findViewById(R.id.Button02);
btn_start_audio.setOnClickListener(newClickEvent());
btn_stop_audio.setOnClickListener(newClickEvent());
skb_audio=(SeekBar)this.findViewById(R.id.SeekBar01);
skb_audio.setOnSeekBarChangeListener(newSeekBarChangeEvent());
btn_start_video=(Button)this.findViewById(R.id.Button03);
btn_stop_video=(Button)this.findViewById(R.id.Button04);
btn_start_video.setOnClickListener(newClickEvent());
btn_stop_video.setOnClickListener(newClickEvent());
skb_video=(SeekBar)this.findViewById(R.id.SeekBar02);
skb_video.setOnSeekBarChangeListener(newSeekBarChangeEvent());
surfaceView=(SurfaceView)findViewById(R.id.SurfaceView01);
surfaceHolder=surfaceView.getHolder();
surfaceHolder.setFixedSize(100,100);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
/*
*按键事件处理
*/
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btn_start_audio)
{
m.reset();//恢复到未初始化的状态
m=MediaPlayer.create(testMedia.this,R.raw.big);//读取音频
skb_audio.setMax(m.getDuration());//设置SeekBar的长度
try{
m.prepare();//准备
}catch(IllegalStateExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
m.start();//播放
}
elseif(v==btn_stop_audio||v==btn_stop_video)
{
m.stop();
}
elseif(v==btn_start_video)
{
m.reset();//恢复到未初始化的状态
m=MediaPlayer.create(testMedia.this,R.raw.test);//读取视频
skb_video.setMax(m.getDuration());//设置SeekBar的长度
m.setAudioStreamType(AudioManager.STREAM_MUSIC);
m.setDisplay(surfaceHolder);//设置屏幕
try{
m.prepare();
}catch(IllegalArgumentExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IllegalStateExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
m.start();
}
}
}
/*
*SeekBar进度改变事件
*/
classSeekBarChangeEventimplementsSeekBar.OnSeekBarChangeListener{
@Override
publicvoidonProgressChanged(SeekBarseekBar,intprogress,
booleanfromUser){
//TODOAuto-generatedmethodstub
}
@Override
publicvoidonStartTrackingTouch(SeekBarseekBar){
isChanging=true;
}
@Override
publicvoidonStopTrackingTouch(SeekBarseekBar){
m.seekTo(seekBar.getProgress());
isChanging=false;
}
}
}
Android提高第二篇之SurfaceView的基本使用
SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此
是个非常重要的绘图容器,这次我就用两篇文章来介绍SurfaceView的用法。网上介绍SurfaceView的用法有很多,写法也层出不同,例如继承SurfaceView类,或者继承SurfaceHolder.Callback类等,这个可以根据功能实际需要自己选择,我这里就直接在普通的用户界面调用SurfaceHolder的lockCanvas和unlockCanvasAndPost。
右图用.lockCanvas(null),而左图用.lockCanvas(newRect(oldX,0,oldX+length,
getWindowManager().getDefaultDisplay().getHeight())),对比一下两个效果,由于左图是按指定Rect绘画,所以效率会比右图的全控件绘画高些,并且在清屏之后(canvas.drawColor(Color.BLACK))不会留有上次绘画的残留。
android:layout_width="fill_parent"android:layout_height="fill_parent"
android:orientation="vertical">
android:layout_width="wrap_content"android:layout_height="wrap_content">
android:layout_height="wrap_content"android:text="简单绘画">
android:layout_height="wrap_content"android:text="定时器绘画">
android:layout_width="fill_parent"android:layout_height="fill_parent">
packagecom.testSurfaceView;
importjava.util.Timer;
importjava.util.TimerTask;
importandroid.app.Activity;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.Rect;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceView;
importandroid.view.View;
importandroid.widget.Button;
publicclasstestSurfaceViewextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnSimpleDraw,btnTimerDraw;
SurfaceViewsfv;
SurfaceHoldersfh;
privateTimermTimer;
privateMyTimerTaskmTimerTask;
intY_axis[],//保存正弦波的Y轴上的点
centerY,//中心线
oldX,oldY,//上一个XY点
currentX;//当前绘制到的X轴上的点
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnSimpleDraw=(Button)this.findViewById(R.id.Button01);
btnTimerDraw=(Button)this.findViewById(R.id.Button02);
btnSimpleDraw.setOnClickListener(newClickEvent());
btnTimerDraw.setOnClickListener(newClickEvent());
sfv=(SurfaceView)this.f
indViewById(R.id.SurfaceView01);
sfh=sfv.getHolder();
//动态绘制正弦波的定时器
mTimer=newTimer();
mTimerTask=newMyTimerTask();
//初始化y轴数据
centerY=(getWindowManager().getDefaultDisplay().getHeight()-sfv
.getTop())/2;
Y_axis=newint[getWindowManager().getDefaultDisplay().getWidth()];
for(inti=1;i Y_axis[i-1]=centerY
-(int)(100*Math.sin(i*2*Math.PI/180));
}
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnSimpleDraw){
SimpleDraw(Y_axis.length-1);//直接绘制正弦波
}elseif(v==btnTimerDraw){
oldY=centerY;
mTimer.schedule(mTimerTask,0,5);//动态绘制正弦波
}
}
}
classMyTimerTaskextendsTimerTask{
@Override
publicvoidrun(){
SimpleDraw(currentX);
currentX++;//往前进
if(currentX==Y_axis.length-1){//如果到了终点,则清屏重来
ClearDraw();
currentX=0;
oldY=centerY;
}
}
}
/*
*绘制指定区域
*/
voidSimpleDraw(intlength){
if(length==0)
oldX=0;
Canvascanvas=sfh.lockCanvas(newRect(oldX,0,oldX+length,
getWindowManager().getDefaultDisplay().getHeight()));//关键:获取画布
Log.i("Canvas:",
String.valueOf(oldX)+","+String.valueOf(oldX+length));
PaintmPaint=newPaint();
mPaint.setColor(Color.GREEN);//画笔为绿色
mPaint.setStrokeWidth(2);//设置画笔粗细
inty;
for(inti=oldX+1;i y=Y_axis[i-1];
canvas.drawLine(oldX,oldY,i,y,mPaint);
oldX=i;
oldY=y;
}
sfh.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
}
voidClearDraw(){
Canvascanvas=sfh.lockCanvas(null);
canvas.drawColor(Color.BLACK);/
/清除画布
sfh.unlockCanvasAndPost(canvas);
}
}
注意一下for(inti=oldX+1;i Android提高第三篇之SurfaceView与多线程的混搭
SurfaceView与多线程混搭,是为了防止动画闪烁而实现的一种多线程应用。android的多线程用法与JAVA的多线程用法完全一样,本文不做多线程方面的介绍了。直接讲解SurfaceView与多线程的混合使用,即开一条线程专门读取图片,另外一条线程专门绘图。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都“边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高动画播放的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
android:layout_width="fill_parent"android:layout_height="fill_parent"
android:orientation="vertical">
android:layout_width="wrap_content"android:layout_height="wrap_content">
android:layout_height="wrap_content"android:text="单个独立线程">
android:layout_height="wrap_content"android:text="两个独立线程">
android:layout_width="fill_parent"android:layout_height="fill_parent">
packagecom.testSurfaceView;
importjava.lang.reflect.Field;
importjava.util.ArrayList;
importandroid.app.Activity;
importandroid.graphics.Bitmap;
importandroid.graphics.BitmapFactory;
importandroid.graphics.Canvas;
importandroid.graphics.Paint;
importandroid.graphics.Rect;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceView;
importandroid.view.View;
importandroid.widget.Button;
publicclasstestSurfaceViewextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnSingleThread,btnDoubleThread;
SurfaceViewsfv;
SurfaceHoldersfh;
ArrayListimgList=newArrayList();
intimgWidth,imgHeight;
Bitmapbitmap;//独立线程读取,独立线程绘图
@Override
publicvoidonCreate(Bundle
savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnSingleThread=(Button)this.findViewById(R.id.Button01);
btnDoubleThread=(Button)this.findViewById(R.id.Button02);
btnSingleThread.setOnClickListener(newClickEvent());
btnDoubleThread.setOnClickListener(newClickEvent());
sfv=(SurfaceView)this.findViewById(R.id.SurfaceView01);
sfh=sfv.getHolder();
sfh.addCallback(newMyCallBack());//自动运行surfaceCreated以及surfaceChanged
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnSingleThread){
newLoad_DrawImage(0,0).start();//开一条线程读取并绘图
}elseif(v==btnDoubleThread){
newLoadImage().start();//开一条线程读取
newDrawImage(imgWidth+10,0).start();//开一条线程绘图
}
}
}
classMyCallBackimplementsSurfaceHolder.Callback{
@Override
publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intwidth,
intheight){
Log.i("Surface:","Change");
}
@Override
publicvoidsurfaceCreated(SurfaceHolderholder){
Log.i("Surface:","Create");
//用反射机制来获取资源中的图片ID和尺寸
Field[]fields=R.drawable.class.getDeclaredFields();
for(Fieldfield:fields){
if(!"icon".equals(field.getName()))//除了icon之外的图片
{
intindex=0;
try{
index=field.getInt(R.drawable.class);
}catch(IllegalArgumentExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IllegalAccessExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
//保存图片ID
imgList.add(index);
}
}
//取得图像大小
BitmapbmImg=BitmapFactory.decodeResource(getResources(),
imgList.get(0));
imgWidth=bmImg.getWidth();
imgHeight=bmImg.getHeight();
}
@Override
publicvoidsurfaceDestroyed(SurfaceHolderholder){
Log.i("Surface:","Destroy");
}
}
/*
*读取并显示图片的线程
*/
classLoad_DrawImageextendsThread{
intx,y;
intimgIndex=0;
publicLoad_DrawImage(intx,inty){
this.x=x;
this.y=y;
}
publicvoidrun(){
while(true){
Canvasc=sfh.lockCanvas(newRect(this.x,this.y,this.x
+imgWidth,this.y+imgHeight));
BitmapbmImg=BitmapFactory.decodeResource(getResources(),
imgList.get(imgIndex));
c.drawBitmap(bmImg,this.x,this.y,newPaint());
imgIndex++;
if(imgIndex==imgList.size())
imgIndex=0;
sfh.unlockCanvasAndPost(c);//更新屏幕显示内容
}
}
};
/*
*只负责绘图的线程
*/
classDrawImageextendsThread{
intx,y;
publicDrawImage(intx,inty){
this.x=x;
this.y=y;
}
publicvoidrun(){
while(true){
if(bitmap!=null){//如果图像有效
Canvasc=sfh.lockCanvas(newRect(this.x,this.y,this.x
+imgWidth,this.y+imgHeight));
c.drawBitmap(bitmap,this.x,this.y,newPaint());
sfh.unlockCanvasAndPost(c);//更新屏幕显示内容
}
}
}
};
/*
*只负责读取图片的线程
*/
classLoadImageextendsThread{
intimgIndex=0;
publicvoidrun(){
while(true){
bitmap=BitmapFactory.decodeResource(getResources(),
imgList.get(imgIndex));
imgIndex++;
if(imgIndex==imgList.size())//如果到尽
头则重新读取
imgIndex=0;
}
}
};
}
在一个LayoutXML与一个Activity捆绑的情况下可以视为一个Form,多个LayoutXML与一个Activity捆绑的话那就是个Application本身了。Intent可以分为显式Intent和隐式Intent:显式Intent用于启动明确的目标组件(前面所说的三大组件),同一个Application内的多个Activity调用也是显式Intent;隐式Intent就是调用没有明确的目标组件,可以是系统也可以是第三方程序。隐式Intent一般用于调用系统组件功能,相关例程都是网络上很容易找到的(调用某些系统组件的时候要申请权限)。
Android有三个基础组件Activity,Service和BroadcastReceiver,他们都是依赖Intent来启动。本文介绍的是Activity的生命周期以及针对Activity的Intent使用
//有些外部调用需要开启权限
Uriuri=Uri.parse("google");
Intentit=newIntent(Intent.ACTION_VIEW,uri);
startActivity(it);
Android提高第五篇之Service
如果把Activity比喻为前台程序,那么Service就是后台程序,Service的整个生命周期都只会在后台执行。Service跟Activity一样也由Intent调用。在工程里想要添加一个Service,先新建继承Service的类,然后到AndroidManifest.xml->Application->ApplicationNodes中的Service标签中添加。
Service要由Activity通过startService或者bindService来启动,Intent负责传递参数。
startService与bindService都可以启动Service,那么它们之间有什么区别呢?它们两者的区别就是使Service的周期改变。由startService启动的Service必须要有stopService来结束Service,不调用stopService则会造成Activity结束了而Service还运行着。bindService启动的Service可以由unbindService来结束,也可以在Activity结束之后(onDestroy)自动结束。
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_height="wrap_content"android:id="@+id/btnStartMyService"
android:text="StartMyService">
android:layout_height="wrap_content"android:id="@+id/btnStopMyService"
android:text="StopMyService">
android:layout_height="wrap_content"android:id="@+id/btnBindMyService"
android:text="BindMyService">
android:layout_height="wrap_content"android:id="@+id/btnUnbind
MyService"
android:text="UnbindMyService">
android:layout_height="wrap_content"android:id="@+id/btnExit"
android:text="退出程序">
packagecom.testService;
importandroid.app.Activity;
importandroid.app.Service;
importandroid.content.ComponentName;
importandroid.content.Intent;
importandroid.content.ServiceConnection;
importandroid.os.Bundle;
importandroid.os.IBinder;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.Button;
publicclasstestServiceextendsActivity{
ButtonbtnStartMyService,btnStopMyService,btnBindMyService,btnUnbindMyService,btnExit;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStartMyService=(Button)this.findViewById(R.id.btnStartMyService);
btnStartMyService.setOnClickListener(newClickEvent());
btnStopMyService=(Button)this.findViewById(R.id.btnStopMyService);
btnStopMyService.setOnClickListener(newClickEvent());
btnBindMyService=(Button)this.findViewById(R.id.btnBindMyService);
btnBindMyService.setOnClickListener(newClickEvent());
btnUnbindMyService=(Button)this.findViewById(R.id.btnUnbindMyService);
btnUnbindMyService.setOnClickListener(newClickEvent());
btnExit=(Button)this.findViewById(R.id.btnExit);
btnExit.setOnClickListener(newClickEvent());
}
@Override
publicvoidonDestroy()
{
super.onDestroy();
Log.e("Activity","onDestroy");
}
privateServiceConnection_connection=newServiceConnection(){
@Override
publicvoidonServiceConnected(ComponentNamearg0,IBinderarg1){
//TODOAuto-generatedmethodstub
}
@Override
publicvoidonServiceDisconnected(ComponentNamename){
//TODOAuto-generatedmethodstub
}
};
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
Intentintent=newIntent(testService.this,MyService.class);
if(v==btnStartMyService){
testService.this.startService(intent);
}
elseif(v==btnStopMyService){
testService.this.stopService(intent);
}
elseif(v==btnBindMyService){
testService.this.bindService(intent,_connection,Service.BIND_AUTO_CREATE);
}
elseif(v==btnUnbindMyService){
if(MyService.ServiceState=="onBind")//Service绑定了之后才能解绑
testService.this.unbindService(_connection);
}
elseif(v==btnExit)
{
testService.this.finish();
}
}
}
}
packagecom.testService;
importandroid.app.Service;
importandroid.content.Intent;
importandroid.os.IBinder;
importandroid.util.Log;
publicclassMyServiceextendsService{
staticpublicStringServiceState="";
@Override
publicIBinderonBind(Intentarg0){
Log.e("Service","onBind");
ServiceState="onBind";
returnnull;
}
@Override
publicbooleanonUnbind(Intentintent){
super.onUnbind(intent);
Log.e("Service","onUnbind");
ServiceState="onUnbind";
returnfalse;
}
@Override
publicvoidonCreate(){
super.onCreate();
Log.e("Service","onCreate");
ServiceState="onCreate";
}
@Override
publicvoidonDestroy(){
super.onDestroy();
Log.e("Service","onDestroy");
ServiceState="onDestroy";
}
@Override
publicvoidonStart(Intentintent,intstartid){
super.onStart(intent,startid);
Log.e("Service","onStart");
ServiceState="onStart";
}
}
在Android里面可以使用SAX和DOM,DOM需要把整个XML文件读入内存再解析,比较消耗内存,而SAX基于事件驱动的处理方式,可以在各节点触发回调函数,不过SAX适合节点结构简单的XML文档,复杂的XML文档在后期的节点深度处理会有点麻烦。
hellogv
blog.csdn.net/hellogv
使用SAX解析,需要定义SAXParserFactory(使应用程序能够配置和获取基于SAX的解析器以解析XML文档),SAXParser(从各种输入源解析XML),XMLReader(使用回调函数读取XML文档),其中XMLReader是个关键。XMLReader可以为解析XML定义各种回调函数,“条件符合”的时候触发这些回调函数。
SAXParserFactoryfactory=SAXParserFactory.newInstance();
SAXParserparser=factory.newSAXParser();
XMLReaderreader=parser.getXMLReader();
reader.setContentHandler(handler);
reader.parse(newInputSource(testSAX.this.getResources()
.openRawResource(R.raw.test)));
在这段代码里,XMLReader就调用继承DefaultHandler的SAXHandler。DefaultHandler已实现ContentHandler,DTDHandler,EntityResolver,ErrorHandler等接口,包含常见读取XML的操作,具体请看下面的SAXHandler.java源码。生成XML的结果如下
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="fill_parent"android:id="@+id/btnSAX"
android:text="使用SAX解析XML">
android:layout_width="fill_parent"android:text="生成XML"android:id="@+id/btnOutput">
android:layout_width="fill_parent"android:layout_height="fill_parent">
packagecom.testSAX;
importjava.io.StringWriter;
importjavax.xml.parsers.SAXParser;
importjavax.xml.parsers.SAXParserFactory;
importorg.xml.sax.InputSource;
importorg.xml.sax.XMLReader;
importorg.xmlpull.v1.XmlSerializer;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.util.Xml;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.EditText;
publicclasstestSAXextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnSAX,btnOutput;
EditTextmemo;
SAXHandlerhandler=newSAXHandler();
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnSAX=(Button)this.findViewById(R.id.btnSAX);
btnSAX.setOnClickListener(newClickEvent());
btnOutput=(Button)this.findViewById(R.id.btnOutput);
btnOutput.setOnClickListener(newClickEvent());
memo=(EditText)this.findViewById(R.id.EditText01);
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnSAX){//解析XML,并保存标记,属性等值
try{
SAXParserFactoryfactory=SAXParserFactory.newInstance();
SAXParserparser=factory.newSAXParser();
XMLReaderreader=parser.getXMLReader();
reader.setContentHandler(handler);
reader.parse(newInputSource(testSAX.this.getResources()
.openRa
wResource(R.raw.test)));
}catch(Exceptionee){}
}
elseif(v==btnOutput){//生成XML
try{
XmlSerializerserializer=Xml.newSerializer();
StringWriterwriter=newStringWriter();
try{
serializer.setOutput(writer);
serializer.startDocument("UTF-8",true);
for(inti=0;i {
if(handler.GetKeys().get(i).equals("startTag"))
{
serializer.startTag("",(String)handler.GetValues().get(i));
}
elseif(handler.GetKeys().get(i).equals("Attr")){
String[]str=(String[])handler.GetValues().get(i);
serializer.attribute("",str[0],str[1]);
}
elseif(handler.GetKeys().get(i).equals("text"))
serializer.text((String)handler.GetValues().get(i));
elseif(handler.GetKeys().get(i).equals("endTag"))
{
serializer.endTag("",(String)handler.GetValues().get(i));
}
}
serializer.endDocument();
Stringtext=writer.toString();
text=text.replace("><",">/r/n<");
memo.setText(text);//输出到文本框
}catch(Exceptione){
thrownewRuntimeException(e);
}
}catch(Exceptione){}
}
}
}
}
Android提高第八篇之SQLite分页读取
Android包含了常用于嵌入式系统的SQLite,免去了开发者自己移植安装的功夫。SQLite支持多数SQL92标准,很多常用的SQL命
令都能在SQLite上面使用,除此之外Android还提供了一系列自定义的方法去简化对SQLite数据库的操作。不过有跨平台需求的程序就建议使用标准的SQL语句,毕竟这样容易在多个平台之间移植。
本文主要讲解了SQLite的基本用法,如:创建数据库,使用SQL命令查询数据表、插入数据,关闭数据库,以及使用GridView实现了一个分页栏(关于GridView的用法),用于把数据分页显示。
android:layout_height="wrap_content"android:paddingBottom="4dip"
android:layout_width="fill_parent">
android:layout_below="@+id/ItemImage"android:layout_height="wrap_content"
android:text="TextView01"android:layout_centerHorizontal="true"
android:id="@+id/ItemText">
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="fill_parent"android:id="@+id/btnCreateDB"
android:text="创建数据库">
android:layout_width="fill_parent"android:text="插入一串实验数据"android:id="@+id/btnInsertRec">
android:text="关闭数据库"android:layout_width="fill_parent">
android:layout_width="fill_parent"android:layout_height="256dip">
android:layout_height="32dip"android:numColumns="auto_fit"
android:columnWidth="40dip">
packagecom.testSQLite;
importjava.util.ArrayList;
importjava.util.HashMap;
importandroid.app.Activity;
importandroid.database.Cursor;
importandroid.database.SQLException;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.AdapterView;
importandroid.widget.AdapterView.OnItemClickListener;
importandroid.widget.Button;
importandroid.widget.EditText;
importandroid.widget.GridView;
importandroid.widget.SimpleAdapter;
publicclasstestSQLiteextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnCreateDB,btnInsert,btnClose;
EditTextedtSQL;//显示分页
数据
SQLiteDatabasedb;
intid;//添加记录时的id累加标记,必须全局
staticfinalintPageSize=10;//分页时,每页的数据总数
privatestaticfinalStringTABLE_NAME="stu";
privatestaticfinalStringID="id";
privatestaticfinalStringNAME="name";
SimpleAdaptersaPageID;//分页栏适配器
ArrayList>lstPageID;//分页栏的数据源,与PageSize和数据总数相关
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnCreateDB=(Button)this.findViewById(R.id.btnCreateDB);
btnCreateDB.setOnClickListener(newClickEvent());
btnInsert=(Button)this.findViewById(R.id.btnInsertRec);
btnInsert.setOnClickListener(newClickEvent());
btnClose=(Button)this.findViewById(R.id.btnClose);
btnClose.setOnClickListener(newClickEvent());
edtSQL=(EditText)this.findViewById(R.id.EditText01);
GridViewgridview=(GridView)findViewById(R.id.gridview);//分页栏控件
//生成动态数组,并且转入数据
lstPageID=newArrayList>();
//生成适配器的ImageItem<====>动态数组的元素,两者一一对应
saPageID=newSimpleAdapter(testSQLite.this,//没什么解释
lstPageID,//数据来源
R.layout.pagebuttons,//XML实现
newString[]{"ItemText"},
newint[]{R.id.ItemText});
//添加并且显示
gridview.setAdapter(saPageID);
//添加消息处理
gridview.setOnItemClickListener(newOnItemClickListener(){
@Override
publicvoidonItemClick(AdapterView>arg0,Viewarg1,intarg2,
longarg3){
LoadPage(arg2);//根据所选分页读取对应的数据
}
});
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnCreateDB){
CreateDB();
}elseif(v==btnInsert){
InsertRecord(16);//插入16条记录
RefreshPage();
}elseif(v==btnClose){
db.close();
}
}
}
/*
*读取指定ID的分页数据
*SQL:Select*FromTABLE_NAMELimit9Offset10;
*表示从TABLE_NAME表获取数据,跳过10行,取9行
*/
voidLoadPage(intpageID)
{
Stringsql="select*from"+TABLE_NAME+
"Limit"+String.valueOf(PageSize)+"Offset"+String.valueOf(pageID*PageSize)
;
Cursorrec=db.rawQuery(sql,null);
setTitle("当前分页的数据总数:"+String.valueOf(rec.getCount()));
//取得字段名称
Stringtitle="";
intcolCount=rec.getColumnCount();
for(inti=0;i title=title+rec.getColumnN, ame(i)+"";
//列举出所有数据
Stringcontent="";
intrecCount=rec.getCount();
for(inti=0;i rec.moveToPosition(i);
for(intii=0;ii {
content=content+rec.getString(ii)+"";
}
content=content+"/r/n";
}
edtSQL.setText(title+"/r/n"+content);//显示出来
rec.close();
}
/*
*在内存创建数据库和数据表
*/
voidCreateDB(){
//在内存创建数据库
db=SQLiteDatabase.create(null);
Log.e("DBPath",db.getPath());
Stringamount=String.valueOf(databaseList().length);
Log.e("DBamount",amount);
//创建数据表
Stringsql="CREATETABLE"+TABLE_NAME+"("+ID
+"textnotnull,"+NAME+"textnotnull"+");";
try{
db.execSQL("DROPTABLEIFEXISTS"+TABLE_NAME);
db.execSQL(sql);
}catch(SQLExceptione){}
}
/*
*插入N条数据
*/
voidInsertRecord(intn){
inttotal=id+n;
for(;id Stringsql="insertinto"+TABLE_NAME+"("+ID+","+NAME
+")values('"+String.valueOf(id)+"','test');";
try{
db.execSQL(sql);
}catch(SQLExceptione){
}
}
}
/*
*插入之后刷新分页
*/
voidRefreshPage()
{
Stringsql="selectcount(*)from"+TABLE_NAME;
Cursorrec=db.rawQuery(sql,null);
rec.moveToLast();
longrecSize=rec.getLong(0);//取得总数
rec.close();
intpageNum=(int)(recSize/PageSize)+1;//取得分页数
lstPageID.clear();
for(inti=0;i HashMapmap=newHashMap();
map.put("ItemText","No."+String.valueOf(i));
lstPageID.add(map);
}
saPageID.notifyDataSetChanged();
}
}
Android提高第九篇之SQLite分页表格
实现并封装一个SQL分页表格控件,不仅支持分页还是以表格的形式展示数据
这个SQL分页表格控件主要分为“表格区”和“分页栏”这两
部分,这两部分都是基于GridView实现的。网上介绍Android上实现表格的DEMO一般都用ListView。ListView与GridView对比,ListView最大的优势是格单元的大小可以自定义,可以某单元长某单元短,但是难于实现自适应数据表的结构;而GridView最大的优势就是自适应数据表的结构,但是格单元统一大小。。。对于数据表结构多变的情况,建议使用GridView实现表格。
1.自适应数据表结构,但是格单元统一大小;
2.支持分页;
3.“表格区”有按键事件回调处理,“分页栏”有分页切换事件回调处理。
packagecom.testSQLite;
importandroid.app.Activity;
importandroid.database.Cursor;
importandroid.database.SQLException;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.LinearLayout;
importandroid.widget.Toast;
publicclasstestSQLiteextendsActivity{
GVTabletable;
ButtonbtnCreateDB,btnInsert,btnClose;
SQLiteDatabasedb;
intid;//添加记录时的id累加标记,必须全局
privatestaticfinalStringTABLE_NAME="stu";
privatestaticfinalStringID="id";
privatestaticfinalStringNAME="name";
privatestaticfinalStringPHONE="phone";
privatestaticfinalStringADDRESS="address";
privatestaticfinalStringAGE="age";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnCreateDB=(Button)this.findViewById(R.id.btnCreateDB);
btnCreateDB.setOnClickListener(newClickEvent());
btnInsert=(Button)this.findViewById(R.id.btnInsertRec);
btnInsert.setOnClickListener(newClickEvent());
btnClose=(Button)this.findViewById(R.id.btnClose);
btnClose.setOnClickListener(newClickEvent());
table=newGVTable(this);
table.gvSetTableRowCount(8);//设置每个分页的ROW总数
LinearLayoutly=(LinearLayout)findViewById(R.id.MainLinearLayout);
table.setTableOnClickListener(newGVTable.OnTableClickListener(){
@Override
publicvoidonTableClickListener(intx,inty,Cursorc){
c.moveToPosition(y);
Stringstr=c.getString(x)+"位置:("+String.valueOf(x)+","+String.valueOf(y)+")";
Toast.makeText(testSQLite.this,str,1000).show();
}
});
table.setOnPageSwitchListener(newGVTable.OnPageSwitchListener(){
@Override
publicvoidonPageSwitchListener(intpageID,intpageCount){
Stringstr="共有"+String.valueOf(pageCount)+
"当前第"+String.valueOf(pageID)+"页";
Toast.makeText(testSQLite.this,str,1000).show();
}
});
ly.addView(table);
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnCreateDB){
CreateDB();
}elseif(v==btnInsert){
InsertRecord(16);//插入16条记录
table.gvUpdatePageBar("selectcount(*)from"+TABLE_NAME,db);
table.gvReadyTable("select*from"+TABLE_NAME,db);
}elseif(v==btnClose){
table.gvRemoveAll();
db.close();
}
}
}
/**
*在内存创建数据库和数据表
*/
voidCreateDB(){
//在内存创建数据库
db=SQLiteDatabase.create(null);
Log.e("DBPath",db.getPath());
Stringamount=String.valueOf(databaseList().length);
Log.e("DBamount",amount);
//创建数据表
Stringsql="CREATETABLE"+TABLE_NAME+"("+
ID+"textnotnull,"+NAME+"textnotnull,"+
ADDRESS+"textnotnull,"+PHONE+"textnotnull,"+
AGE+"textnotnull"+");";
try{
db.execSQL("DROPTABLEIFEXISTS"+TABLE_NAME);
db.execSQL(sql);
}catch(SQLExceptione){}
}
/**
*插入N条数据
*/
voidInsertRecord(intn){
inttotal=id+n;
for(;id Stringsql="insertinto"+TABLE_NAME+"("+
ID+","+NAME+","+ADDRESS+","+PHONE+","+AGE
+")values('"+String.valueOf(id)+"','man','address','123456789','18');";
try{
db.execSQL(sql);
}catch(SQLExceptione){
}
}
}
}
packagecom.testSQLite;
importjava.util.ArrayList;
importjava.util.HashMap;
importandroid.content.Context;
importandroid.database.Cursor;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.view.View;
importandroid.widget.AdapterView;
importandroid.widget.GridView;
importandroid.widget.LinearLayout;
importandroid.widget.SimpleAdapter;
importandroid.widget.AdapterView.OnItemClickListener;
publicclassGVTableextendsLinearLayout{
protectedGridViewgvTable,gvPage;
protectedSimpleAdaptersaPageID,saTable;//适配器
protectedArrayList>srcPageID,srcTable;//数据源
protectedintTableRowCount=10;//分页时,每页的Row总数
protectedintTableColCount=0;//每页col的数量
protectedSQLiteDatabasedb;
protectedStringrawSQL="";
protectedCursorcurTable;//分页时使用的Cursor
protectedOnTableClickListenerclickListener;//整个分页控件被点击时的回调函数
protectedOnPageSwitchListenerswitchListener;//分页切换时的回调函数
publicGVTable(Contextcontext){
super(context);
this.setOrientation(VERTICAL);//垂直
//----------------------------------------
gvTable=newGridView(context);
addView(gvTable,newLinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT));//宽长式样
srcTable=newArrayList>();
saTable=newSimpleAdapter(context,
srcTable,//数据来源
R.layout.items,//XML实现
newString[]{"ItemText"},//动态数组与ImageItem对应的子项
newint[]{R.id.ItemText});
//添加并且显示
gvTable.setAdapter(saTable);
gvTable.setOnItemClickListener(newOnItemClickListener(){
@Override
publicvoidonItemClick(AdapterView>arg0,Viewarg1,intarg2,
longarg3){
inty=arg2/curTable.getColumnCount()-1;//标题栏的不算
intx=arg2%curTable.getColumnCount();
if(clickListener!=null//分页数据被点击
&;&;y!=-1){//点中的不是标题栏时
clickListener.onTableClickListener(x,y,curTable);
}
}
});
//----------------------------------------
gvPage=newGridView(context);
gvPage.setColumnWidth(40);//设置每个分页按钮的宽度
gvPage.setNumColumns(GridView.AUTO_FIT);//分页按钮数量自动设置
addView(gvPage,newLinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
Layou
tParams.WRAP_CONTENT));//宽长式样
srcPageID=newArrayList>();
saPageID=newSimpleAdapter(context,
srcPageID,//数据来源
R.layout.items,//XML实现
newString[]{"ItemText"},//动态数组与ImageItem对应的子项
newint[]{R.id.ItemText});
//添加并且显示
gvPage.setAdapter(saPageID);
//添加消息处理
gvPage.setOnItemClickListener(newOnItemClickListener(){
@Override
publicvoidonItemClick(AdapterView>arg0,Viewarg1,intarg2,
longarg3){
LoadTable(arg2);//根据所选分页读取对应的数据
if(switchListener!=null){//分页切换时
switchListener.onPageSwitchListener(arg2,srcPageID.size());
}
}
});
}
/**
*清除所有数据
*/
publicvoidgvRemoveAll()
{
if(this.curTable!=null)
curTable.close();
srcTable.clear();
saTable.notifyDataSetChanged();
srcPageID.clear();
saPageID.notifyDataSetChanged();
}
/**
*读取指定ID的分页数据,返回当前页的总数据
*SQL:Select*FromTABLE_NAMELimit9Offset10;
*表示从TABLE_NAME表获取数据,跳过10行,取9行
*@parampageID指定的分页ID
*/
protectedvoidLoadTable(intpageID)
{
if(curTable!=null)//释放上次的数据
curTable.close();
Stringsql=rawSQL+"Limit"+String.valueOf(TableRowCount)+"Offset"+String.valueOf(pageID*TableRowCount);
curTable=db.rawQuery(sql,null);
gvTable.setNumColumns(curTable.getColumnCount());//表现为表格的关键点!
TableColCount=curTable.getColumnCount();
srcTable.clear();
//取得字段名称
intcolCount=curTable.getColumnCount();
for(inti=0;i HashMapmap=newHashMap();
map.put("ItemText",curTable.getColumnName(i));
srcTable.add(map);
}
//列举出所有数据
intrecCount=curTable.getCount();
for(inti=0;i curTable.moveToPosition(i);
for(intii=0;ii {
HashMapmap=newHashMap();
map.put("ItemText",curTable.getString(ii));
srcTable.add(map);
}
}
saTable.notifyDataSetChanged();
}
/**
*设置表格的最多显示的行数
*@paramrow表格的行数
*/
publicvoidgvSetTableRowCount(introw)
{
TableRowCount=row;
}
/**
*取得表格的最大行数
*@return行数
*/
publicintgvGetTableRowCount()
{
returnTableRowCount;
}
/**
*取得当前分页的Cursor
*@return当前分页的Cursor
*/
publicCursorgvGetCurrentTable()
{
returncurTable;
}
/**
*准备分页显示数据
*@paramrawSQLsql语句
*@paramdb数据库
*/
publicvoidgvReadyTable(StringrawSQL,SQLiteDatabasedb)
{
this.rawSQL=rawSQL;
this.db=db;
}
/**
*刷新分页栏,更新按钮数量
*@paramsqlSQL语句
*@paramdb数据库
*/
publicvoidgvUpdatePageBar(Stringsql,SQLiteDatabasedb)
{
Cursorrec=db.rawQuery(sql,null);
rec.moveToLast();
longrecSize=rec.getLong(0);//取得总数
rec.close();
intpageNum=(int)(recSize/TableRowCount)+1;//取得分页数
srcPageID.clear();
for(inti=0;i HashMapmap=newHashMap();
map.put("ItemText","No."+String.valueOf(i));//添加图像资源的ID
srcPageID.add(map);
}
saPageID.notifyDataSetChanged();
}
//---------------------------------------------------------
/**
*表格被点击时的回调函数
*/
publicvoidsetTableOnClickListener(OnTableClickListenerclick){
this.clickListener=click;
}
publicinterfaceOnTableClickListener{
publicvoidonTableClickListener(intx,inty,Cursorc);
}
//---------------------------------------------------------
/**
*分页栏被点击时的回调函数
*/
publicvoidsetOnPageSwitchListener(OnPageSwitchListenerpageSwitch){
this.switchListener=p
ageSwitch;
}
publicinterfaceOnPageSwitchListener{
publicvoidonPageSwitchListener(intpageID,intpageCount);
}
}
Android提高第十篇之AudioRecord实现"助听器"
Android可以通过MediaRecorder和AudioRecord这两个工具来实现录音,MediaRecorder直接把麦克风的数据存到文件,并且能够直接进行编码(如AMR,MP3等),而AudioRecord则是读取麦克风的音频流。本文使用AudioRecord读取音频流,使用AudioTrack播放音频流,通过“边读边播放”以及增大音量的方式来实现一个简单的助听器程序。
PS:由于目前的Android模拟器还不支持AudioRecord,因此本程序需要编译之后放到真机运行。
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="fill_parent"android:text="开始边录边放">
android:layout_width="fill_parent"android:text="停止"android:id="@+id/btnStop">
android:layout_width="fill_parent"android:text="退出">
android:text="程序音量调节"android:layout_width="fill_parent">
android:layout_width="fill_parent">
packagecom.testRecord;
importandroid.app.Activity;
importandroid.media.AudioFormat;
importandroid.media.AudioManager;
importandroid.media.AudioRecord;
importandroid.media.AudioTrack;
importandroid.media.MediaRecorder;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.SeekBar;
importandroid.widget.Toast;
publicclasstestRecordextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnRecord,btnStop,btnExit;
SeekBarskbVolume;//调节音量
booleanisRecording=false;//是否录放的标记
staticfinalintfrequency=44100;
staticfinalintchannelConfiguration=AudioFormat.CHANNEL_CONFIGURATION_MONO;
staticfinalintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;
intrecBufSize,playBufSize;
AudioRecordaudioRecord;
AudioTrackaudioTrack;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setTitle("助听器");
recBufSize=AudioRecord.getMinBufferSize(frequency,
channelConfiguration,audioEncoding);
playBufSize=AudioTrack.getMinBufferSize(frequency,
channelConfiguration,audioEncoding);
//-----------------------------------------
audioRecord=newAudioRecord(MediaRecorder.AudioSource.MIC,frequency,
channelConfiguration,audioEncoding,recBufSize);
audioTrack=newAudioTrack(AudioManager.STREAM_MUSIC,frequency,
channelConfiguration,audioEncoding,
playBufSize,AudioTrack.MODE_STREAM);
//------------------------------------------
btnRecord=(Button)this.findViewById(R.id.btnRecord);
btnRecord.setOnClickListener(newClickEvent());
btnStop=(Button)this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(newClickEvent());
btnExit=(Button)this.findViewById(R.id.btnExit);
btnExit.setOnClickListener(newClickEvent());
skbVolume=(SeekBar)this.findViewById(R.id.skbVolume);
skbVolume.setMax(100);//音量调节的极限
skbVolume.setProgress(70);//设置seekbar的位置值
audioTrack.setStereoVolume(0.7f,0.7f);//设置当前音量大小
skbVolume.setOnSeekBarChangeListener(newSeekBar.OnSeekBarChangeListener(){
@Override
publicvoidonStopTrackingTouch(SeekBarseekBar){
floatvol=(float)(seekBar.getProgress())/(float)(seekBar.getMax());
audioTrack.setStereoVolume(vol,vol);//设置音量
}
@Override
publicvoidonStartTrackingTouch(SeekBarseekBar){
//TODOAuto-generatedmethodstub
}
@Override
publicvoidonProgressChanged(SeekBarseekBar,intprogress,
booleanfromUser){
//TODOAuto-generatedmethodstub
}
});
}
@Override
protectedvoidonDestroy(){
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnRecord){
isRecording=true;
newRecordPlayThread().start();//开一条线程边录边放
}elseif(v==btnStop){
isRecording=false;
}elseif(v==btnExit){
isRecording=false;
testRecord.this.finish();
}
}
}
classRecordPlayThreadextendsThread{
publicvoidrun(){
try{
byte[]buffer=newbyte[recBufSize];
audioRecord.startRecording();//开始录制
audioTrack.play();//开始播放
while(isRecording){
//从MIC保存数据到缓冲区
intbufferReadResult=audioRecord.read(buffer,0,
recBufSize);
byte[]tmpBuf=newbyte[bufferReadResult];
System.arraycopy(buffer,0,tmpBuf,0,bufferReadResult);
//写入数据即播放
audioTrack.write(tmpBuf,0,tmpBuf.length);
}
audioTrack.stop();
audioRecord.stop();
}catch(Throwablet){
Toast.makeText(testRecord.this,t.getMessage(),1000);
}
}
};
}
Android提高第十一篇之模拟信号示波器
结合SurfaceView实现一个Android版的手机模拟信号示波器。最近物联网炒得很火,作为手机软件开发者,如何在不修改手机硬件电路的前提下实现与第三方传感器结合呢?麦克风就是一个很好的ADC接口,通过麦克风与第三方传感器结合,再在软件里对模拟信号做相应的处理,就可以提供更丰富的传感化应用。
本文程序使用8000hz的采样率,对X轴方向绘图的实时性要求较高,如果不降低X轴的分辨率,程序的实时性较差,因此程序对X轴数据缩小区间为8倍~16倍。由于采用16位采样,因此Y轴数据的高度相对于手机屏幕来说也偏大,程序也对Y轴数据做缩小,区间为1倍~10倍。在SurfaceView的OnTouchListener方法里加入了波形基线的位置调节,直接在SurfaceView控件上触摸即可控制整体波形偏上或偏下显示。
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_height="wrap_content"android:layout_width="fill_parent"
android:orientation="horizontal">
android:text="开始"android:layout_width="80dip">
android:id="@+id/btnExit"android:layout_width="80dip">
android:layout_height="wrap_content"android:id="@+id/zctlX">
android:layout_height="wrap_content"android:id="@+id/zctlY">
android:layout_height="fill_parent"android:layout_width="fill_parent">
ClsOscilloscope.java是实现示波器的类库,包含AudioRecord操作线程和SurfaceView绘图线程的实现,两个线程同步操作,
packagecom.testOscilloscope;
importjava.util.ArrayList;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.Rect;
importandroid.media.AudioRecord;
importandroid.view.SurfaceView;
publicclassClsOscilloscope{
privateArrayListinBuf=newArrayList();
privatebooleanisRecording=false;//线程控制标记
/**
*X轴缩小的比例
*/
publicintrateX=4;
/**
*Y轴缩小的比例
*/
publicintrateY=4;
/**
*Y轴基线
*/
publicintbaseLine=0;
/**
*初始化
*/
publicvoidinitOscilloscope(intrateX,intrateY,intbaseLine){
this.rateX=rateX;
this.rateY=rateY;
this.baseLine=baseLine;
}
/**
*开始
*
*@paramrecBufSize
*AudioRecord的MinBufferSize
*/
publicvoidStart(AudioRecordaudioRecord,intrecBufSize,SurfaceViewsfv,
PaintmPaint){
isRecording=true;
newRecordThread(audioRecord,recBufSize).start();//开始录制线程
newDrawThread(sfv,mPaint).start();//开始绘制线程
}
/**
*停止
*/
publicvoidStop(){
isRecording=false;
inBuf.clear();//清除
}
/**
*负责从MIC保存数据到inBuf
*
*@authorGV
*
*/
classRecordThreadextendsThread{
privateintrecBufSize;
privateAudioRecordaudioRecord;
publicRecordThread(AudioRecordaudioRecord,intrecBufSize){
this.audioRecord=audioRecord;
this.recBufSize=recBufSize;
}
publicvoidrun(){
try{
short[]buffer=newshort[recBufSize];
audioRecord.startRecording();//开始录制
while(isRecording){
//从MIC保存数据到缓冲区
intbufferReadResult=audioRecord.read(buffer,0,
recBufSize);
short[]tmpBuf=newshort[bufferReadResult/rateX];
for(inti=0,ii=0;i *rateX){
tmpBuf[i]=buffer[ii];
}
synchronized(inBuf){//
inBuf.add(tmpBuf);//添加数据
}
}
audioRecord.stop();
}catch(Throwablet){
}
}
};
/**
*负责绘制inBuf中的数据
*
*@authorGV
*
*/
classDrawThreadextendsThread{
privateintoldX=0;//上次绘制的X坐标
privateintoldY=0;//上次绘制的Y坐标
privateSurfaceViewsfv;//画板
privateintX_index=0;//当前画图所在屏幕X轴的坐标
privatePaintmPaint;//画笔
publicDrawThread(SurfaceViewsfv,PaintmPaint){
this.sfv=sfv;
this.mPaint=mPaint;
}
publicvoidrun(){
while(isRecording){
ArrayListbuf=newArrayList();
synchronized(inBuf){
if(inBuf.size()==0)
continue;
buf=(ArrayList)inBuf.clone();//保存
inBuf.clear();//清除
}
for(inti=0;i short[]tmpBuf=buf.get(i);
SimpleDraw(X_index,tmpBuf,rateY,baseLine);//把缓冲区数据画出来
X_index=X_index+tmpBuf.length;
if(X_index>sfv.getWidth()){
X_index=0;
}
}
}
}
/**
*绘制指定区域
*
*@paramstart
*X轴开始的位置(全屏)
*@parambuffer
*缓冲区
*@paramrate
*Y轴数据缩小的比例
*@parambaseLine
*Y轴基线
*/
voidSimpleDraw(intstart,short[]buffer,intrate,intbaseLine){
if(start==0)
oldX=0;
Canvascanvas=sfv.getHolder().lockCanvas(
newRect(start,0,start+buffer.length,sfv.getHeight()));//关键:获取画布
canvas.drawColor(Color.BLACK);//清除背景
inty;
for(inti=0;i intx=i+start;
y=buffer[i]/rate+baseLine;//调节缩小比例,调节基准线
canvas.drawLine(oldX,oldY,x,y,mPaint);
oldX=x;
oldY=y;
}
sfv.getHolder().unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
}
}
}
packagecom.testOscilloscope;
importandroid.app.Activity;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.media.AudioFormat;
importandroid.media.AudioRecord;
importandroid.media.MediaRecorder;
importandroid.os.Bundle;
importandroid.view.MotionEvent;
importandroid.view.SurfaceView;
importandroid.view.View;
importandroid.view.View.OnTouchListener;
importandroid.widget.Button;
importandroid.widget.ZoomControls;
publicclasstestOscilloscopeextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnStart,btnExit;
SurfaceViewsfv;
ZoomControlszctlX,zctlY;
ClsOscilloscopeclsOscilloscope=newClsOscilloscope();
staticfinalintfrequency=8000;//分辨率
staticfinalintchannelConfiguration=AudioFormat.CHANNEL_CONFIGURATION_MONO;
staticfinalintaudioEncoding=AudioFormat.ENCODING_PCM_16BIT;
staticfinalintxMax=16;//X轴缩小比例最大值,X轴数据量巨大,容易产生刷新延时
staticfinalintxMin=8;//X轴缩小比例最小值
staticfinalintyMax=10;//Y轴缩小比例最大值
staticfinalintyMin=1;//Y轴缩小比例最小值
intrecBufSize;//录音最小buffer大小
AudioRecordaudioRecord;
PaintmPaint;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//录音组件
recBufSize=Aud
ioRecord.getMinBufferSize(frequency,
channelConfiguration,audioEncoding);
audioRecord=newAudioRecord(MediaRecorder.AudioSource.MIC,frequency,
channelConfiguration,audioEncoding,recBufSize);
//按键
btnStart=(Button)this.findViewById(R.id.btnStart);
btnStart.setOnClickListener(newClickEvent());
btnExit=(Button)this.findViewById(R.id.btnExit);
btnExit.setOnClickListener(newClickEvent());
//画板和画笔
sfv=(SurfaceView)this.findViewById(R.id.SurfaceView01);
sfv.setOnTouchListener(newTouchEvent());
mPaint=newPaint();
mPaint.setColor(Color.GREEN);//画笔为绿色
mPaint.setStrokeWidth(1);//设置画笔粗细
//示波器类库
clsOscilloscope.initOscilloscope(xMax/2,yMax/2,sfv.getHeight()/2);
//缩放控件,X轴的数据缩小的比率高些
zctlX=(ZoomControls)this.findViewById(R.id.zctlX);
zctlX.setOnZoomInClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
if(clsOscilloscope.rateX>xMin)
clsOscilloscope.rateX--;
setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"
+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");
}
});
zctlX.setOnZoomOutClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
if(clsOscilloscope.rateX clsOscilloscope.rateX++;
setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"
+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");
}
});
zctlY=(ZoomControls)this.findViewById(R.id.zctlY);
zctlY.setOnZoomInClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
if(clsOscilloscope.rateY>yMin)
clsOscilloscope.rateY--;
setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"
+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");
}
});
zctlY.setOnZoomOutClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
if(clsOscilloscope.rateY clsOscilloscope.rateY++;
setTitle("X轴缩小"+String.valueOf(clsOscilloscope.rateX)+"倍"
+","+"Y轴缩小"+String.valueOf(clsOscilloscope.rateY)+"倍");
}
});
}
@Override
protectedvoidonDestroy(){
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}
/**
*按键事件处理
*@authorGV
*
*/
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnStart){
clsOscilloscope.baseLine=sfv.getHeight()/2;
clsOscilloscope.Start(audioRecord,recBufSize,sfv,mPaint);
}elseif(v==btnExit){
clsOscilloscope.Stop();
}
}
}
/**
*触摸屏动态设置波形图基线
*@authorGV
*
*/
classTouchEventimplementsOnTouchListener{
@Override
publicbooleanonTouch(Viewv,MotionEventevent){
clsOscilloscope.baseLine=(int)event.getY();
returntrue;
}
}
}
Android提高第十二篇之蓝牙传感应用
如果传感器本身需要包含控制电路(例如采集血氧信号需要红外和红外线交替发射),那么传感器本身就需要带一片主控IC,片内采集并输出数字信号了。Android手机如何在不改硬件电路的前提下与这类数字传感器交互呢?可选的通信方式就有USB和蓝牙,两种方式各有好处:USB方式可以给传感器供电,蓝牙方式要自备电源;USB接口标准不一,蓝牙普遍支持SPP协议。本文选择蓝牙方式做介绍,介绍Android的蓝牙API以及蓝牙客户端的用法。
在Android2.0,官方终于发布了蓝牙API(2.0以下系统的非官方的蓝牙API可以参考这里:code.google/p/android-bluetooth/)。Android手机一般以客户端的角色主动连接SPP协议设备(接上蓝牙模块的数字传感器),连接流程是:
1.使用registerReceiver注册BroadcastReceiver来获取蓝牙状态、搜索设备等消息;
2.使用BlueAdatper的搜索;
3.在BroadcastReceiver的onReceive()里取得搜索所得的蓝牙设备信息(如名称,MAC,RSSI);
4.通过设备的MAC地址来建立一个BluetoothDevice对象;
5.由BluetoothDevice衍生出BluetoothSocket,准备SOCKET来读写设备;
6.通过Blue
toothSocket的createRfcommSocketToServiceRecord()方法来选择连接的协议/服务,这里用的是SPP(UUID:00001101-0000-1000-8000-00805F9B34FB);
7.Connect之后(如果还没配对则系统自动提示),使用BluetoothSocket的getInputStream()和getOutputStream()来读写蓝牙设备。
Stringaction=intent.getAction();
Bundleb=intent.getExtras();
Object[]lstName=b.keySet().toArray();
//显示所有收到的消息及其细节
for(inti=0;i StringkeyName=lstName[i].toString();
Log.e(keyName,String.valueOf(b.get(keyName)));
}
Android提高第十三篇之探秘蓝牙隐藏API
。用过Android系统设置(Setting)的人都知道蓝牙搜索之后可以建立配对和解除配对,但是这两项功能的函数没有在SDK中给出,那么如何去使用这两项功能呢?本文利用JAVA的反射机制去调用这两项功能对应的函数:createBond和removeBond,具体的发掘和实现步骤如下:
1.使用Git工具下载platform/packages/apps/Settings.git,在Setting源码中查找关于建立配对和解除配对的API,知道这两个API的宿主(BluetoothDevice);
2.使用反射机制对BluetoothDevice枚举其所有方法和常量,看看是否存在:
staticpublicvoidprintAllInform(ClassclsShow){
try{
//取得所有方法
Method[]hideMethod=clsShow.getMethods();
inti=0;
for(;i Log.e("methodname",hideMethod[i].getName());
}
//取得所有常量
Field[]allFields=clsShow.getFields();
for(i=0;i Log.e("Fieldname",allFields[i].getName());
}
}catch(SecurityExceptione){
//thrownewRuntimeException(e.getMessage());
e.printStackTrace();
}catch(IllegalArgumentExceptione){
//thrownewRuntimeException(e.getMessage());
e.printStackTrace();
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
/**
*与设备配对参考源码:platform/packages/apps/Settings.git
*/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
staticpublicbooleancreateBond(ClassbtClass,BluetoothDevicebtDevice)throwsException{
MethodcreateBondMethod=btClass.getMethod("createBond"
);
BooleanreturnValue=(Boolean)createBondMethod.invoke(btDevice);
returnreturnValue.booleanValue();
}
/**
*与设备解除配对参考源码:platform/packages/apps/Settings.git
*/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
staticpublicbooleanremoveBond(ClassbtClass,BluetoothDevicebtDevice)throwsException{
MethodremoveBondMethod=btClass.getMethod("removeBond");
BooleanreturnValue=(Boolean)removeBondMethod.invoke(btDevice);
returnreturnValue.booleanValue();
}
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_height="wrap_content"android:layout_width="fill_parent">
android:text="Search"android:layout_width="160dip">
android:layout_width="160dip"android:text="Show"android:id="@+id/btnShow">
android:layout_width="wrap_content"android:layout_height="wrap_content">
, android:layout_height="fill_parent">
packagecom.testReflect;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
importandroid.bluetooth.BluetoothDevice;
importandroid.util.Log;
publicclassClsUtils{
/**
*与设备配对参考源码:platform/packages/apps/Settings.git
*/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
staticpublicbooleancreateBond(ClassbtClass,BluetoothDevicebtDevice)throwsException{
MethodcreateBondMethod=btClass.getMethod("createBond");
BooleanreturnValue=(Boolean)createBondMethod.invoke(btDevice);
returnreturnValue.booleanValue();
}
/**
*与设备解除配对参考源码:platform/packages/apps/Settings.git
*/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
*/
staticpublicbooleanremoveBond(ClassbtClass,BluetoothDevicebtDevice)throwsException{
MethodremoveBondMethod=btClass.getMethod("removeBond");
BooleanreturnValue=(Boolean)removeBondMethod.invoke(btDevice);
returnreturnValue.booleanValue();
}
/**
*
*@paramclsShow
*/
staticpublicvoidprintAllInform(ClassclsShow)
{
try{
//取得所有方法
Method[]hideMethod=clsShow.getMethods();
inti=0;
for(;i Log.e("methodname",hideMethod[i].getName());
}
//取得所有常量
Field[]allFields=clsShow.getFields();
for(i=0;i Log.e("Fieldname",allFields[i].getName());
}
}catch(SecurityExceptione){
//thrownewRuntimeException(e.getMessage());
e.printStackTrace();
}catch(IllegalArgumentExceptione){
//thrownewRuntimeException(e.getMessage());
e.printStackTrace();
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
packagecom.testReflect;
importjava.util.ArrayList;
importjava.util.List;
importandroid.app.Activity;
importandroid.bluetooth.BluetoothAdapter;
importandroid.bluetooth.BluetoothDevice;
importandroid.content.BroadcastReceiver;
importandroid.content.Context;
importandroid.content.Intent;
importandroid.content.IntentFilter;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.AdapterView;
importandroid.widget.ArrayAdapter;
importandroid.widget.Button;
importandroid.widget.ListView;
importandroid.widget.Toast;
publicclasstestReflectextendsActivity{
ButtonbtnSearch,btnShow;
ListViewlvBTDevices;
ArrayAdapteradtDevices;
ListlstDevices=newArrayList();
BluetoothDevicebtDevice;
BluetoothAdapterbtAdapt;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnSearch=(Button)this.findViewById(R.id.btnSearch);
btnSearch.setOnClickListener(newClickEvent());
btnShow=(Button)this.findViewById(R.id.btnShow);
btnShow.setOnClickListener(newClickEvent());
lvBTDevices=(ListView)this.findViewById(R.id.ListView01);
adtDevices=newArrayAdapter(testReflect.this,
android.R.layout.simple_list_item_1,lstDevices);
lvBTDevices.setAdapter(adtDevices);
lvBTDevices.setOnItemClickListener(newItemClickEvent());
btAdapt=BluetoothAdapter.getDefaultAdapter();//初始化本机蓝牙功能
if(btAdapt.getState()==BluetoothAdapter.STATE_OFF)//开蓝牙
btAdapt.enable();
//注册Receiver来获取蓝牙设备相关的结果
IntentFilterintent=newIntentFilter();
intent.addAction(BluetoothDevice.ACTION_FOUND);
intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(searchDevices,intent);
}
privateBroadcastReceiversearchDevices=newBroadcastReceiver(){
publicvoidonReceive(Contextcontext,Intentintent){
Stringaction=intent.getAction();
Bundleb=intent.getExtras();
Object[]lstName=b.keySet().toArray();
//显示所有收到的消息及其细节
for(inti=0;i StringkeyName=lstName[i].toString();
Log.e(keyName,String.valueOf(b.get(keyName)));
}
//搜索设备时,取得设备的MAC地址
if(BluetoothDevice.ACTION_FOUND.equals(action)){
BluetoothDevicedevice=intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device.getBondState()==BluetoothDevice.BOND_NONE){
Stringstr="未配对|"+device.getName()+"|"+device.getAddress();
lstDevices.add(str);//获取设备名称和mac地址
adtDevices.notifyDataSetChanged();
}
}
}
};
classItemClickEventimplementsAdapterView.OnItemClickListener{
@Override
publicvoidonItemClick(AdapterViewarg0,Viewarg1,intarg2,
longarg3){
btAdapt.cancelDiscovery();
Stringstr=lstDevices.get(arg2);
String[]values=str.split("//|");
Stringaddress=values[2];
btDevice=btAdapt.getRemoteDevice(address);
try{
if(values[0].equals("未配对"))
{
Toast.makeText(testReflect.this,"由未配对转为已配对",500).show();
ClsUtils.createBond(btDevice.getClass(),btDevice);
}
elseif(values[0].equals("已配对"))
{
Toast.makeText(testReflect.this,"由已配对转为未配对",500).show();
ClsUtils.removeBond(btDevice.getClass(),btDevice);
}
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
/**
*按键处理
*@authorGV
*
*/
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnSearch){//搜索附近的蓝牙设备
lstDevices.clear();
Object[]lstDevice=btAdapt.getBondedDevices().toArray();
for(inti=0;i BluetoothDevicedevice=(BluetoothDevice)lstDevice[i];
Stringstr="已配对|"+device.getName()+"|"+device.getAddress();
lstDevices.add(str);//获取设备名称和mac地址
adtDevices.notifyDataSetChanged();
}
//开始搜索
setTitle("本机蓝牙地址:"+btAdapt.getAddress());
btAdapt.startDiscovery();
}
elseif(v==btnShow){//显示BluetoothDevice的所有方法和常量,包括隐藏API
ClsUtils.printAllInform(btDevice.getClass());
}
}
}
}
本文程序演示了以下功能:
1.所有来电自动接听;
2.所有来电自动挂断;
3.开启/关闭Radio;
4.开启/关闭数据连接(WAPorNET的连接)。
调用TelephonyManager的隐藏API是先参考Framework的/base/telephony/java/com/android/internal/telephony/ITelephony.aidl,然后自己实现一个ITelephony.aidl,最后在TelephonyManager中通过反射机制实例化自定义的ITelephony,实例化之后就可以调用ITelephony里面的函数了。
本文程序需要在AndroidManifest.xml添加以下两行代码,以获得权限:
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_width="fill_parent"android:id="@+id/rGrpSelect">
android:layout_width="fill_parent"andro
id:id="@+id/rbtnAutoAccept"
android:text="所有来电自动接听">
android:layout_width="fill_parent"android:id="@+id/rbtnAutoReject"
android:text="所有来电自动挂断">
android:layout_width="fill_parent"android:id="@+id/tbtnRadioSwitch"
android:textOn="Radio已经启动"android:textOff="Radio已经关闭"
android:textSize="24dip"android:textStyle="normal">
android:layout_width="fill_parent"android:id="@+id/tbtnDataConn"
android:textSize="24dip"android:textStyle="normal"android:textOn="允许数据连接"
android:textOff="禁止数据连接">
packagecom.testTelephony;
importjava.lang.reflect.Field;
importjava.lang.reflect.Method;
importcom.android.internal.telephony.ITelephony;
importandroid.telephony.TelephonyManager;
importandroid.util.Log;
publicclassPhoneUtils{
/**
*从TelephonyManager中实例化ITelephony,并返回
*/
staticpublicITelephonygetITelephony(TelephonyManagertelMgr)throwsException{
MethodgetITelephonyMethod=telMgr.getClass().getDeclaredMethod("getITelephony");
getITelephonyMethod.setAccessible(true);//私有化函数也能使用
return(ITelephony)getITelephonyMethod.invoke(telMgr);
}
staticpublicvoidprintAllInform(ClassclsShow){
try{
//取得所有方法
Method[]hideMethod=clsShow.getDeclaredMethods();
inti=0;
for(;i Log.e("methodname",hideMethod[i].getName());
}
//取得所有常量
Field[]allFields=clsShow.getFields();
for(i=0;i Log.e("Fieldname",allFields[i].getName());
}
}catch(SecurityExceptione){
//thrownewRuntimeException(e.getMessage());
e.printStackTrace();
}catch(IllegalArgumentExceptione){
//thrownewRuntimeException(e.getMessage());
e.printStackTrace();
}catch(Exceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
packagecom.testTelephony;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.telephony.PhoneStateListener;
importandroid.telephony.TelephonyManager;
importandroid.util.Log;
importandroid.view.View;
importandroid.wi
dget.RadioGroup;
importandroid.widget.ToggleButton;
publicclasstestTelephonyextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
RadioGrouprg;//来电操作单选框
ToggleButtontbtnRadioSwitch;//Radio开关
ToggleButtontbtnDataConn;//数据连接的开关
TelephonyManagertelMgr;
CallStateListenerstateListner;
intcheckedId=0;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
telMgr=(TelephonyManager)getSystemService(TELEPHONY_SERVICE);
telMgr.listen(newCallStateListener(),CallStateListener.LISTEN_CALL_STATE);
PhoneUtils.printAllInform(TelephonyManager.class);
rg=(RadioGroup)findViewById(R.id.rGrpSelect);
rg.setOnCheckedChangeListener(newCheckEvent());
tbtnRadioSwitch=(ToggleButton)this.findViewById(R.id.tbtnRadioSwitch);
tbtnRadioSwitch.setOnClickListener(newClickEvent());
try{
tbtnRadioSwitch.setChecked(PhoneUtils.getITelephony(telMgr).isRadioOn());
}catch(Exceptione){
Log.e("error",e.getMessage());
}
tbtnDataConn=(ToggleButton)this.findViewById(R.id.tbtnDataConn);
tbtnDataConn.setOnClickListener(newClickEvent());
try{
tbtnDataConn.setChecked(PhoneUtils.getITelephony(telMgr).isDataConnectivityPossible());
}catch(Exceptione){
Log.e("error",e.getMessage());
}
}
/**
*来电时的操作
*@authorGV
*
*/
publicclassCheckEventimplementsRadioGroup.OnCheckedChangeListener{
@Override
publicvoidonCheckedChanged(RadioGroupgroup,intcheckedId){
testTelephony.this.checkedId=checkedId;
}
}
/**
*Radio和数据连接的开关
*@authorGV
*
*/
publicclassClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==tbtnRadioSwitch){
try{
PhoneUtils.getITelephony(telMgr).setRadio(tbtnRadioSwitch.isChecked());
}catch(Exceptione){
Log.e("error",e.getMessage());
}
}
elseif(v==tbtnDataConn){
try{
if(
tbtnDataConn.isChecked())
PhoneUtils.getITelephony(telMgr).enableDataConnectivity();
elseif(!tbtnDataConn.isChecked())
PhoneUtils.getITelephony(telMgr).disableDataConnectivity();
}catch(Exceptione){
Log.e("error",e.getMessage());
}
}
}
}
/**
*监视电话状态
*@authorGV
*
*/
publicclassCallStateListenerextendsPhoneStateListener{
@Override
publicvoidonCallStateChanged(intstate,StringincomingNumber){
if(state==TelephonyManager.CALL_STATE_IDLE)//挂断
{
Log.e("IDLE",incomingNumber);
}
elseif(state==TelephonyManager.CALL_STATE_OFFHOOK)//接听
{
Log.e("OFFHOOK",incomingNumber);
}
elseif(state==TelephonyManager.CALL_STATE_RINGING)//来电
{
if(testTelephony.this.checkedId==R.id.rbtnAutoAccept)
{
try{
//需要
PhoneUtils.getITelephony(telMgr).silenceRinger();//静铃
PhoneUtils.getITelephony(telMgr).answerRingingCall();//自动接听
}catch(Exceptione){
Log.e("error",e.getMessage());
}
}
elseif(testTelephony.this.checkedId==R.id.rbtnAutoReject)
{
try{
PhoneUtils.getITelephony(telMgr).endCall();//挂断
PhoneUtils.getITelephony(telMgr).cancelMissedCallsNotification();//取消未接显示
}catch(Exceptione){
Log.e("error",e.getMessage());
}
}
}
super.onCallStateChanged(state,incomingNumber);
}
}
}
本帖最后由aishu于2012-3-1021:21编辑
说说如何用ListView实现自适应的表格。GridView比ListView更容易实现自适应的表
格,但是GridView每个格单元的大小固定,而ListView实现的表格可以自定义每个格单元的大小,但因此实现自适应表格也会复杂些(格单元大小不一)。另外,GridView实现的表格可以定位在具体某个格单元,而ListView实现的表格则只能定位在表格行。因此还是那句老话:根据具体的使用环境而选择GridView或者ListView实现表格
本文实现的ListView表格,可以每个格单元大小不一,文本(TextView)或图片(ImageView)做格单元的数据,不需要预先定义XML实现样式(自适应的根本目标)。由于ListView置于HorizontalScrollView中,因此对于列比较多/列数据比较长的数据表也能很好地适应其宽度。main.xml源码如下
android:orientation="vertical"android:layout_width="fill_parent"
android:layout_height="fill_parent">
android:layout_height="fill_parent"android:layout_width="fill_parent">
android:layout_width="wrap_content">
packagecom.testMyListView;
importjava.util.ArrayList;
importcom.testMyListView.TableAdapter.TableCell;
importcom.testMyListView.TableAdapter.TableRow;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.AdapterView;
importandroid.widget.ListView;
importandroid.widget.LinearLayout.LayoutParams;
importandroid.widget.Toast;
/**
*@authorhellogv
*/
publicclasstestMyListViewextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ListViewlv;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.setTitle("ListView自适应实现表格---hellogv");
lv=(ListView)this.findViewById(R.id.ListView01);
ArrayList[table]
table=newArrayList[table]
();
TableCell[]titles=newTableCell[5];//每行5个单元
intwidth=this.getWindowManager().getDefaultDisplay().getWidth()/titles.length;
//定义标题
for(inti=0;i titles[i]=newTableCell("标题"+String.valueOf(i),
width+8*i,
LayoutParams.FILL_PARENT,
TableCell.STRING);
}
table.add(newTableRow(titles));
//每行的数据
TableCell[]cells=newTableCell[5];//每行5个单元
for(inti=0;i cells[i]=newTableCell("No."+String.valueOf(i),
titles[i].width,
LayoutParams.FILL_PARENT,
TableCell.STRING);
}
cells[cells.length-1]=newTableCell(R.drawable.icon,
titles[cells.length-1].width,
LayoutParams.WRAP_CONTENT,
TableCell.IMAGE);
//把表格的行添加到表格
for(inti=0;i<12;i++)
table.add(newTableRow(cells));
TableAdaptertableAdapter=newTableAdapter(this,table);
lv.setAdapter(tableAdapter);
lv.setOnItemClickListener(newItemClickEvent());
}
classItemClickEventimplementsAdapterView.OnItemClickListener{
@Override
publicvoidonItemClick(AdapterViewarg0,Viewarg1,intarg2,
longarg3){
Toast.makeText(testMyListView.this,"选中第"+String.valueOf(arg2)+"行",500).show();
}
}
}
ListView自适应实现Table的类TableAdapter.java代码如下:PS:TableCell是格单元的类,TableRow是表格行的类,TableRowView是实现表格行的组件。实现步骤:TableCell-->TableRow(TableRowView)-->ListView
packagecom.testMyListView;
importjava.util.List;
importandroid.content.Context;
importandroid.graphics.Color;
importandroid.view.Gravity;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.BaseAdapter;
importandroid.widget.ImageView;
importandroid.widget.LinearLayout;
importandroid.widget.TextView;
publicclassTableAdapterextendsBaseAdapter{
privateContextcontext;
privateList[table]
table;
publicTableAdapter(Contextcontext,List[table]
table){
this.context=context;
this.table=table;
}
@Override
publicintgetCount(){
returntable.size();
}
@Override
publiclonggetItemId(intposition){
returnposition;
}
publicTableRowgetItem(intposition){
returntable.get(position);
}
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
Tabl
eRowtableRow=table.get(position);
returnnewTableRowView(this.context,tableRow);
}
/**
*TableRowView实现表格行的样式
*@authorhellogv
*/
classTableRowViewextendsLinearLayout{
publicTableRowView(Contextcontext,TableRowtableRow){
super(context);
this.setOrientation(LinearLayout.HORIZONTAL);
for(inti=0;i TableCelltableCell=tableRow.getCellValue(i);
LinearLayout.LayoutParamslayoutParams=newLinearLayout.LayoutParams(
tableCell.width,tableCell.height);//按照格单元指定的大小设置空间
layoutParams.setMargins(0,0,1,1);//预留空隙制造边框
if(tableCell.type==TableCell.STRING){//如果格单元是文本内容
TextViewtextCell=newTextView(context);
textCell.setLines(1);
textCell.setGravity(Gravity.CENTER);
textCell.setBackgroundColor(Color.BLACK);//背景黑色
textCell.setText(String.valueOf(tableCell.value));
addView(textCell,layoutParams);
}elseif(tableCell.type==TableCell.IMAGE){//如果格单元是图像内容
ImageViewimgCell=newImageView(context);
imgCell.setBackgroundColor(Color.BLACK);//背景黑色
imgCell.setImageResource((Integer)tableCell.value);
addView(imgCell,layoutParams);
}
}
this.setBackgroundColor(Color.WHITE);//背景白色,利用空隙来实现边框
}
}
/**
*TableRow实现表格的行
*@authorhellogv
*/
staticpublicclassTableRow{
privateTableCell[]cell;
publicTableRow(TableCell[]cell){
this.cell=cell;
}
publicintgetSize(){
returncell.length;
}
publicTableCellgetCellValue(intindex){
if(index>=cell.length)
returnnull;
returncell[index];
}
}
/**
*TableCell实现表格的格单元
*@authorhellogv
*/
staticpublic
classTableCell{
staticpublicfinalintSTRING=0;
staticpublicfinalintIMAGE=1;
publicObjectvalue;
publicintwidth;
publicintheight;
privateinttype;
publicTableCell(Objectvalue,intwidth,intheight,inttype){
this.value=value;
this.width=width;
this.height=height;
this.type=type;
}
}
}
Android提高十六篇之使用NDK把彩图转换灰度图
在Android上使用JAVA实现彩图转换为灰度图,跟J2ME上的实现类似,不过遇到频繁地转换或者是大图转换时,就必须使用NDK来提高速度了。本文主要通过JAVA和NDK这两种方式来分别实现彩图转换为灰度图,并给出速度的对比。
先来简单地介绍一下Android的NDK使用步骤:
以NDKr4为例,或许以后新版的NDK的使用方法略有不同。
1、下载支持C++的android-ndk-r4-crystax,支持C++的话可玩性更强......
2、下载cygwin,选择ftp://mirrors.kernel.org这个镜像,搜索DevelInstall安装gcc和make等工具;
在搜索框里分别搜索gcc和make,必须是DevelInstall栏的。
3、Cygwin安装目录下,找到home/username的目录下的.bash_profile文件,打开文件在最后加上:
NDK=/cygdrive/d:cygwin/android-ndk-r4-crystax
exportNDK
PS:假设安装在D:/cygwin/android-ndk-r4-crystax。
4、运行cygwin,通过cd命令去到NDK/samples/例子目录/,运行$NDK/ndk-build来编译该目录下的Android.mk
以下是个人习惯.......
5、安装Eclipse的CDT,官方下载cdt安装包,解压缩后把plugins和feagures复制覆盖到eclipse文件夹下即可
6、去到系统属性->环境变量->Path添加"D:/cygwin/bin"(假设cygwin安装在D:下)和"D:/cygwin/android-ndk-r4-crystax",重启计算机,然后就可以在Eclipse里面建立基于cygwin的C/C++工程了,先通过这一步来验证NDK的程序能够编译成功,然后再通过第4步来生成SO文件。
-
packagecom.testToGray;
importandroid.app.Activity;
importandroid.graphics.Bitmap;
importandroid.graphics.Bitmap.Config;
importandroid.graphics.d
rawable.BitmapDrawable;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.widget.Button;
importandroid.widget.ImageView;
publicclasstestToGrayextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
ButtonbtnJAVA,btnNDK;
ImageViewimgView;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.setTitle("使用NDK转换灰度图---hellogv");
btnJAVA=(Button)this.findViewById(R.id.btnJAVA);
btnJAVA.setOnClickListener(newClickEvent());
btnNDK=(Button)this.findViewById(R.id.btnNDK);
btnNDK.setOnClickListener(newClickEvent());
imgView=(ImageView)this.findViewById(R.id.ImageView01);
}
classClickEventimplementsView.OnClickListener{
@Override
publicvoidonClick(Viewv){
if(v==btnJAVA)
{
longcurrent=System.currentTimeMillis();
Bitmapimg=ConvertGrayImg(R.drawable.cat);
longperformance=System.currentTimeMillis()-current;
//显示灰度图
imgView.setImageBitmap(img);
testToGray.this.setTitle("w:"+String.valueOf(img.getWidth())+",h:"+String.valueOf(img.getHeight())
+"JAVA耗时"+String.valueOf(performance)+"毫秒");
}
elseif(v==btnNDK)
{
longcurrent=System.currentTimeMillis();
//先打开图像并读取像素
Bitmapimg1=((BitmapDrawable)getResources().getDrawable(R.drawable.cat)).getBitmap();
intw=img1.getWidth(),h=img1.getHeight();
int[]pix=newint[w*h];
img1.getPixels(pix,0,w,0,0,w,h);
//通过ImgToGray.so把彩色像素转为灰度像素
int[]resultInt=LibFuns.ImgToGray(pix,w,h);
BitmapresultImg=Bitmap.createBitmap(w,h,Config.RGB_565);
resultImg.setPixels(resultInt,0,w,0,0,w,h);
longperformance=System.currentTimeMillis()-current;
//显示灰度图
imgView.setImageBitmap(resultImg);
testToGray.this.setTitle("w:"+String.valueOf(img1.getWidth())+",h:"+String.valueOf(img1.getHeight())
+"NDK耗时"+String.valueOf(performance)+"毫秒");
}
}
}
/**
*把资源图片转为灰度图
*@paramresID资源ID
*@return
*/
publicBitmapConvertGrayImg(intresID)
{
Bitmapimg1=((BitmapDrawable)getResources().getDrawable(resID)).getBitmap();
intw=img1.getWidth(),h=img1.getHeight();
int[]pix=newint[w*h];
img1.getPixels(pix,0,w,0,0,w,h);
intalpha=0xFF<<24;
for(inti=0;i for(intj=0;j //获得像素的颜色
intcolor=pix[w*i+j];
intred=((color&;0x00FF0000)>>16);
intgreen=((color&;0x0000FF00)>>8);
intblue=color&;0x000000FF;
color=(red+green+blue)/3;
color=alpha|(color<<16)|(color<<8)|color;
pix[w*i+j]=color;
}
}
Bitmapresult=Bitmap.createBitmap(w,h,Config.RGB_565);
result.setPixels(pix,0,w,0,0,w,h);
returnresult;
}
}
封装NDK函数的JAVA类LibFuns.java的源码如下:
packagecom.testToGray;
publicclassLibFuns{
static{
System.loadLibrary("ImgToGray");
}
/**
*@paramwidththecurrentviewwidth
*@paramheightthecurrentviewheight
*/
publicstaticnativeint[]ImgToGray(int[]buf,intw,inth);
}
彩图转换为灰度图的ImgToGray.cpp源码:
#include
#include
#include
extern"C"{
JNIEXPORTjintArrayJNICALLJava_com_testToGray_LibFuns_ImgToGray(
JNIEnv*env,jobjectobj,jintArraybuf,intw,inth);
}
;
JNIEXPORTjintArrayJNICALLJava_com_testToGray_LibFuns_ImgToGray(
JNIEnv*env,jobjectobj,jintArraybuf,intw,inth){
jint*cbuf;
cbuf=env->GetIntArrayElements(buf,false);
if(cbuf==NULL){
return0;/*exceptionoccurred*/
}
intalpha=0xFF<<24;
for(inti=0;i for(intj=0;j //获得像素的颜色
intcolor=cbuf[w*i+j];
intred=((color&;0x00FF0000)>>16);
intgreen=((color&;0x0000FF00)>>8);
intblue=color&;0x000000FF;
color=(red+green+blue)/3;
color=alpha|(color<<16)|(color<<8)|color;
cbuf[w*i+j]=color;
}
}
intsize=w*h;
jintArrayresult=env->NewIntArray(size);
env->SetIntArrayRegion(result,0,size,cbuf);
env->ReleaseIntArrayElements(buf,cbuf,0);
returnresult;
}
Android.mk的源码:
LOCAL_PATH:=$(callmy-dir)
include$(CLEAR_VARS)
LOCAL_MODULE:=ImgToGray
LOCAL_SRC_FILES:=ImgToGray.cpp
include$(
BUILD_SHARED_LIBRARY)
Android提高第二十篇之MediaPlayer播放网络音频
本文主要实现MediaPlayer在线播放音频的功能,由于在线视频播放比在线音频播放复杂,因此先介绍在线音频播放的实现,这样可以帮助大家逐步深入了解MediaPlayer的在线播放功能。
android:layout_height="fill_parent"android:layout_width="fill_parent">
android:layout_width="fill_parent"android:orientation="vertical"
android:layout_gravity="top">
android:layout_gravity="center_horizontal"android:layout_marginTop="4.0dip"
android:layout_height="wrap_content"android:layout_width="wrap_content">
android:layout_height="wrap_content"android:id="@+id/btnPlayUrl"
android:text="播放网络音频">
android:text="暂停"android:layout_width="80dip">
android:layout_width="80dip"android:text="停止"android:id="@+id/btnStop">
android:layout_width="fill_parent"android:layout_height="wrap_content"
android:layout_marginBottom="20dip">
android:paddingLeft="10dip"android:layout_weight="1.0"
android:layout_height="wrap_content"android:layout_width="wrap_content"
android:id="@+id/skbProgress"android:max="100">
Player.java是本文的核心,Player.java实现了“进度条更新”、“数据缓冲”等功能,虽然不是很复杂的功能,但却是非常有用的功能。Player.java源码如下:
packagecom.musicplayer;
importjava.io.IOException;
importjava.util.Timer;
importjava.util.TimerTask;
importandroid.media.AudioManager;
importandroid.media.MediaPlayer;
importandroid.media.MediaPlayer.OnBufferingUpdateListener;
importandroid.media.MediaPlayer.OnCompletionListener;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.util.Log;
importandroid.widget.SeekBar;
publicclassPlayerimplementsOnBufferingUpdateListener,
OnCompletionListener,MediaPlayer.OnPreparedListener{
publicMediaPlayermediaPlayer;
privateSeekBarskbP
rogress;
privateTimermTimer=newTimer();
publicPlayer(SeekBarskbProgress)
{
this.skbProgress=skbProgress;
try{
mediaPlayer=newMediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
}catch(Exceptione){
Log.e("mediaPlayer","error",e);
}
mTimer.schedule(mTimerTask,0,1000);
}
/*******************************************************
*通过定时器和Handler来更新进度条
******************************************************/
TimerTaskmTimerTask=newTimerTask(){
@Override
publicvoidrun(){
if(mediaPlayer==null)
return;
if(mediaPlayer.isPlaying()&;&;skbProgress.isPressed()==false){
handleProgress.sendEmptyMessage(0);
}
}
};
HandlerhandleProgress=newHandler(){
publicvoidhandleMessage(Messagemsg){
intposition=mediaPlayer.getCurrentPosition();
intduration=mediaPlayer.getDuration();
if(duration>0){
longpos=skbProgress.getMax()*position/duration;
skbProgress.setProgress((int)pos);
}
};
};
//*****************************************************
publicvoidplay()
{
mediaPlayer.start();
}
publicvoidplayUrl(StringvideoUrl)
{
try{
mediaPlayer.reset();
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.prepare();//prepare之后自动播放
//mediaPlayer.start();
}catch(IllegalArgumentExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IllegalStateExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
publicvoidpause()
{
mediaPlayer.pause();
}
publicvoidstop()
{
if
(mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
}
@Override
/**
*通过onPrepared播放
*/
publicvoidonPrepared(MediaPlayerarg0){
arg0.start();
Log.e("mediaPlayer","onPrepared");
}
@Override
publicvoidonCompletion(MediaPlayerarg0){
Log.e("mediaPlayer","onCompletion");
}
@Override
publicvoidonBufferingUpdate(MediaPlayerarg0,intbufferingPro, gress){
skbProgress.setSecondaryProgress(bufferingProgress);
intcurrentProgress=skbProgress.getMax()*mediaPlayer.getCurrentPosition()/mediaPlayer.getDuration();
Log.e(currentProgress+"%play",bufferingProgress+"%buffer");
}
}
test_musicplayer.java是主程序,负责调用Player类,其中关键部分是SeekBarChangeEvent这个SeekBar拖动的事件:SeekBar的Progress是0~SeekBar.getMax()之内的数,而MediaPlayer.seekTo()的参数是0~MediaPlayer.getDuration()之内数,所以MediaPlayer.seekTo()的参数是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()。test_musicplayer.java源码如下:
packagecom.musicplayer;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importandroid.widget.SeekBar;
publicclasstest_musicplayerextendsActivity{
privateButtonbtnPause,btnPlayUrl,btnStop;
privateSeekBarskbProgress;
privatePlayerplayer;
/**Calledwhentheactivityisfirstcreated.*/
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.setTitle("在线音乐播放---hellogv编写");
btnPlayUrl=(Button)this.findViewById(R.id.btnPlayUrl);
btnPlayUrl.setOnClickListener(newClickEvent());
btnPause=(Button)this.findViewById(R.id.btnPause);
btnPause.setOnClickListener(newClickEvent());
btnStop=(Button)this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(newClickEvent());
skbProgress=(SeekBar)this.findViewById(R.id.skbProgress);
skbProgress.setOnSeekBarChangeListener(newSeekBarChangeEvent());
player=newPlayer(skbProgress);
}
classClickEventimplementsOnClickListener{
@Override
publicvoidonClick(Viewarg0){
if(arg0==btnPause){
player.pause();
}elseif(arg0==btnPlayUrl){
/
/在百度MP3里随便搜索到的,大家可以试试别的链接
Stringurl="219.138.125.22/myweb/mp3/CMP3/JH19.MP3";
player.playUrl(url);
}elseif(arg0==btnStop){
player.stop();
}
}
}
classSeekBarChangeEventimplementsSeekBar.OnSeekBarChangeListener{
intprogress;
@Override
publicvoidonProgressChanged(SeekBarseekBar,intprogress,
booleanfromUser){
//原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
this.progress=progress*player.mediaPlayer.getDuration()
/seekBar.getMax();
}
@Override
publicvoidonStartTrackingTouch(SeekBarseekBar){
}
@Override
publicvoidonStopTrackingTouch(SeekBarseekBar){
//seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
player.mediaPlayer.seekTo(progress);
}
}
}
播放网络视频比播放网络音频多需要一个SurfaceView而已,已经熟悉MediaPlayer播放网络音频之后,相信大家对播放网络视频也能很快地掌握。
android:layout_height="fill_parent"android:layout_width="fill_parent">
android:layout_height="fill_parent"android:layout_width="fill_parent">
android:layout_width="fill_parent"android:layout_gravity="bottom"
android:orientation="vertical">
android:layout_gravity="center_horizontal"android:layout_marginTop="4.0dip"
android:layout_height="wrap_content"android:layout_width="wrap_content">
android:layout_height="wrap_content"android:id="@+id/btnPlayUrl"
android:text="播放网络视频">
android:text="暂停"android:layout_width="80dip">
android:layout_width="80dip"android:text="停止"android:id="@+id/btnStop">
andro
id:layout_width="fill_parent"android:layout_height="wrap_content"
android:layout_marginBottom="20dip">
android:paddingLeft="10dip"android:layout_weight="1.0"
android:layout_height="wrap_content"android:layout_width="wrap_content"
android:id="@+id/skbProgress"android:max="100">
Player.java是本文的核心,Player.java实现了“进度条更新”、“数据缓冲”、“SurfaceHolder生命周期”等功能,其中“SurfaceHolder生命周期”是视频与音频播放的最大区别,通过surfaceCreated()、surfaceDestroyed()、surfaceChanged()可以创建/释放某些资源。下面这个地方需要注意一下:
videoWidth=mediaPlayer.getVideoWidth();
videoHeight=mediaPlayer.getVideoHeight();
if(videoHeight!=0&;&;videoWidth!=0){
arg0.start();
}
有些视频是android播放器不能播放的,不能播放时videoHeight=0,videoWidth=0,以此来判断是否播放视频。Player.java源码如下
packagecom.videoplayer;
importjava.io.IOException;
importjava.util.Timer;
importjava.util.TimerTask;
importandroid.media.AudioManager;
importandroid.media.MediaPlayer;
importandroid.media.MediaPlayer.OnBufferingUpdateListener;
importandroid.media.MediaPlayer.OnCompletionListener;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.util.Log;
importandroid.view.SurfaceHolder;
importandroid.view.SurfaceView;
importandroid.widget.SeekBar;
publicclassPlayerimplementsOnBufferingUpdateListener,
OnCompletionListener,MediaPlayer.OnPreparedListener,
SurfaceHolder.Callback{
privateintvideoWidth;
privateintvideoHeight;
publicMediaPlayermediaPlayer;
privateSurfaceHoldersurfaceHolder;
privateSeekBarskbProgress;
privateTimermTimer=newTimer();
publicPlayer(SurfaceViewsurfaceView,SeekBarskbProgress)
{
this.skbProgress=skbProgress;
surfaceHolder=surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mTimer.schedule(mTimerTask,0,1000);
}
/*******************************************************
*通过定时器和Handler来更新进度条
******************************************************/
TimerTaskmTimerTask=newTimerTask(){
@Override
publicvoidrun(){
if(mediaPlayer==null)
return;
if(mediaPlayer.isPlaying()&;&;
skbProgress.isPressed()==false){
handleProgress.sendEmptyMessage(0);
}
}
};
HandlerhandleProgress=newHandler(){
publicvoidhandleMessage(Messagemsg){
intposition=mediaPlayer.getCurrentPosition();
intduration=mediaPlayer.getDuration();
if(duration>0){
longpos=skbProgress.getMax()*position/duration;
skbProgress.setProgress((int)pos);
}
};
};
//*****************************************************
publicvoidplay()
{
mediaPlayer.start();
}
publicvoidplayUrl(StringvideoUrl)
{
try{
mediaPlayer.reset();
mediaPlayer.setDataSource(videoUrl);
mediaPlayer.prepare();//prepare之后自动播放
//mediaPlayer.start();
}catch(IllegalArgumentExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IllegalStateExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}catch(IOExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
publicvoidpause()
{
mediaPlayer.pause();
}
publicvoidstop()
{
if(mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
}
@Override
publicvoidsurfaceChanged(SurfaceHolderarg0,intarg1,intarg2,intarg3){
Log.e("mediaPlayer","surfacechanged");
}
@Override
publicvoidsurfaceCreated(SurfaceHolderarg0){
try{
mediaPlayer=newMediaPlayer();
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnPreparedListener(this);
}catch(Exceptione){
Log.e("mediaPlayer","error",e);
}
Log.e("mediaPlayer","surfacecreated");
}
@Override
publicvoidsurfaceDestroyed(SurfaceHolderarg0){
Log.e("mediaPlayer","surfacedestroyed");
}
@Override
/**
*通过onPrepared播放
*/
publicvoidonPrepared(MediaPlayerarg0){
videoWidth=mediaPlayer.getVideoWidth();
videoHeight=mediaPlayer.getVideoHeight();
if(videoHeight!=0&;&;videoWidth!=0){
arg0.start();
}
Log.e("mediaPlayer","onPrepared");
}
@Override
publicvoidonCompletion(MediaPlayerarg0){
//TODOAuto-generatedmethodstub
}
@Override
publicvoidonBufferingUpdate(MediaPlayerarg0,intbufferingProgress){
skbProgress.setSecondaryProgress(bufferingProgress);
intcurrentProgress=skbProgress.getMax()*mediaPlayer.getCurrentPosition()/mediaPlayer.getDuration();
Log.e(currentProgress+"%play",bufferingProgress+"%buffer");
}
}
test_videoplayer.java是主程序,负责调用Player类,其中关键部分是SeekBarChangeEvent这个SeekBar拖动的事件:SeekBar的Progress是0~SeekBar.getMax()之内的数,而MediaPlayer.seekTo()的参数是0~MediaPlayer.getDuration()之内数,所以MediaPlayer.seekTo()的参数是(progress/seekBar.getMax())*MediaPlayer.getDuration()。test_videoplayer.java源码如下:
packagecom.videoplayer;
importandroid.app.Activity;
importandroid.content.pm.ActivityInfo;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.SurfaceView;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importandroid.widget.SeekBar;
publicclasstest_videoplayerextendsActivity{
privateSurfaceViewsurfaceView;
privateButtonbtnPause,btnPlayUrl,btnStop;
privateSeekBarskbProgress;
privatePlayerplayer;
/**Calledwhentheactivityisfirstcreated.*/
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
surfaceView=(SurfaceView)this.findViewById(R.id.surfaceView1);
btnPlayUrl=(Button)this.findViewById(R.id.btnPlayUrl);
btnPlayUrl.setOnClickListener(newClickEvent());
btnPause=(Button)this.findViewById(R.id.btnPause);
btnPause.setOnClickListener(newClickEvent());
btnStop=(Button)this.findViewById(R.id.btnStop);
btnStop.setOnClickListener(newClickEvent());
skbProgress=(SeekBar)this.findViewById(R.id.skbProgress);
skbProgress.setOnSeekBarChangeListener(newSeekBarChangeEvent());
player=newPlayer(surfaceView,s
kbProgress);
}
classClickEventimplementsOnClickListener{
@Override
publicvoidonClick(Viewarg0){
if(arg0==btnPause){
player.pause();
}elseif(arg0==btnPlayUrl){
Stringurl="daily3gp/vids/family_guy_penis_car.3gp";
player.playUrl(url);
}elseif(arg0==btnStop){
player.stop();
}
}
}
classSeekBarChangeEventimplementsSeekBar.OnSeekBarChangeListener{
intprogress;
@Override
publicvoidonProgressChanged(SeekBarseekBar,intprogress,
booleanfromUser){
//原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
this.progress=progress*player.mediaPlayer.getDuration()
/seekBar.getMax();
}
@Override
publicvoidonStartTrackingTouch(SeekBarseekBar){
}
@Override
publicvoidonStopTrackingTouch(SeekBarseekBar){
//seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
player.mediaPlayer.seekTo(progress);
}
}
}