zoukankan      html  css  js  c++  java
  • Android开发视频教学_读书笔记

    相关链接

    视频下载地址:http://www.verycd.com/topics/2837883/

    源码下载地址:http://www.mars-droid.com/bbs/forum.php?mod=viewthread&tid=13&extra=page%3D1

    附上本人练习用的Mp3Player源码:https://files.cnblogs.com/vipcjob/Mp3Player.rar

    05 ActivityIntent

     

    新建一个layout(file),命名为other.xml(文件名小写),加入一个TextView,加ID属性,android:id=”@+id/myTextView”

    新建一个java文件(类),命名为OtherActivity,继承extends Activity.

    重写onCreate方法,右键,source-override-onCreate。加载对应的样式文件,setContentView(R.layout.other)。定义成员变量,private Textview myTextView=null;

    找出TextView, myTextView = (TextView)findViewById(R.id.myTextvew);。

    注册Activity。AndroidMainfest.xml中加入<activity android:name=".secondActivity" android:label="@string/app_name"></activity>

    原Activity跳转:

    mainButton.setOnClickListener(otherListener);

    private OnClickListener otherListener = new OnClickListener() {

           @override

           public void onClick(View v){

                  Intent intent = new Intent();

                  intent.setClass(FirstActivity.this,SencondActivity.class);

                  startActivity(intent);

    }

    }

    //监听器另一种写法,定义内部类(内部类可以调用外部的成员变量)

    class MyButtonListener implements OnClickListener{ @Override … (同上)}

    绑定btn: btn.setOnClickListener(new MyButtonListener ());

    实现跳转的基础上,加入数据传递:intent加入键值对

    intent.putExtra(“testKey”,”testValue”);

    在目标activity中取出:

    Intent intent = getIntent();

    String value = intent.getStringExtra(“testKey”);

    另一种写法:

    Bundle bun = intent.getExtras();

    String value = bun.getString(“testKey”);

    Intent跳转的不一定是一个Activity,甚至不是同一个应用程序里的东西

    Uri uri = Uri.parse(“smsto://0800000123”); //Android自带的发短信程序

    Intnet intent = new Intent(Intetn.ACTION_SENDTO,uri);

    intnet.putExtra(“sms_body”,”sms_text”);

    startActivity(intent);

    06 常用控件

    导入命名空间的快捷键是:ctrl + shift + o。import …

    EditText,TextView,Button

    xxx = (Button)findViewbyId(R.id.xxx);

    xxx.setText(“controlname”);

    整型和字符串转化:

    int intOne = Integer.parseInt(strOne);

    String strOne = intOne + “”;

    点击menu按钮时的事件:重写事件onCreateOptionsMenu。

    为menu加上菜单按钮:         

    menu.add(0,1,1,R.string.exit);

    menu.add(0,2,2,R.string.about);

    为按钮添加事件:重写事件:onOptionsItemSelected

    判断事件处理:

    if(item.getItemId()==1){ finish(); } //itemid为1的按钮事件是finish(),退出。

    07 Activity的生命周期

    记录日志的方法:在函数里加入:System.out.println(“msg”);

    测试时不起作用,再增加一行后正常: Log.e(“mytag”,”mymsg”);

    启动第一个Activity,

    FirstActivity:onCreate(创建),OnStart(开始,可见),OnResume(显示,获取焦点,可以点击,不被弹出窗遮挡)

    从第一个activity启动第二个Activity(非窗体,完全遮挡):

    FirstActivity:OnPause(暂停,保存数据),

    SecondActivity:OnCreate,OnStart,OnResume,

    FirstActivity:OnStop(完全被遮挡,如果第二个Activity是弹出框,即没有完全遮挡时,不会调用此方法)。

    从第二个Activity返回:

    SecondActivity:OnPause,

    FirstActivity:OnReStart,OnStart,OnResume,

    SecondActivity:OnStop,OnDestory(被销毁).

    再进第二个:

    FirstActivity:OnPause

    SecondActivity:OnCreate,OnStart,OnResume

    FirstActivity:OnStop

    08 Activity的生命周期2

    Task:Activity对象的栈结构(后进先出)

    跳转Activity时,进栈,点击back,出栈

    finish()函数会销毁Activity

    在AndroidManifest.xml中声明Activity时,加入属性:

    android:theme=”@android:style/Theme.Dialog”,此Activity会变成窗口模式。

    导入源代码:右键,import,Genderal-Existing Projects into workspace,选择文件夹

    09Activity布局初步1

    智能提示快捷键:alt+/

    LinearLayout.xml

    android:orientation:水平布局或垂(tga)直

    xml注释写法:<!-- …. -->

    android:gravity=”…” 制控此控件中的文本的位置(非此控件的位置)

    android:textsize=”35pt”

    android:background=”#aa0000”

    android:paddingxxx=”20dip” //比px更好地适应不同的屏幕

    android:layout_weight="2" //布局比例值,对于值为1的控件(同一父控件),此控件高度(或宽度)是它的两倍

    android:singleLine="true" //文字超出长度时会以…代替

    TableLayout.xml:

    android:stretchColumns="0" //当控件不足以填充行时,第1列(序号从0起)拉伸

    <TableRow><TextView…></TextView>…</TableRow> //简单布局,一个TextView相当于一个单元格

    10Activity布局初步2

    复杂布局的实现:LinearLayout和TableLayout嵌套使用。

    < LinearLayout xx布局>

    < LinearLayout 水平布局>…</ LinearLayout>

    < LinearLayout 垂直布局>

           < TableLayout>…</ TableLayout>

    </ LinearLayout>

    </ LinearLayout>

    11Activity布局初步3

    RelativeLayout.xml

    android:layout_ above ="@id/controlid" 将该控件的底部至于给定ID的控件( controlid )之上

    类似的:layout_below,layout_toLeftOf,layout_toRightOf。把此控件放到xxx的上/下/左/右去

    android:layout_alignBottom将该控件的底部边缘与给定ID控件的底部边缘

    类似的:layout_alignLeft,layout_alignRight,layout_alignTop。把它们两个向上/下/左/右对齐

    android:layout_alignParentLeft如果该值为true,则将该控件的左边与父控件的左边对齐

    类似的:layout_alignParentRight,layout_alignParentTop

    android:layout_centerHorizontal 如果值为真,该控件将被至于水平方向的中央

    android:layout_centerInParent 如果值为真,该控件将被至于父控件水平方向和垂直方向的中央

    android:layout_centerVertical 如果值为真,该控件将被至于垂直方向的中央

    12常用控件2

    RadioGroup,RadioButton,CheckBox,Toast(相当于一个alert的控件)

    <RadioGroup…><RadioButton…/><RadioButton…/></RadioGroup>

    单选定义点击事件,对Group添加:

    genderGroup = (RadioGroup)findViewById(R.id.genderGroup);

    femaleButton = (RadioButton)findViewById(R.id.femaleButton);

    genderGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {

    @Override

    public void onCheckedChanged(RadioGroup group, int checkedId) {

    if(femaleButton.getId() == checkedId){ …}

    }

    });

    为多选框添加事件,需要逐个添加

    cbOne.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

    @Override

    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

    if(isChecked){…}

           }

    });

    Toast.makeText(ThisActivityName.this, "alertmsg", Toast.LENGTH_SHORT).show(); //显示信息

    13常用控件3

    ProgressBar进度条:

    水平样式:style="?android:attr/progressBarStyleHorizontal",转圈样式:style="?android:attr/progressBarStyle"

    初始化是否可见:android:visibility="gone",程序运行可见:BarObj.setVisibility(View.VISIBLE);

    进度条android:max="200",总共有多少份进度。默认为100,程序设置:HorizontalBar.setMax(150);

    HorizontalBar.setProgress(i); HorizontalBar.setSecondaryProgress(i + 10); //设置主进度和第二进度的当前值

    ListView数据列表

    public class Activity01 extends ListActivity{…} //继承ListActivity

    ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();

    HashMap<String, String> map1 = new HashMap<String, String>();

    map1.put("user_name", "zhangsan");

    map1.put("user_ip", "192.168.0.1");

    list.add(map1);

    MyAdapter listAdapter = new MyAdapter(this, list, R.layout.user, new String[] { "user_name", "user_ip" }, new int[] { R.id.user_name,R.id.user_ip}); //当前activity,ArrayList,列表布局xml,list中HashMap的键名称,对应xml中的控件id(指定位置)

    添加Item点击事件,override onListItemClick

    14 Handler的使用1

    Handler handler = new Handler(); //实现异步处理

    handler.post(updateThread); //1点击事件中,调用Handler的post方法,将要执行的线程对象添加到队列中

    Runnable updateThread = new Runnable() {

           public over run(){ //2将要执行的操作写在线程对象的run方法中

                  System.out.println(“deal with data”); //数据处理

           handler.postDelayed(updateThread,3000); //3在run中循环执行,用postDelayed或post方法

    }

    }

    可以定义更复杂的handler,如复写它的handleMessage方法。

    Handler barHandler = new Handler(){

           public void handleMessage(Message msg){…} //处理barHandler用sendMessage方法压进来的队列msg

    }

    在要调用sendMessage的方法里面,定义上面的msg参数:

    Message msg = updateHandler.obtainMessage(); //obtain,获得,取得

    msg可以加入参数值,比如:

    msg.arg1 = somevalue; //用arg1和arg2传值时系统耗能少些

    Bundle bundle = new Bundle();

    bundle.putString(“test”,”test bundle”);

    msg.setData(bundle); //对应取出数据用Bundle bundle = msg.getData();

    定义完msg之后,就可以把msg压到消息队列中去了:barHandler.sendMessage(msg);或者用msg.sendToTarget();

    系统自动转向执行barHandler的handleMessage方法。

    15 Handler的使用2

    Handler.post方法添加线程对象(new Runnable(){…}),但他们是运行在同一个线程里的。因为post只调用了线程对象的run方法去执行,而没有调用start方法去开启另一个线程。

    要异步执行线程,可以用Thread t = new Thread(线程对象); t.start();

    也可以用HandlerThread类。先定义一个该类对象:

    HandlerThread handlerThread = new HandlerThread(“handler_thread”);

    handlerThread.start(); //在使用getLooper方法前调用

    再定义一个要加入此对象的Handler对象:

    class MyHandler extends Handler{…

           public MyHandler(Looper looper){

                  super(looper);

    }

    public void handleMessage(Message msg){…}

    …}

    用Handler类来定义MyHandler类的对象:

    MyHandler myHandler = new MyHandler(handlerThread.getLooper());

    定义这个handler的msg:Message msg = myHandler.obtainMessage(); … 然后msg.sendToTarget();

    即把myHandler加入到了一个线程队列中,它将以一个新的线程运行。

    16 SQLite

    SQLite:很小的一个关系型数据库

    SQLiteOpenHelper:帮助类

    当需要操作SQLite数据库时,首先需要一个SQLiteOpenHelper类的对象。因为SQLiteOpenHelper是一个抽象类,我们必须先写一个类去继承它。用getReadableDatabase()或getWritableDatabase()去得到SQLiteDatabase对象。

    继承类中必须有构造函数,如:

    public DatabaeHeapler(Context context,String name,CursorFactory factory,int version){

           super(context,name,factory,version); //必须通过super调用父类中的构造函数

    }

    在继承类中,可以重写onCreate、onUpgrade两个回调函数。onCreate方法的调用,是在SQLiteOpenHelper对象调用getReadableDatabase()或getWritableDatabase()方法时执行(不是在创建SQLiteOpenHelper对象时就执行)。onCreate可以用来创建表,如:

    public void onCreate(SQLiteDatabase db){

           db.execSQL(“create table user (id int,name varchar(20)”);

    }

    实例:

    DatabaseHelper dbHelper = new DatabaseHelper(xxxActivity.this,”pro_dbname_db”,…); //生成的数据库名

    SQLiteDatabase db = dbHelper.getReadableDatabase(); //得到android内置类的对象

    当上面创建dbHelper对象的版本参数(整形,递增),发生变化时,会调用我们继承类中重写的onUpgrade 方法。

    插入数据:

    ContentValues values = new ContentValues(); //创建ContentValues对象

    values.put("id", 1); //插入键值对,其中键是列名,值必须和数据库当中的数据类型一致

    values.put("name","zhangsan");

    DatabaseHelper dbHelper = new DatabaseHelper(xxxActivity.this," pro_dbname_db ",2); //2是版本号

    SQLiteDatabase db = dbHelper.getWritableDatabase();

    db.insert("user", null, values);

    cmd-adb shell命令,如果提示没有找到设备,原因是没有启动模拟器。

    进行Linux命令行模式,用Linux命令来操作模拟器。

    常用命令:ls –l 目录结构 cd 进入目录

    cd data

    cd data

    列出了模拟器中的所有应用程序和它的包名。进入当前应用程序(包名mars.sqlite3)(在eclipse中run as)。

    cd mars.sqlite3

    ls

    执行onCreateb函数,即创建数据库后ls,目录下多了个databases,进入databases,会看到新建的数据库

    操作此数据库:

    sqlite3 test_mars_db //sqlite3是一个命令,后接数据库名,命令提示符由#变成了sqlite

    尝试一下命令 .schema(.开始),列表了表和创建表的语句(其中android_metadata是自带的)。

    点击虚拟机的insert按钮插入数据后,select * from user; (记得用;结束)查看数据。

    更新:

    ContentValues values = new ContentValues();

    values.put(“name”,”zhangsanfeng”); //意义:把name这一列的值改成zhangsanfeng

    db.update(“user”,values,”id=?”,new String[]{“1”}); //表名,ContentValues对象,where,占位符参数值

    查询:

    Cursor cursor = db.query("user", new String[]{"id","name"}, "id=?", new String[]{"1"}, null, null, null);

    //表名,查询的列,where占位语句,where参数值,group by ,having ,order by

    while(cursor.moveToNext()){

           String name = cursor.getString(cursor.getColumnIndex(“name”)); …

    }

    //讲者建议不要过分信赖和依赖sqlite数据库,有时会出现莫名奇妙的问题。

    17 调试程序

    在java视图中(不是在ddms里)添加logcat:

    window-show view-other-logcat,可以把它拉到想要的位置

    添加过滤器:

    Filter Name:标签名,如sysout,by Log Tag:日志标识,如System.out,把这个方法的输出写到日志里

    可以加到某函数或语句的开始,判断是否执行到。

    出现异常时,可以看log的error日志。

    用Log类写日志:Log.d(“myDebug”,”myMsg”); //myDebug是by Log Tag的值

    DDMS中有一个File Explorer,在启动虚拟机之后,会读取到里面的文件,可以做添加和取出操作。

    18 下载文件

    注意关闭输入输出流,加入权限配置。

    下载文本:

    private URL url=null; …

    url = new URL(urlStr); //创建URL对象

    HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();

    buffer = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));

    //把得到的InputStream转成buffer类型

    while ((line = buffer.readLine()) != null) {sb.append(line); }

    下载文件:

    创建目录:

    File dir = new File(Environment.getExternalStorageDirectory() + “/”+ dirName);

    dir.mkdirs();

    创建文件:

    File file = new File(Environment.getExternalStorageDirectory() + “/”+filename);

    file.createNewFile(); //创建一个空文件

    //将InputSteram的数据(input参数)写入SD卡

    OutputStream output = new FileOutputStream(file); //取创建文件的outputstream

    byte buffer[] = new byte[4*1024];

    int temp;

    while((temp = input.read(buffer))!=-1){ output.write(buffer, 0, temp ); } //往文件中写数据

    return file; //返回File对象

    19 ContentProvider初步

    1.提供为存储和获取数据提供了统一的接口

    2.可以在不同的应用程序之间共享数据(SQLite只对同一应用程序)

    3.Android为常见的一些数据提供了ContentProvider(包括音频,视频,图片和通讯录等等)

    如果不要求在不同应用程序间共享数据,就没必要用ContentProvider,而使用数据库,文件,xml等存放数据。

    20 XML文件解析

    采用SAX(Simple API for XML)来解析。

    DOM解析是把XML看成一棵树,把它全部加载进来解析,优点是操作比较方便,缺点是对大xml文件操作不合适。

    对比SAX:逐行顺序读取解析,随时停止读取(已经读到所需信息)。缺点:操作复杂。对增删结点等操作不合适。

    解析文档过程:

    对于如下文档:<doc><para>Hello,world!</para></doc>

    在解析文档的过程中会产生如下一系列事件:

    start document

    start element:doc

    start element:para

    characters:Hello,world!

    end element:para

    end element:doc

    end document

    1. 创建事件处理程序

    2. 创建SAX解析器

    3. 将事件处理程序分配给解析器

    4. 对文档进行解析,将每个事件发送给处理程序

    上面所说的事件存在于一个特列的SAX接口:ContentHandler,这个接口位于org.xml.sax包中。

    实例:

    SAXParserFactory factory = SAXParserFactory.newInstance(); //创建一个SAXParserFactory

    XMLReader reader = factory.newSAXParser().getXMLReader(); //得到XMLReader

    reader.setContentHandler(new MyContentHandler()); //为XMLReader设置内容处理器

    reader.parse(new InputSource(new StringReader(xmlstring))); //开始解析xml

    其中MyContentHandler是继承DefaultHanlder(用空方法实现了ContentHandler接口)的类:

    public class MyContentHandler extends DefaultHandler{

           public void startDocument() throws SAXException { … } //抛出所有可能的异常,交由上层调用函数处理

           public void startElement(String namespaceURI,String localName,String qName,Attributes attr) throws SAXException {

                  //当前读取结点的命名空间,没有前缀的标签名,带前缀的标签名,标签里的属性

                  //如<abc:name id=”001”></abc:name>,这个abc就是前缀,name就是标签名,id就是属性

                  nodeName = localName; //全局变量

    if (localName.equals("worker")) { //worker结点,endElement方法一样,作判断条件用

    for (int i = 0; i < attr.getLength(); i++) { //获取标签的全部属性

                                System.out.println(attr.getLocalName(i) + "=" + attr.getValue(i));

                         }

    }

    }

           public void characters(char[] ch,int start,int length) throws SAXException{

           if(nodeName.equals(“name”)){ //判断条件

           xxx = new String(ch,start,length); …

    }

    }

    public void endElement(String namespaceURI,String localName,String qName) throws SAXException{…}

    public void endDocument() throws SAXException { … }

    }

    21 广播机制1

    自定类,继承自BroadcastReceiver,重写onReceive方法。

    public class TestReceiver extends BroadcastReceiver{

           public TestReceiver(){ System.out.println("TestReceiver"); }

           @Override

           public void onReceive(Context context, Intent intent) {       System.out.println("onReceive"); }

    }

    将receiver注册到android系统中:在AndroidManifest.xml加入reciver结点:

    <receiver android:name=".TestReceiver">

           <intent-filter>

                  <action android:name="android.intent.action.EDIT " /> //指定哪个接收器接收哪一个事件

           </intent-filter>

    </receiver>

    实例:

    Intent intent = new Intent();

    intent.setAction(Intent. ACTION_EDIT); //设置动作,对应AndroidManifest.xml中的Action

    xxxActivtiy.this.sendBroadcast(intent); //发送广播

    22 广播机制2

    注册BroadcastReceiver的方法:

    1.       在AndroidManifest.xml中注册

    当应用程序被关闭之后,receiver仍会收到广播。比如监听电池的状态。

    2.       在应用程序代码中注册。

    使用receiver来更新Activity中的状态。

    注册(Activity启动时):registerReceiver(receiver,filter) //相当于AndroidManifest.xml中的intent-filter

    取消注册(Activity不可见时):unregisterReceiver(receiver)

    实例:

    smsReceiver = new SMSReceiver(); //生成自定义Receiver类,extends BroadcastReceiver

    IntentFilter filter = new IntentFilter(); //生成IntentFilter对象

    filter.addAction(SMS_ACTION); //添加Action

    xxxActivity.this.registerReceiver(smsReceiver,filter); //注册

    xxxActivity.this.unregisterReceiver(smsReceiver); //取消注册

    在自定义的Receiver类中获取广播数据:

    public void onReceive(Context context,Intent intent){

           Bundle bundle = intent.getExtras();  //接受intent对象中的数据

           Object[] myOBJpdus = (Object[])bundle.get(“pdus”); //Bundle对象中有一个属性名为pdus的obj数据

           SmsMessage[] messages = new SmsMessage[myOBJpdus.length]; //创建一个SmsMessage类型的数组

           for(int i=0;i<myOBJpdus.length;i++){

                  message[i] = SmsMessage.createFromPdu((byte[])myOBJpdus[i]); //创建SmsMessage对象

                  System.out.println(message[i].getDisplayMessageBody()); //消息内容

    }

    }

    24 Socket编程

    Socket,套接字,用于描术IP地址和端口,是一个通信链的句柄。

    应用程序通过套接字向网络发出请求或者应答网络请求。

    服务器端:

    new ServerThread().start(); //对于服务器端的监听,使用新线程启动

    class ServerThread extends Thread{

           public void run(){ //客户端采用TCP协议发送时

                  ServerSocket serverSoket = null;

                  serverSocket = new ServerSocket(4567); //创建ServerSocket对象,监听4567端口

                  Socket socket = serverSocket.accept(); //接受请求

                  InputStream inputStream = socket.getInputStream(); //用流的方式传输

                  byte buffer[] = new byte[1024*4];

                  int temp =0;

                  while((temp = intputStream.read(buffer))!=-1){

           System.out.println(new String(buffer,0,temp)); //从inputStream中读取接收到的数据

    }

    }

    public void run(){ //客户端使用UDP协议发送时

           DatagramSocket socket = new DatagramSocket(4567); //创建DatagramSocket对象

           byte data[] = new byte[1024];

           DatagramPacket packet = new DatagramPacket(data,data.length); //用包的形式传输

           socket.receive(packet);

           String result = new String(packet.getData(),packet.getOffset(),packet.getLength());

    }

    }

    TCP客户端:

    Socket socket = new Socket(“192.168.0.xx”,4567); //定义Socket对象

    InputStream inputStream = new FileInputStream(“F://file/words.txt”); //读取文件流

    OutputStream outputStream = socket.getOutputStream(); //socket的outputStream

    byte buffer[] = new byte[4*1024];

    int temp = 0;

    while ((temp = inputStream.read(buffer))!=-1){

           outputStream.write(buffer,0,temp); //将inputStream的数据写到socket的outputStream中

    }

    UDP客户端:

    DatagramSocket socket = new DatagramSocket(4567); // 创建DatagramSocket对象

    InetAdress serverAddress = InetAddress.getByName(192.168.0.xx”);

    byte data[] = “mystringword”.getBytes();

    DatagramPacket packet = new DatagramPacket(data,data.length,serverAddress,4567);

    socket.send(packet); //发送数据包

    29实例代码-mp3播放器

    新建项目。

    values – strings.xml 中的app_name 不能是全小写

    继承ListActivity,在layout的xml中加入<ListView android:id="@id/android:list"…/>

    新建菜单:onCreateOptionsMenu

    private static final int ITEMID_BTN_UPDATE = 1;

    menu.add(0, ITEMID_BTN_UPDATE,1,R.string.updatelist);

    添加点击事件: onOptionsItemSelected

    if(item.getItemId()==ITEMID_BTN_UPDATE){…}

    下载xml文件:

    ctrl + shift + F : 格式化代码

    String xml = DownLoadXML("http://59.34.17.68:8099/ClientWeb/xml/resources.xml"); //调用下载文本类

    添加用户权限:AndroidManifest.xml: <uses-permission android:name="android.permission.INTERNET" />

    添加实体类:mp3info:

    定义成员变量private String id; private String mp3Name; …

    生成set和get方法:右键-Sources-Generate Getters and Setters,全选,确定。

    生成构造函数:右键-Sources-Generate Constructor using Fields…,全选和全不选,分别确定,生成两个

    生成toString()方法(方便调试):右键-Sources- Generate toString()…

    添加xml操作类Mp3ListContentHandler extends DefaultHandler

    重写characters endDocument endElement startDocument startElement五个方法(参考第20讲),摘取:

    startElement

    tagName=localName; //记录当前的结点名

    if(localName .equals("resource")){ mp3Info = new Mp3Info(); } //碰到数据结点开始时初始化对象

    characters

    String temp = new String(ch, start, length);

    if (tagName.equals("id")) { mp3Info.setId(temp); }

    endElement

    if (qName.equals("resource")) { infos.add(mp3Info); } //添加结点信息

    在Mp3LisrActivity中获取数据源:

    private List<Mp3Info> parse(String xmlStr) {

           SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

           List<Mp3Info> infos = new ArrayList<Mp3Info>();

           XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader();

           Mp3ListContentHandler mp3ListContentHandler = new Mp3ListContentHandler(infos); //传入infos

           xmlReader.setContentHandler(mp3ListContentHandler);

           xmlReader.parse(new InputSource(new StringReader(xmlStr))); //解释xml,把结果添加到infos中

           //for + ctrl + /,可以自动生成类foreach语句,用自动生成的实体toString方法可以把数据打印出来

           return infos;

    }

    新建显示数据的布局文件:

    new-file:命名mp3info_item.xml,确定

    加入两个TextView,设置好宽 android:layout_width,高android:layout_height,边距android:paddingLeft等。

    <TextView android:id=”@+id/mp3_name”…/> 在R中会生成一个mp3_name的id

    绑定数据,见13讲。

    List<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>(); //定义空数据源

    // for + ctrl + /,选择 for- iterate over collection,将list换成数据源Mp3Infos(要历遍的lists)

    //将HashMap<String, String>找成lists中的实体类,Mp3Info mp3Info = (Mp3Info) iterator.next();

    HashMap<String, String> map = new HashMap<String, String>(); //在for中填充空数据源

    map.put("name", mp3Info.getName()); map.put("size", mp3Info.getSize()); list.add(map);

    下面要实现下载文件,见18讲:

    文件下载类:

    SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; //根目录

    File dirFile = new File(SDCardRoot + dir + File.separator);   dirFile.mkdirs(); //创建目录

    File file = new File(SDCardRoot + dir + File.separator + fileName);  file.createNewFile(); //创建空文件

    将InputStream里面的数据写到文件中:

    File file = CreateFileInSDCard(fileName, path); //创建文件

    OutputStream output = new FileOutputStream(file); //取得文件的outputstream

    //将inputstream写入file中

    byte buffer[] = new byte[4*1024];

    int temp;

    while((temp = input.read(buffer))!=-1) { output.write(buffer,0,temp); }

    点击事件,ListActivity中重写onListItemClick:

    //在oncreate方法中获取到列表数据源infos,在此方法中就可以用position得到点击的对象了。

    Mp3Info mp3Info = infos.get(position); //得到点击对象

    下载Mp3文件:

    sysout + ctrl + / = System.out.println(“”);

    1.       需要创建一个Service,把mp3对象传到service当中去。因为Service不依赖于Activity界面,优先级比较高,保证下载程序不会被关掉。

    2.       每个文件的下载都需要在一个独立的线程当中进行。防止主线程阻塞。

    创建Service:

    包名前缀取ListActivity的包名,如dachun.mp3player.service,才能方便在AndroidManifest.xml中注册。

    public class DownloadService extends Service,重写方法onBind,onStartCommand。

    在AndroidManifest.xml的application结点中注册service:

    <service android:name=".service.DownloadService"></service>

    回到ListActivity的onListItemClick事件中,需要把mp3Info对象通过Intent传到Service中去:

    先对Mp3Info类进行序列化:

    public class Mp3Info implements Serializable …

    点击行左边的黄色提示图标,选择Add default serial version ID,添加一个序列ID

    然后补充ListActivity点击事件:

    Intent intent = new Intent();

    intent.putExtra(“mp3Info”,mp3Info);   //传递mp3Info对象

    intent.setClass(this, DownloadService.class); //绑定Service

    startService(intent); //启动Serivce

    在Service的onStartCommand方法(每次startService都会调用):由intent参数可得到传过来的mp3info对象:

    Mp3Info mp3Info = (Mp3Info)intent.getSerializableExtra("mp3Info"); //可以打印调试下

    用线程类(如下)去下载。故在Service中创建线程内部类:

    class DownloadThread implements Runnable,创建带Mp3Info对象的构造函数进行初始化,覆写run方法:

    String mp3Url = "http://59.34.17.68:8099/ClientWeb/source/"+ mp3Info.getName();

    HttpDownloader httpDownloader = new HttpDownloader();

    int result = httpDownloader.downFile(mp3Url, "mp3/", mp3Info.getName()); //下载文件方法

    继写onStartcommand方法:

    DownloadThread downloadThread = new DownloadThread(mp3Info);

    Thread thread = new Thread(downloadThread); //新建线程

    thread.start(); //启动下载线程

    记得创建目录时要配置权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    下载成功之后,可以用adb shell,ls –l进入sdcard-mp3中查看是否真的存在了下载的mp3文件。

    31实例代码-续

    使用tab分别显示本地下载文件和服务器可下载文件。

    新建MainActivity extends TabActivity,用来实现tab,并在AndroidManifest.xml中注册。

    <activity android:label="@string/app_name" android:name=".MainActivity" >

    新建LocalMp3ListActivity extends ListActivity,用来显示本地文件。同理进行注册。

    配置启动显示的Activity:有<intent-filter>结点的就是启动时显示的,把它的android:name设为.MainActivity。把之前注册的.MainActivity改成修改前的android:name(.Mp3ListActivity)。

    alt + shift + R,重命名文件。

    将之前的main.xml(用作服务器文件列表)重命名为remote_mp3_list.xml。

    新建main.xml作为MainActivity的layout文件:

    <TabHost …><LinearLayout…>

    <TabWidget…></TabWidget><FrameLayout…></FrameLayout>

    </LinearLayout></TabHost>

    为MainActivity设置layout,重写onCreate方法:setContentView(R.layout.main);

    创建Tab对象显示:

    TabHost tabHost = getTabHost(); //得到tab主控件对象

    TabHost.TabSpec remoteSpce = tabHost.newTabSpec("Remote"); //添加选项卡

    Resources res = getResources(); //设置图标

    remoteSpce.setIndicator("Remote", res.getDrawable(android.R.drawable.stat_sys_download));

    Intent remoteIntent = new Intent(); //设置显示的Activity

    remoteIntent.setClass(this, Mp3ListActivity.class);

    remoteSpce.setContent(remoteIntent);

    tabHost.addTab(remoteSpce); //添加Tab

    同理,创建另一个tab,显示本地文件。localSpce,localIntent,android.R.drawable.stat_sys_upload。

    由于在本地有在服务器上的显示样式一样,复制一份remote_mp3_list.xml,重命名local_mp3_list.xml即可。

    在FileUtils中添加函数List<Mp3Info> getMp3Files,用来读取目录中的mp3名字和大小,传入参数path:

    File file = new File(SDCardRoot + File.separator + path); //得到当前路径的file对象,用来操作目录文件

    File[] files = file.listFiles(); //列出所有文件,

    if (files[i].getName().endsWith("mp3")) { … } //files[i].getName()和files[i].length()就是名字和大小了

    LocalMp3Activity中:

    setContentView(R.layout.local_mp3_list); //设置布局

    覆写onResume,读取文件并绑定显示:

    FileUtils fileUtils = new FileUtils(); //文件操作类

    List<Mp3Info> mp3Infos = fileUtils.GetMp3Files("mp3/");

    List<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>(); //作绑定数据源

    for (Iterator iterator = mp3Infos.iterator(); iterator.hasNext();) { //历遍添加list子项

    Mp3Info mp3Info = (Mp3Info) iterator.next();

    HashMap<String,String> map = new HashMap<String,String>();

    map.put("mp3_name", mp3Info.getName());

    map.put("mp3_size", mp3Info.getSize());

    list.add(map);

    }

    //cmd – adb shell – cd sdcard – cd mp3 – rm a1mp3 删除a1.mp3

    32实例代码-续

    实现点击local列表中的mp3时打开另一个activity并播放。新建activity:PlayerActivity extends Activity,注册。

    覆写LocalMp3ListActivity的onListItemClick函数:

    if (mp3Infos != null) { //mp3Infos在onResume时赋值,显示在列表中

    Mp3Info mp3Info = mp3Infos.get(position); //获得点击的mp3

    Intent intent = new Intent();

    intent.putExtra("mp3Info", mp3Info); //传数据

    intent.setClass(this, PlayerActivity.class);

    startActivity(intent);

    }

    PlayerActivity

    添加播放、暂停、停止,图标,新建布局文件player.xml:

    <LinearLayout…>

           <ImageButton android:id="@+id/start" android:layout_width="wrap_content"

            android:layout_height="wrap_content" android:src="@drawable/start"/>

    <ImageButton…/><ImageButton…/>

    </LinearLayout>

    覆写onCreate:

    setContentView(R.layout.player); //显示图标

    Intent intent = getIntent(); //取得参数

    Mp3Info mp3Info = (Mp3Info)intent.getSerializableExtra("mp3Info"); //获取数据

    startButton = (ImageButton)findViewById(R.id.start); //取得按钮

    startButton.setOnClickListener(new StartButtonListener()); //为按钮添加事件

    内部监听类定义如下:

    class StartButtonListener implements OnClickListener{

           public void onClick(View v){

           if(!isPlaying){ //boolean值,标识是否在播放

           String path = GetMp3Path(mp3Info);

           mediaPlayer = MediaPlayer.create(PlayerActivity.this,Uri.parse(“file//”+path)); //新建操作类

           mediaPlayer.start();

    }

    }

    }

    private String GetMp3Path(Mp3Info mp3Info){ //sd卡中存放mp3的路径

           String SDCardRoot = Enviroment.getExternalStorageDirectory().getAbsolutePath(); //sd卡路径

           return SDCardRoot + File.separator + “mp3” + File.separator + mp3Info.getName();

    }

    33实例代码-续

    将mp3播放功能转移到Service中。(Activity并不稳定)

    对LRC歌词文件进行预处理。

    读取LRC文件(时间+歌词)。

    使用Handler动态的更新歌词(歌词同步)

    新建一个PlayerService extends Service,覆写onStartCommand方法(记得要注册)。

    Mp3Info mp3Info = (Mp3Info) intent.getSerializableExtra("mp3Info"); //获取对象

    int MSG = intent.getIntExtra("MSG", 0); //获取动作

    //play,pause,stop是从上节的按钮监听器中移过来的方法

    if (MSG == AppConstant.PlayMsg.PLAY_MSG) {play(mp3Info);}   //PLAY_MSG是整形常量

    else if (MSG == AppConstant.PlayMsg.PAUSE_MSG) {pause();}

    else if (MSG == AppConstant.PlayMsg.STOP_MSG) {stop(); }

    原PlayerActivity中的监听事件改成:

    Intent intent = new Intent();

    intent.setClass(PlayerActivity.this, PlayerService.class);

    intent.putExtra("mp3Info",mp3Info);

    intent.putExtra("MSG", AppConstant.PlayMsg.PLAY_MSG);

    startService(intent);

    函数添加注释的方法:/ + * + * + 回车,类似///

    同步歌词处理:

    创建一个LrcProcessor处理类,放到lrc包内。

    public ArrayList<Queue> process(InputStream inputStream){…},此方法接收歌词文件的input做参数,返回一个队列数组,一个Queue<Long>存放时间,一个Queue<String>存放歌词,部分代码:

    InputStreamReader inputStreamReader = new InputStreamReader(inputStream); //读取utf-8格式的lrc

    BufferedReader bufferedReader = new BufferedReader(inputReader); //bufferedReader对象每次读一行

    Pattern p = Pattern.compile(“正则”); //Matcher m = p.matcher(“字符串”);,if(m.find()){ …m.group(); …}

    temp = bufferedReader.readLine(); //读一行,if(temp!=null)..

    PlayerActivity的layout中添加一个TextView:lrcText用来显示歌词。

    启动虚拟机,打开DDMS,进入mnt – sdcard – mp3,把歌词文件复制进去。

    用class UpdateTimeCallback implements Runnable类去实现歌词切换:

    public void run(){

           long offset = System.currentTimeMills() – begin; //计算时间偏移量

           …nextTimeMill = (Long)times.poll(); //从Queue对象中取出一个值

           … handler.postDelayed(updateTimeCallback,10); //本类对象

    }

    在StartButtonListener中调用UpdateTimeCallback去处理:

    LrcProcessor lrcProcessor = new LrcProcessor();

    queues = lrcProcessor.process(inputStream); //从文件中解释出time和message

    updateTimeCallback = new UpdateTimeCallback(queues); //循环调用,控制textview显示

    //出现waiting for debugger,不关拟虚机,关了eclipse再开,再运行调试

    34实例代码-续

    实现歌词在界面切出切入时的控制。使用BroadcastReceiver来实现,PlayActivity只在接收到广播时做动作。

    ctrl + 左键,点击类名可转到定义

    在PlayActivity中:

    1.定义广播接收器calss LrcMessageBroadcastReceiver extends BroadcastReceiver。

    覆写onReceive方法,取歌词信息:intent.getStringExtra(“lrcMsg”),设置到lrcTextView中显示。

    2.定义intentFileter。可以在AndroidManifest.xml中注册,也可以在代码中生成:

    intentFilter = new IntentFilter();

    intentFilter.addAction(AppConstant.LRC_MESSAGE_ACTION); //由自定义常量定义动作

    3.覆写onPause:unregisterReceiver(receiver);

    和onResume方法:

    receiver = new LrcMessageBroadcastReceiver(); //用自定义广播类实例化对象

    registerReceiver(receiver, getIntentFileter()); //getIntentFileter自定义方法,注册intentFileter(见上)

  • 相关阅读:
    设计模式(六)—原型模式Prototype(创建型)
    hdu_2039_三角形_解题报告
    古代赌局 俗话说:十赌九输。因为大多数赌局的背后都藏有阴谋。不过也不尽然,有些赌局背后藏有的是:“阳谋”。 有一种赌局是这样的:桌子上放六个匣子,编号是1至6。多位参与者(以下称玩家)可以把
    读《Boost程序库完全开发指南》
    使用HttpSessionListener接口监听Session的创建和失效
    HDU2317:Nasty Hacks
    意淫的需求要不得
    enumerate(s)与range(len(s))的作用是相同
    一种数据展示方式,UI设计新颖,供大家参考(源码部分) (demo已经上传)
    RMAN Compressed Backupset
  • 原文地址:https://www.cnblogs.com/vipcjob/p/2276336.html
Copyright © 2011-2022 走看看