[转载]使用android-rss库实现从网络中获取rss信息,并通过ListView显示 - 移动开发团队 - 博客园

mikel阅读(857)

[转载]使用android-rss库实现从网络中获取rss信息,并通过ListView显示 – 移动开发团队 – 博客园.

团队最近打算出一款rss订阅功能的应用,本文针对通过网络http的方式获取xml的rss来做一个技术准备。
在rss reader方面,目前有一些比较成熟的应用,比如腾讯的腾讯订阅,蘑菇新闻等。个人比较喜欢简洁的蘑菇新闻。团队也针对这个做了反编译。

针对rss的应用,会涵盖http、xml解析、多线程、异步等操作。而在xml解析方面,我选择了托管在github上面的Android-rss项目作为基库。参考文章[1].

Android-rss库使用org.xml.sax.helpers.DefaultHandler进行解析xml,通过网络流的方式来作为解析数据的传入。然后将解析后的数据存储在RSSFeed的items中,通过函数getItems即可得到所处理后的数据。

使用步骤如下:
1 从https://github.com/ahorn/Android-rss将项目代码下载到本地,可以是zip的格式。
2 解压代码到某个目录
3 使用eclipse 创建一个项目,本文取项目名称为WPReader(WordPress Reader).
4 点击src目录,创建一个新的package,取名org.mcsoxford.rss.必须是这个名字。
5 在这个包上面点击右键,弹出import
6 选择FileSystem
7 然后选择我们刚才的zip解压目录
8 将所有的文件导入即可。下面我们就可以使用这个所有的代码了。
9 创建一个item.xml用于显示每个单独的Title.
10 main.xml里面放置一个ListView即可。

主代码如下:

复制代码
package com.jouhu.reader;
 
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import org.mcsoxford.rss.*;
 
public class WPReaderActivity extends Activity {
    /** Called when the activity is first created. */
    public String tag = "WPReaderActivity";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //创建一个RSSReader
        RSSReader reader = new RSSReader();
        String url = "http://feeds.bbci.co.uk/news/world/rss.xml";
        //创建一个ArrayList用于存储rss数据
        ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
        try {
            //这是最核心的方法,reader.load会解析url上面的xml文件
            RSSFeed feed = reader.load(url);
            EditText et = (EditText)findViewById(R.id.editText1);
            et.setText(feed.getDescription());
            Integer it = feed.getItems().size();
            //将所有的解析到的数据加入到listItem中
            for(int i = 0 ; i < it ; i ++)
            {
                RSSItem item = feed.getItems().get(i);
                HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("itemtitle", item.getTitle());
                //map.put("itemcontent", item.getDescription());
                map.put("itemcontent", "");
                listItem.add(map);
            }
        } catch (RSSReaderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ListView lv = (ListView)findViewById(R.id.listView1);
        //构造一个Adapter
        SimpleAdapter listItemAdapter =
            new SimpleAdapter(this, listItem,R.layout.item, new String[]{"itemtitle","itemcontent"}, new int[]{R.id.title,R.id.content});
        lv.setAdapter(listItemAdapter);
    }
}
复制代码

效果图:

源代码下载:

WPReader.rar

注意第八项中,这是一个通用方法,对于一些开源的库,我们经常可以这样使用。将其所有的源代码加入到项目中,然后直接调用其库的代码。

在本项目中,最重要的一个地方就是RSSHandler类的构造。在参考文章[2]/[3]/[4]分别提到的这个类的构造方法。
在继承的 时候几个最重要的方法startDocument、endDocument、startElement、endElement、characters是一 般需要重载实现的。这里可以对我们需要解析的xml文件进行针对性的处理。参加下面从参考文献[3]中的demo代码。

复制代码
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;
public class MyDefaultHandler extends DefaultHandler {
    private StringBuffer buf;
    public void startDocument() throws SAXException {
        buf=new StringBuffer();
        System.out.println("*******开始解析文档*******");
    }
    public void endDocument() throws SAXException {
        System.out.println("*******解析文档结束*******");
    }
    public void startPrefixMapping( String prefix, String uri ) {
System.out.println("\n前缀映射: " + prefix +" 开始!"+ "  它的URI是:"+uri);
    }
    public void endPrefixMapping( String prefix ) {
       System.out.println("\n前缀映射: "+prefix+" 结束!");
    }
    public void startElement( String namespaceURI, String localName,
                                  String fullName, Attributes attributes )
                          throws SAXException {
        System.out.println("\n元素: " + "["+fullName+"]" +" 开始解析!");
        // 打印出属性信息
        for ( int i = 0; i < attributes.getLength(); i++ ) {
            System.out.println("\t属性名称:" + attributes.getLocalName(i)
                + " 属性值:" + attributes.getValue(i));
        }
    }
    public void endElement( String namespaceURI, String localName,
                                                      String fullName )
                          throws SAXException {
       //打印出非空的元素内容并将StringBuffer清空
       String nullStr="";
       if (!buf.toString().trim().equals(nullStr)){
          System.out.println("\t内容是: " + buf.toString().trim());
       }
       buf.setLength(0);
       //打印元素解析结束信息
        System.out.println("元素: "+"["+fullName+"]"+" 解析结束!");
    }
    public void characters( char[] chars, int start, int length )
                                throws SAXException {
       //将元素内容累加到StringBuffer中
       buf.append(chars,start,length);
    }
    public void warning( SAXParseException exception ) {
        System.out.println("*******WARNING******");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("********************");
    }
    public void error( SAXParseException exception ) throws SAXException{
        System.out.println("******* ERROR ******");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("********************");
    }
    public void fatalError( SAXParseException exception ) throws SAXException {
        System.out.println("******** FATAL ERROR ********");
        System.out.println("\t行:\t" + exception.getLineNumber());
        System.out.println("\t列:\t" + exception.getColumnNumber());
        System.out.println("\t错误信息:\t" + exception.getMessage());
        System.out.println("*****************************");
    }
}
复制代码

参考文章:
1 https://github.com/ahorn/android-rss
2 http://www.ibm.com/developerworks/opensource/library/x-android/index.html
3 http://www.ibm.com/developerworks/cn/xml/x-saxhandle/index.html?ca=drs-
4 http://qiaoakai.iteye.com/blog/583941

 

本文另外发布地址:

http://doandroid.info/?p=1167

[转载]Android Rss阅读器 - 爱生活,爱编程 - 博客园

mikel阅读(1039)

[转载]Android Rss阅读器 – 爱生活,爱编程 – 博客园.

前言

        前几天去北京面试,题目是让我解析一下腾讯的Rss。之前虽然知道xml,但是自己从来没有去学习怎么解析,在网上查一些例子,但是就是没有解析出来。现 在看看还蛮好笑的,因为我那时候是使用sax解析xml的,可不巧的是让我解析的xml的encoding是“GBK”,但是sax默认使用utf-8。 所以没有解析出来也不足为奇了。关于如何解决这个问题下面会说。

 

Rss概念

      要在Android平台中开发RSS客户端,首先需要了解什么是RSS,RSS怎样工作,及怎样解析XML等知识。

      首先,我们必须要了解什么是RSS:

1. RSS指Really Simple Syndication(真正简易联合)

2. RSS使您有能力聚合(syndicate)网站的内容

3. RSS定义了非常简单的方法来共享和查看标题和内容

4. RSS文件可被自动更新

5. RSS允许为不同的网站进行视图的个性化

6. RSS使用XML编写

RSS被设计用来展示选定的数据。如果没有 RSS,用户就不得不每日都来您的网站检查新的内容。对许多用户来说这样太费时了。通过RSS feed(RSS 通常被称为 News feed 或 RSS feed),用户们可以使用 RSS 聚合器来更快地检查您的网站更新(RSS 聚合器是用来聚集并分类 RSS feed 的网站或软件)。由于RSS 数据很小巧并可快速加载,它可轻易地被类似移动电话或 PDA 的服务使用。拥有相似内容的网站环(Web-rings)可以轻易地在它们的网站共享内容,使这些网站更出色更有价值。

谁应当使用RSS?

那些极少更新内容的网管们不需要RSS!

RSS对那些频繁更新内容的网站是很有帮助的,比如:

新闻站点          列出新闻的标题、日期以及描述

企业                 列出新闻和新产品

日程表             列出即将来临的安排和重要日期

站点更新         列出更新过的页面或新的页面

RSS如何工作

使用RSS,您在名为聚合器的公司注册您的内容。

步骤之一是,创建一个RSS 文档,然后使用.xml 后缀来保存它。然后把此文件上传到您的网站。接下来,通过一个RSS 聚合器来注册。每天,聚合器都会到被注册的网站搜索RSS文档,校验其链接,并显示有关 feed 的信息,这样客户就能够链接到使他们产生兴趣的文档。

       一个RSS实例文档

RSS文档使用一种简单的自我描述的语法: 让我们看一个简单的RSS文档:

<?xml version="1.0" encoding="ISO-8859-1" ?>  
<rss version="2.0">  

<channel>  
  <title>W3School Home Page</title>  
  <link>http://www.w3school.com.cn</link>  
  <description>Free web building tutorials</description>  
  <item>  
    <title>RSS Tutorial</title>  
    <link>http://www.w3school.com.cn/rss</link>  
    <description>New RSS tutorial on W3School</description>  
  </item>  
  <item>  
    <title>XML Tutorial</title>  
    <link>http://www.w3school.com.cn/xml</link>  
    <description>New XML tutorial on W3School</description>  
  </item>  
</channel>  

</rss>

文档中的第一行:XML声明 – 定义了文档中使用的XML 版本和字符编码。此例子遵守1.0规范,并使用ISO-8859-1 (Latin-1/West European) 字符集。

下一行是标识此文档是一个RSS文档的RSS 声明(此例是 RSS version 2.0)。

下一行含有 <channel> 元素。此元素用于描述RSS feed。

<channel> 元素有三个必需的子元素:

<title> – 定义频道的标题。(比如 w3school 首页)

<link> – 定义到达频道的超链接。(比如:www.w3school.com.cn)

<description> – 描述此频道(比如免费的网站建设教程)

每个<channel>元素可拥有一个或多个 <item> 元素。

每个<item>元素可定义 RSS feed 中的一篇文章或 “story”。

<item> 元素拥有三个必需的子元素:

<title> – 定义项目的标题。(比如 RSS 教程)

<link> – 定义到达项目的超链接。(比如:http://www.w3school.com.cn/rss)

<description> – 描述此项目(比如 w3school 的RSS 教程)

最后,后面的两行关闭 <channel> 和 <rss> 元素。

 

       上面的只是是一个例子,下面来看一下实际应用的Rss:

<?xml version="1.0" encoding="gb2312"?>

<?xml-stylesheet type="text/xsl" href="/css/rss_xml_style.css"?>

<rss version="2.0">
  <channel>
    <title>新闻国内</title>
    <image>
      <title>新闻国内</title>
      <link>http://news.qq.com</link>
      <url>http://mat1.qq.com/news/rss/logo_news.gif</url>
    </image>
    <description>新闻国内</description>
    <link>http://news.qq.com/china_index.shtml</link>
    <copyright>Copyright 1998 - 2005 TENCENT Inc. All Rights Reserved</copyright>
    <language>zh-cn</language>
    <generator>www.qq.com</generator>
    <item>
      <title>浙江温州泰顺广播电视台原台长受贿获刑十年</title>
      <link>http://news.qq.com/a/20130608/012227.htm</link>
      <author>www.qq.com</author>
      <category/>
      <pubDate>2013-06-08 12:05:21</pubDate>
      <comments/>
      <description>  中新网温州6月8日电(记者赵小燕通讯员晨翔)利用担任广播电视台台长的职务便利,在产品采购、干部任用、人事调动、人事招录等方面为他人谋取利益,先后收受许某某等人所送共计284200元。近日,温州泰顺县人民法院以受贿罪判处被告人张某某有期徒刑十年,并处没收财产30000元;没收被告人张某某受贿违法所得284200元,上</description>
    </item>
    <item>
      <title>河南新蔡回应开发商违规 对外界质疑只字不提</title>
      <link>http://news.qq.com/a/20130608/012148.htm</link>
      <author>www.qq.com</author>
      <category/>
      <pubDate>2013-06-08 11:59:42</pubDate>
      <comments/>
      <description>中新网驻马店6月8日电(记者侯伟胜)本网对河南省新蔡县“城市1号工程”伟利国际广场在征地、施工、销售等环节涉嫌违规一事进行报道后,引起广泛关注。6月7日下午,新蔡县国土局以电子邮件的形式对报道内容进行了说明。但是对于群众最为关注的几点质疑,却只字未提。伟利国际广场是驻马店市、新蔡县重点招商引资项目,新蔡县</description>
    </item>

       后面省略了很多item,大体看一下结构就可以。
        RSS中的注释

       在RSS 中书写注释的语法与HTML 的语法类似:

       <!– This is an RSS comment –>

       RSS使用XML来编写

       因为RSS也是XML,请记住:

       1. 所有的元素必许拥有关闭标签

       2. 元素对大小写敏感

       3. 元素必需被正确地嵌套

       4. 属性值必须带引号

       以上内容来自http://www.w3school.com.cn/rss/index.asp,对RSS进行了简单讲解,如果想看更详细的内容可以 去w3school看看。我们后面要讲的Android RSS客户端就是以此为基础完成的,所以掌握好RSS基础很重要。

 

Rss解析 

          了解了以上内容,我们就通过一个例子来学习RSS,如何对XML文件进行解析。首先,展示一下完成之后项目的运行结果:

                                               

                                                

       下面是程序的具体实现

一:界面布局,具体代码如下:

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView 
        android:id="@+id/list" 
        android:cacheColorHint="#00000000"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>

activity_show_description.xml  :

 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:autoLink="web"
        android:text="" />

    <Button
        android:ida="@+id/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="@string/back" />

</LinearLayout>

二:编写实体类RssFeed及RssItem

 

RssFeed.java:

package com.example.rssnews.domain;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class RssFeed {

	private String title; // 标题
	private String pubdate; // 发布日期

	private int itemCount; // 用于计算列表的数目
	private List<RssItem> rssItems; // 用于描述列表item

	public RssFeed() {
		rssItems = new ArrayList<RssItem>();
	}

	// 添加RssItem条目,返回列表长度
	public int addItem(RssItem rssItem) {
		rssItems.add(rssItem);
		itemCount++;
		return itemCount;
	}

	// 根据下标获取RssItem
	public RssItem getItem(int position) {
		return rssItems.get(position);
	}

	public List<HashMap<String, Object>> getAllItems() {
		List<HashMap<String, Object>> data = new ArrayList<HashMap<String, Object>>();
		for (int i = 0; i < rssItems.size(); i++) {
			HashMap<String, Object> item = new HashMap<String, Object>();
			item.put(RssItem.TITLE, rssItems.get(i).getTitle());
			item.put(RssItem.PUBDATE, rssItems.get(i).getPubdate());
			data.add(item);
		}
		return data;

	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getPubdate() {
		return pubdate;
	}

	public void setPubdate(String pubdate) {
		this.pubdate = pubdate;
	}

	public int getItemCount() {
		return itemCount;
	}

	public void setItemCount(int itemCount) {
		this.itemCount = itemCount;
	}

}

RssItem.java:

 

package com.example.rssnews.domain;

public class RssItem {

	private String title;
	private String link;
	private String author;
	private String category;
	private String pubdate;
	private String comments;
	private String description;

	public static final String TITLE = "title";
	public static final String PUBDATE = "pubdate";

	public RssItem() {

	}

	public String getTitle() {
		if (title.length() > 20) {
			return title.substring(0, 19) + "...";
		}
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getLink() {
		return link;
	}

	public void setLink(String link) {
		this.link = link;
	}

	public String getCategory() {
		return category;
	}

	public void setCategory(String category) {
		this.category = category;
	}

	public String getPubdate() {
		return pubdate;
	}

	public void setPubdate(String pubdate) {
		this.pubdate = pubdate;
	}

	public String getComments() {
		return comments;
	}

	public void setComments(String comments) {
		this.comments = comments;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	@Override
	public String toString() {
		return "RssItem [title=" + title + ", description=" + description
				+ ", link=" + link + ", category=" + category + ", pubdate="
				+ pubdate + "]";
	}

}

三:编写解析XML文件时的处理类,用自定义的RssHandler继承自DefaultHandler,然后重载DefaultHandler的五个方法。具体代码如下:

 

RssHandler.java:

package com.example.rssnews.util;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import com.example.rssnews.domain.RssFeed;
import com.example.rssnews.domain.RssItem;
import android.util.Log;

public class RssHandler extends DefaultHandler {

	RssFeed rssFeed;
	RssItem rssItem;

	String lastElementName = "";// 标记变量,用于标记在解析过程中我们关心的几个标签,若不是我们关心的标签记做0

	final int RSS_TITLE = 1;// 若是 title 标签,记做1,注意有两个title,但我们都保存在item的成员变量中
	final int RSS_LINK = 2;// 若是 link 标签,记做2
	final int RSS_AUTHOR = 3;
	final int RSS_CATEGORY = 4;// 若是category标签,记做 4
	final int RSS_PUBDATE = 5; // 若是pubdate标签,记做5,注意有两个pubdate,但我们都保存在item的pubdate成员变量中
	final int RSS_COMMENTS = 6;
	final int RSS_DESCRIPTION = 7;// 若是 description 标签,记做3

	int currentFlag = 0;

	public RssHandler() {

	}

	@Override
	public void startDocument() throws SAXException {
		super.startDocument();
		rssFeed = new RssFeed();
		rssItem = new RssItem();

	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		super.characters(ch, start, length);
		// 获取字符串
		String text = new String(ch, start, length);
		Log.i("i", "要获取的内容:" + text);

		switch (currentFlag) {
		case RSS_TITLE:
			rssItem.setTitle(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		case RSS_PUBDATE:
			rssItem.setPubdate(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		case RSS_CATEGORY:
			rssItem.setCategory(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		case RSS_LINK:
			rssItem.setLink(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		case RSS_AUTHOR:
			rssItem.setAuthor(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		case RSS_DESCRIPTION:
			rssItem.setDescription(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		case RSS_COMMENTS:
			rssItem.setComments(text);
			currentFlag = 0;// 设置完后,重置为开始状态
			break;
		default:
			break;
		}
	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		super.startElement(uri, localName, qName, attributes);
		if ("chanel".equals(localName)) {
			// 这个标签内没有我们关心的内容,所以不作处理,currentFlag=0
			currentFlag = 0;
			return;
		}
		if ("item".equals(localName)) {
			rssItem = new RssItem();
			return;
		}
		if ("title".equals(localName)) {
			currentFlag = RSS_TITLE;
			return;
		}
		if ("description".equals(localName)) {
			currentFlag = RSS_DESCRIPTION;
			return;
		}
		if ("link".equals(localName)) {
			currentFlag = RSS_LINK;
			return;
		}
		if ("pubDate".equals(localName)) {
			currentFlag = RSS_PUBDATE;
			return;
		}
		if ("category".equals(localName)) {
			currentFlag = RSS_CATEGORY;
			return;
		}

		if ("author".equals(localName)) {
			currentFlag = RSS_AUTHOR;
			return;
		}

		if ("comments".equals(localName)) {
			currentFlag = RSS_COMMENTS;
			return;
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		super.endElement(uri, localName, qName);
		// 如果解析一个item节点结束,就将rssItem添加到rssFeed中。
		if ("item".equals(localName)) {

			rssFeed.addItem(rssItem);
			return;
		}
	}

	@Override
	public void endDocument() throws SAXException {
		super.endDocument();
	}

	public RssFeed getRssFeed() {
		return rssFeed;
	}

}

四: 从 SAXParser解析器中获得解析xml文件的xmlReader ,使用三中自定义的xml解析器作为xmlReader的处理器,然后使用url打开流,并将流作为 xmlReader解析的输入源并解析。这里需要注意Rss的编码格式,做一个处理。具体代码如下:

package com.example.rssnews.util;

import info.monitorenter.cpdetector.io.ASCIIDetector;
import info.monitorenter.cpdetector.io.CodepageDetectorProxy;
import info.monitorenter.cpdetector.io.JChardetFacade;
import info.monitorenter.cpdetector.io.ParsingDetector;
import info.monitorenter.cpdetector.io.UnicodeDetector;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import com.example.rssnews.domain.RssFeed;

public class RssFeed_SAXParser {

	public RssFeed getFeed(String urlStr) throws ParserConfigurationException,
			SAXException, IOException {
		URL url = new URL(urlStr);
		SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); // 构建SAX解析工厂
		SAXParser saxParser = saxParserFactory.newSAXParser(); // 解析工厂生产解析器
		XMLReader xmlReader = saxParser.getXMLReader(); // 通过saxParser构建xmlReader阅读器

		RssHandler rssHandler = new RssHandler();
		xmlReader.setContentHandler(rssHandler);
		// 使用url打开流,并将流作为 xmlReader解析的输入源并解析

		CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
		// 向代理对象添加探测器
		detector.add(JChardetFacade.getInstance());
		// 得到编码字符集对象
		Charset charset = detector.detectCodepage(url);
		// 得到编码名称
		String encodingName = charset.name();
		System.out.println(encodingName);

		InputSource inputSource = null;
		InputStream stream = null;

		// 如果是GB2312编码
		if ("GBK".equals(encodingName)) {
			stream = url.openStream();
			// 通过InputStreamReader设定编码方式
			InputStreamReader streamReader = new InputStreamReader(stream,
					encodingName);
			inputSource = new InputSource(streamReader);
			xmlReader.parse(inputSource);
			return rssHandler.getRssFeed();
		} else {
			// 是utf-8编码
			inputSource = new InputSource(url.openStream());
			inputSource.setEncoding("UTF-8");
			xmlReader.parse(inputSource);
			return rssHandler.getRssFeed();
		}
	}

	/**
	 * 获得远程URL文件的编码格式
	 */
	public static String getReomoteURLFileEncode(URL url) {

		CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
		detector.add(new ParsingDetector(false));
		detector.add(JChardetFacade.getInstance());
		detector.add(ASCIIDetector.getInstance());
		detector.add(UnicodeDetector.getInstance());
		java.nio.charset.Charset charset = null;
		try {
			System.out.println(url);
			charset = detector.detectCodepage(url);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		if (charset != null) {
			return charset.name();
		} else {
			return "utf-8";
		}
	}
}

五:编写MainActivity,将解析的XML文件以列表的形式显示出来,具体代码如下:

 

MainActivity.java:

package com.example.rssnews;

import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import com.example.rssnews.domain.RssFeed;
import com.example.rssnews.domain.RssItem;
import com.example.rssnews.util.RssFeed_SAXParser;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.SimpleAdapter;

public class MainActivity extends Activity implements OnItemClickListener {

	// 从网络获取RSS地址
	public final String RSS_URL = "http://news.qq.com/newsgn/rss_newsgn.xml";

	public final String tag = "RSSReader";
	private RssFeed feed = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		try {
			feed = new RssFeed_SAXParser().getFeed(RSS_URL);
			System.out.println(feed.getAllItems());
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		showListView();
	}

	/*
	 * 把RSS内容绑定到ui界面进行显示
	 */
	private void showListView() {

		ListView itemList = (ListView) this.findViewById(R.id.list);
		if (feed == null) {
			setTitle("访问的RSS无效");
			return;
		}
		SimpleAdapter simpleAdapter = new SimpleAdapter(this,
				feed.getAllItems(), android.R.layout.simple_list_item_2,
				new String[] { RssItem.TITLE, RssItem.PUBDATE }, new int[] {
						android.R.id.text1, android.R.id.text2 });
		itemList.setAdapter(simpleAdapter);
		itemList.setOnItemClickListener(this);
		itemList.setSelection(0);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {

		Intent intent = new Intent();
		intent.setClass(this, ShowDescriptionActivity.class);
		Bundle bundle = new Bundle();
		bundle.putString("title", feed.getItem(position).getTitle());
		bundle.putString("description",feed.getItem(position).getDescription());
		bundle.putString("link", feed.getItem(position).getLink());
		bundle.putString("pubdate", feed.getItem(position).getPubdate());
		// 用android.intent.extra.INTENT的名字来传递参数
		intent.putExtra("android.intent.extra.rssItem", bundle);
		startActivityForResult(intent, 0);
	}

}

编写ShowDescriptionActivity,用于显示每条新闻的详情,具体代码如下:

 

ShowDescriptionActivity.java:

package com.example.rssnews;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ShowDescriptionActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_description);
        String content =null;

        Intent intent = getIntent();
        if(intent!=null){
        	Bundle bundle = intent.getBundleExtra("android.intent.extra.rssItem");
        	if(bundle==null){
        		content = "不好意思程序出错啦";
        	}else{
        		content = bundle.getString("title") + "nn"
						+ bundle.getString("pubdate") + "nn"
						+ bundle.getString("description").replace('n', ' ')
						+ "nn详细信息请访问以下网址:n" + bundle.getString("link");
        	}
        }else{
        	content = "不好意思程序出错啦";
        }

        TextView contentText = (TextView) this.findViewById(R.id.content);
        contentText.setText(content);

        Button backButton = (Button) this.findViewById(R.id.back);
        backButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				finish();
			}
		});
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

 

最 后,不要忘了在AndroidManifest.xml文件中添加访问网络的权限:<uses-permission android:name=”android.permission.INTERNET”/>这样,自此一个简易的RSS阅读器就完成了,还有很多 需要完善的地方,感兴趣的朋友可以在这个基础上进一步完善~相应的项目已上传至csdn资源中、欢迎下载

推荐阅读:

  1. http://www.ourunix.org/android/post/57.html
  2. http://architecture.riaos.com/?p=3003376
  3. http://www.ibm.com/developerworks/cn/education/xml/x-androidrss/section5.html
  4. http://www.jizhuomi.com/android/example/146.html

[转载]Android Dialog用法 - YUZHIBOYI的专栏 - 博客频道 - CSDN.NET

mikel阅读(975)

[转载]Android Dialog用法 – YUZHIBOYI的专栏 – 博客频道 – CSDN.NET.

摘要: 创建对话框 一个对话框一般是一个出现在当前Activity之上的一个小窗口. 处于下面的Activity失去焦点, 对话框接受所有的用户交互. 对话框一般用于提示信息和与当前应用程序直接相关的小功能.Android API 支持下列类型 …

 

创建对话框
  一个对话框一般是一个出现在当前Activity之上的一个小窗口. 处于下面的Activity失去焦点, 对话框接受所有的用户交互. 对话框一般用于提示信息和与当前应用程序直接相关的小功能.
Android API 支持下列类型的对话框对象:
警告对话框 AlertDialog:  一个可以有0到3个按钮, 一个单选框或复选框的列表的对话框. 警告对话框可以创建大多数的交互界面, 是推荐的类型.
进度对话框 ProgressDialog:  显示一个进度环或者一个进度条. 由于它是AlertDialog的扩展, 所以它也支持按钮.
日期选择对话框 DatePickerDialog:  让用户选择一个日期.
时间选择对话框 TimePickerDialog:  让用户选择一个时间.
如果你希望自定义你的对话框, 可以扩展Dialog类.
Showing a Dialog 显示对话框
一个对话框总是被创建和显示为一个Activity的一部分. 你应该在Activity的onCreateDialog(int)中创建对话框. 当你使用这个回调函数时,Android系统自动管理每个对话框的状态并将它们和Activity连接, 将Activity变为对话框的”所有者”. 这样,每个对话框从Activity继承一些属性. 例如,当一个对话框打开时, MENU键会显示Activity的菜单, 音量键会调整Activity当前使用的音频流的音量.
注意: 如果你希望在onCreateDialog()方法之外创建对话框, 它将不会依附在Activity上. 你可以使用setOwnerActivity(Activity)来将它依附在Activity上.
当你希望显示一个对话框时, 调用showDialog(int)并将对话框的id传给它.
当一个对话框第一次被请求时,Android调用onCreateDialog(int). 这里是你初始化对话框的地方. 这个回调函数传入的id和showDialog(int)相同. 创建对话框之后,将返回被创建的对象.
在对话框被显示之前,Android还会调用onPrepareDialog(int, Dialog). 如果你希望每次显示对话框时有动态更改的内容, 那么就改写这个函数. 该函数在每次一个对话框打开时都调用. 如果你不定义该函数,则对话框每次打开都是一样的. 该函数也会传入对话框的id以及你在onCreateDialog()中创建的Dialog对象.
最好的定义onCreateDialog(int) 和onPrepareDialog(int, Dialog) 的方法就是使用一个switch语句来检查传入的id. 每个case创建相应的对话框. 例如, 一个游戏使用两个对话框: 一个来指示游戏暂停,另一个指示游戏结束. 首先, 为它们定义ID:static final int DIALOG_PAUSED_ID = 0;
static final int DIALOG_GAMEOVER_ID = 1;
然后, 在onCreateDialog(int)中加入一个switch语句:

  1. protected Dialog onCreateDialog(int id) {  
  2.     Dialog dialog;  
  3.     switch(id) {  
  4.     case DIALOG_PAUSED_ID:  
  5.         // do the work to define the pause Dialog  
  6.         break;  
  7.     case DIALOG_GAMEOVER_ID:  
  8.         // do the work to define the game over Dialog  
  9.         break;  
  10.     default:  
  11.         dialog = null;  
  12.     }  
  13.     return dialog;  
  14. }   

 

  注意: 在这个例子中, case语句为空因为定义Dialog的程序在后面会有介绍.
在需要显示对话框是, 调用showDialog(int), 传入对话框的id:
showDialog(DIALOG_PAUSED_ID);Dismissing a Dialog 解除对话框
当你准备关闭对话框时, 你可以使用dismiss()函数. 如果需要的话, 你也可以从Activity调用dismissDialog(int), 二者效果是一样的.
如果你使用onCreateDialog(int)来管理你的对话框的状态, 那么每次你的对话框被解除时, 该对话框对象的状态会被Activity保存. 如果你决定你不再需要这个对象或者需要清除对话框的状态, 那么你应该调用 removeDialog(int). 这将把所有该对象的内部引用移除, 如果该对话框在显示的话将被解除.
Using dismiss listeners 使用解除监听器
如果你希望在对话框解除时运行某些程序, 那么你应该给对话框附加一个解除监听器.
首先定义DialogInterface.OnDismissListener接口. 这个接口只有一个方法, onDismiss(DialogInterface), 该方法将在对话框解除时被调用.
然后将你的OnDismissListener实现传给setOnDismissListener().
然而,注意对话框也可以被”取消”. 这是一个特殊的情形, 它意味着对话框被用户显式的取消掉. 这将在用户按下”back”键时, 或者对话框显式的调用cancel()(按下对话框的cancel按钮)时发生. 当一个对话框被取消时, OnDismissListener将仍然被通知, 但如果你希望在对话框被显示取消(而不是正常解除)时被通知, 则你应该使用setOnCancelListener()注册一个DialogInterface.OnCancelListener.
Creating an AlertDialog 创建警告对话框
An AlertDialog is an extension of the Dialog class. It is capable of constructing most dialog user interfaces and is the suggested dialog type. You should use it for dialogs that use any of the following features:
一个警告对话框是对话框的一个扩展. 它能够创建大多数对话框用户界面并且是推荐的对话框类新星. 对于需要下列任何特性的对话框,你都应该使用它:
一个标题
一条文字消息
1个-3个按钮
一个可选择的列表(单选框或者复选框)
要创建一个AlertDialog, 使用AlertDialog.Builder子类. 使用AlertDialog.Builder(Context)来得到一个Builder, 然后使用该类的公有方法来定义AlertDialog的属性. 设定好以后, 使用create()方法来获得AlertDialog对象.
下面的主题展示了如何为AlertDialog定义不同的属性, 使用AlertDialog.Builder类. 如果你使用这些示例代码, 你可以在onCreateDialog()中返回最后的Dialog对象来获得图片中对话框的效果.
Adding buttons 增加按钮

 

 

要创建一个如图所示的窗口, 使用set…Button()方法:

  1. AlertDialog.Builder builder = new AlertDialog.Builder(this);  
  2. builder.setMessage(“Are you sure you want to exit?”)  
  3.        .setCancelable(false)  
  4.        .setPositiveButton(“Yes”new DialogInterface.OnClickListener() {  
  5.            public void onClick(DialogInterface dialog, int id) {  
  6.                 MyActivity.this.finish();  
  7.            }  
  8.        })  
  9.        .setNegativeButton(“No”new DialogInterface.OnClickListener() {  
  10.            public void onClick(DialogInterface dialog, int id) {  
  11.                 dialog.cancel();  
  12.            }  
  13.        });  
  14. AlertDialog alert = builder.create();  

 

首先,使用setMessage(CharSequence)为对话框增加一条消 息。 然后, 开始连续调用方法, 使用setCancelable(boolean)将对话框设为不可取消(不能使用back键来取消)。对每一个按钮,使用set…Button() 方法,该方法接受按钮名称和一个DialogInterface.OnClickListener,该监听器定义了当用户选择该按钮时应做的动作。
注意:对每种按钮类型,只能为AlertDialog创建一个。也就是说,一个AlertDialog不能有两个以上的”positive”按钮。这使得可能的按钮数量最多为三个:肯定、否定、中性。这些名字和实际功能没有联系,但是将帮助你记忆它们各做什么事情。

 

Adding a list 增加列表

 

 

要创建一个具有可选项的AlertDialog,使用setItems()方法:

  1. final CharSequence[] items = {“Red”“Green”“Blue”};   
  2. AlertDialog.Builder builder = new AlertDialog.Builder(this);  
  3. builder.setTitle(“Pick a color”);  
  4. builder.setItems(items, new DialogInterface.OnClickListener() {  
  5.     public void onClick(DialogInterface dialog, int item) {  
  6.         Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();  
  7.     }  
  8. });  
  9. AlertDialog alert = builder.create();  

 

首先增加一个标题。然后使用setItems()增加一个可选列表,该列表接受一个选项名称的列表和一个DialogInterface.OnClickListener, 后者定义了选项对应的响应。

 


Adding checkboxes and radio buttons 增加单选框和复选框

 

 

 

要创建一个带有多选列表或者单选列表的对话框, 使用setMultiChoiceItems()和setSingleChoiceItems()方法。如果你在onCreateDialog()中创建 可选择列表, Android会自动管理列表的状态. 只要activity仍然活跃, 那么对话框就会记住刚才选中的选项,但当用户退出activity时,该选择丢失。
注意: 要在你的acitivity离开和暂停时保存选择, 你必须在activity的声明周期中正确的保存和恢复设置。为了永久性保存选择,你必须使用数据存储技术中的一种。
要创建一个具有单选列表的AlertDialog,只需将一个例子中的setItems()换成 setSingleChoiceItems():

 

  1. final CharSequence[] items = {“Red”“Green”“Blue”};   
  2. AlertDialog.Builder builder = new AlertDialog.Builder(this);  
  3. builder.setTitle(“Pick a color”);  
  4. builder.setSingleChoiceItems(items, –1new DialogInterface.OnClickListener() {  
  5.     public void onClick(DialogInterface dialog, int item) {  
  6.         Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();  
  7.     }  
  8. });  
  9. AlertDialog alert = builder.create();  

 

第二个参数是默认被选中的选项位置,使用“-1”来表示默认情况下不选中任何选项。

 


Creating a ProgressDialog 创建进度对话框

 

 

一个ProgressDialog(进度对话框)是AlertDialog的扩展。它可以显示一个进度的动画――进度环或者进度条。这个对话框也可以提供按钮,例如取消一个下载等。
打开一个进度对话框很简单,只需要调用 ProgressDialog.show()即可。例如,上图的对话框可以不通过onCreateDialog(int),而直接显示:
ProgressDialog dialog = ProgressDialog.show(MyActivity.this, “”,
“Loading. Please wait…”, true);
第一个参数是应用程序上下文。第二个为对话框的标题(这里为空),第三个为对话框内容, 最后一个为该进度是否为不可确定的(这只跟进度条的创建有关,见下一节)。
进度对话框的默认样式为一个旋转的环。如果你希望显示进度值,请看下一节。
Showing a progress bar 显示进度条
使用一个动画进度条来显示进度:
使用 ProgressDialog(Context)构造函数来初始化一个ProgressDialog对象。
将进度样式设置为”STYLE_HORIZONTAL”,使用setProgressStyle(int)方法。并且设置其它属性,例如内容等。
在需要显示时调用show()或者从onCreateDialog(int)回调函数中返回该ProgressDialog。
你可以使用 setProgress(int)或者incrementProgressBy(int)来增加显示的进度。
例如,你的设置可能像这样:ProgressDialog progressDialog;
progressDialog = new ProgressDialog(mContext);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage(“Loading…”);
progressDialog.setCancelable(false);
设置很简单。大部分创建进度对话框需要的代码是在更新它的进程中。你可能需要在一个新的线程中更新它,并使用Handler来将进度报告给Activity。如果你不熟悉使用Handler和另外的线程,请看下列例子,该例子使用了一个新的线程来更新进度。
Example ProgressDialog with a second thread 例–使用一个线程来显示进度对话框
这个例子使用一个线程来跟踪一个进程的进度(其实为从1数到100)。每当进度更新时,该线程通过Handler给主activity发送一个消息。 主Activity更新ProgressDialog.package com.example.progressdialog;

 

  1. import android.app.Activity;  
  2. import android.app.Dialog;  
  3. import android.app.ProgressDialog;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.Button;  
  10. public class NotificationTest extends Activity {  
  11.     static final int PROGRESS_DIALOG = 0;  
  12.     Button button;  
  13.     ProgressThread progressThread;  
  14.     ProgressDialog progressDialog;  
  15.     /** Called when the activity is first created. */  
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.main);   
  19.         // Setup the button that starts the progress dialog  
  20.         button = (Button) findViewById(R.id.progressDialog);  
  21.         button.setOnClickListener(new OnClickListener(){  
  22.             public void onClick(View v) {  
  23.                 showDialog(PROGRESS_DIALOG);  
  24.             }  
  25.         });  
  26.     }  
  27.     protected Dialog onCreateDialog(int id) {  
  28.         switch(id) {  
  29.         case PROGRESS_DIALOG:  
  30.             progressDialog = new ProgressDialog(NotificationTest.this);  
  31.             progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
  32.             progressDialog.setMessage(“Loading…”);  
  33.             progressThread = new ProgressThread(handler);  
  34.             progressThread.start();  
  35.             return progressDialog;  
  36.         default:  
  37.             return null;  
  38.         }  
  39.     }   
  40.     // Define the Handler that receives messages from the thread and update the progress  
  41.     final Handler handler = new Handler() {  
  42.         public void handleMessage(Message msg) {  
  43.             int total = msg.getData().getInt(“total”);  
  44.             progressDialog.setProgress(total);  
  45.             if (total >= 100){  
  46.                 dismissDialog(PROGRESS_DIALOG);  
  47.                 progressThread.setState(ProgressThread.STATE_DONE);  
  48.             }  
  49.         }  
  50.     };   
  51.     /** Nested class that performs progress calculations (counting) */  
  52.     private class ProgressThread extends Thread {  
  53.         Handler mHandler;  
  54.         final static int STATE_DONE = 0;  
  55.         final static int STATE_RUNNING = 1;  
  56.         int mState;  
  57.         int total;  
  58.         ProgressThread(Handler h) {  
  59.             mHandler = h;  
  60.         }  
  61.         public void run() {  
  62.             mState = STATE_RUNNING;     
  63.             total = 0;  
  64.             while (mState == STATE_RUNNING) {  
  65.                 try {  
  66.                     Thread.sleep(100);  
  67.                 } catch (InterruptedException e) {  
  68.                     Log.e(“ERROR”“Thread Interrupted”);  
  69.                 }  
  70.                 Message msg = mHandler.obtainMessage();  
  71.                 Bundle b = new Bundle();  
  72.                 b.putInt(“total”, total);  
  73.                 msg.setData(b);  
  74.                 mHandler.sendMessage(msg);  
  75.                 total++;  
  76.             }  
  77.         }  
  78.         /* sets the current state for the thread, 
  79.          * used to stop the thread */  
  80.         public void setState(int state) {  
  81.             mState = state;  
  82.         }  
  83.     }  
  84. }  

 

Creating a Custom Dialog 创建自定义对话框

 

 

如果你想自定义一个对话框,你可以使用布局元素来创造你的对话框的布局。定义好布局后,将根View对象或者布局资源ID传给setContentView(View).
例如,创建如图所示的对话框:
创建一个xml布局custom_dialog.xml:

 

  1. http://schemas.android.com/apk/res/android”  
  2.               android:id=“@+id/layout_root”  
  3.               android:orientation=“horizontal”  
  4.               android:layout_width=“fill_parent”  
  5.               android:layout_height=“fill_parent”  
  6.               android:padding=“10dp”  
  7.               >  
  8.                    android:layout_width=“wrap_content”  
  9.                android:layout_height=“fill_parent”  
  10.                android:layout_marginRight=“10dp”  
  11.                />  
  12.                   android:layout_width=“wrap_content”  
  13.               android:layout_height=“fill_parent”  
  14.               android:textColor=“#FFF”  
  15.               />  

 

该xml定义了一个LinearLayout中的一个ImageView 和一个TextView。
将以上布局设为对话框的content view,并且定义ImageView 和 TextView的内容:

  1. Context mContext = getApplicationContext();  
  2. Dialog dialog = new Dialog(mContext);   
  3. dialog.setContentView(R.layout.custom_dialog);  
  4. dialog.setTitle(“Custom Dialog”);  
  5. TextView text = (TextView) dialog.findViewById(R.id.text);  
  6. text.setText(“Hello, this is a custom dialog!”);  
  7. ImageView image = (ImageView) dialog.findViewById(R.id.image);  
  8. image.setImageResource(R.drawable.android);  

 

在初始化Dialog之后,使用setContentView(int),将布局资源id传给它。现在Dialog有一个定义好的布局,你可以使用findViewById(int)来找到该元素的id并修改它的内容。
使用前面所讲的方法显示对话框。
一个使用Dialog类建立的对话框必须有一个标题。如果你不调用setTitle(),那么标题区域会保留空白。如果你不希望有一个标题,那么你应 该使用AlertDialog类来创建自定义对话框。然而,由于一个AlertDialog使用AlertDialog.Builder类来建立最方便, 所以你没有方法使用setContentView(int),而是只能使用setView(View)。该方法接受一个View对象,所以你需要从xml 中展开你的根View。
要展开一个xml布局,使用 getLayoutInflater() (或 getSystemService())取得LayoutInflater,然后调用inflate(int, ViewGroup),第一个参数为布局id,而第二个参数为根view的id。现在,你可以使用展开后的布局来找到View对象并定义 ImageView和TextView元素的内容。然后实例化AlertDialog.Builder并使用setView(View)来为对话框设置展 开后的布局。例如:

  1. AlertDialog.Builder builder;  
  2. AlertDialog alertDialog;   
  3. Context mContext = getApplicationContext();  
  4. LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);  
  5. View layout = inflater.inflate(R.layout.custom_dialog,  
  6.                                (ViewGroup) findViewById(R.id.layout_root));  
  7. TextView text = (TextView) layout.findViewById(R.id.text);  
  8. text.setText(“Hello, this is a custom dialog!”);  
  9. ImageView image = (ImageView) layout.findViewById(R.id.image);  
  10. image.setImageResource(R.drawable.android);  
  11. builder = new AlertDialog.Builder(mContext);  
  12. builder.setView(layout);  
  13. alertDialog = builder.create();  

使用AlertDialog来自定义对话框,可以利用其内置特性例如按钮、选择列表、标题、图标等。

显示对话框

 

    对话框经常作为Activity的一部分来创建和显示。你通常应该从protected Dialog Activity.onCreateDialog (int id) 回调方法里创建对话框。当你使用这个回调函数时,Android系统会有效的设置这个Activity为每个对话框的所有者,从而自动管理每个对话框的状态并挂靠到Activity上。这样,每个对话框继承这个Activity的特定属性。比如,当一个对话框打开时,菜单键显示为这个Activity定义的选项菜单,音量键修改Activity使用的音频流

 

  1. 注意: 如果你决定在onCreateDialog()方法之外创建一个对话框,它将不会被附着到活动上。不过,你可以通过setOwnerActivity(Activity)把它附着到一个活动上。 

 

    当你想要显示一个对话框时,调用showDialog(int id) 方法并传递一个唯一标识这个对话框的整数。

 

    当对话框第一次被请求时,Android从你的Activity中调用onCreateDialog(int id),你应该在这里初始化这个对话框Dialog。这个回调方法被传以和showDialog(int id)相同的ID。当你创建这个对话框后,在Activity的最后返回这个对象。

 

    在对话框被显示之前,Android还调用了可选的回调函数onPrepareDialog(int id, Dialog). 如果你想在每一次对话框被打开时改变它的任何属性,你可以定义这个方法。这个方法在每次打开对话框时被调用,而onCreateDialog(int) 仅在对话框第一次打开时被调用如果你不定义onPrepareDialog(),那么这个对话框将保持和上次打开时一样。这个方法也被传递以对话框的ID,和在onCreateDialog()中创建的对话框对象。(个人理解是,在本Activity里第一次show某个Dialog,则先调用onCreateDialog,得到返回的Dialog对象并挂靠在Activity,保存Dialog对象的引用,然后才显示Dialog。这样子,下次再show Dialog就不用重新创建Dialog对象,而是重用旧的)

 

    定义onCreateDialog(int) 和 onPrepareDialog(int, Dialog) 回 调函数的最佳方法是使用一个switch 语句来检查传递进来的id 参数。每个case 应该检查一个唯一的对话框ID然后创建和定义相应的对话框。比如,想象一下一个游戏使用两个不同的对话框:一个用来指示这个游戏已经暂停而另一个来指示游 戏结束。首先,为每个对话框定义一个常量:

 

  1. static final int DIALOG_PAUSED_ID = 0
  2. static final int DIALOG_GAMEOVER_ID = 1

 

然后,为每一个ID用一个switch case定义这个onCreateDialog(int) 回调函数:

 

  1. protected Dialog onCreateDialog(int id) { 
  2.     Dialog dialog; 
  3.     switch(id) { 
  4.     case DIALOG_PAUSED_ID: 
  5.         // do the work to define the pause Dialog 
  6.         break
  7.     case DIALOG_GAMEOVER_ID: 
  8.         // do the work to define the game over Dialog 
  9.         break
  10.     default
  11.         dialog = null
  12.     } 
  13.     return dialog; 

 

当是时候显示其中之一的对话框时,使用对话框ID调用showDialog(int):

 

  1. showDialog(DIALOG_PAUSED_ID); 

 

 

 

消除对话框Dismissing a Dialog

 

    当你准备关闭对话框时,你可以通过对这个对话框调用dismiss()来消除它。如果需要,你还可以从这个Activity中调用dismissDialog(int id) 方法,这实际上将为你对这个对话框调用dismiss() 方法。

 

    如果你想使用onCreateDialog(int id) 方法来管理你对话框的状态(就如同在前面的章节讨论的那样),然后每次你的对话框消除的时候,这个对话框对象的状态将由该Activity保留。如果你决定不再需要这个对象或者清除该状态是重要的,那么你应该调用removeDialog(int id)。这将删除任何内部对象引用而且如果这个对话框正在显示,它将被消除。

 

 

 

使用消除侦听器Using dismiss listeners

 

    如果你希望你的应用程序在一个对话框消亡的时候执行一些流程,那么你应该附着一个on-dismiss侦听器到对话框上。

 

  1. @Override 
  2. protected void onPrepareDialog(int id, Dialog dialog) { 
  3.     switch(id){ 
  4.     case PROGRESS_DIALOG: 
  5.         dialog.setOnDismissListener(new DialogInterface.OnDismissListener()
  6.             @Override 
  7.             public void onDismiss(DialogInterface dialog) { 
  8.                 Toast.makeText(getApplicationContext(), 
  9.                         “dismiss listener!”
  10.                         Toast.LENGTH_SHORT) 
  11.                 .show(); 
  12.             } 
  13.         }); 
  14.     } 

 

    然而, 请注意对话框也可以被“取消”。这是一个表明对话框被用户显示取消的特殊情况。这将在用户按“返回”按钮时发生,或者这个对话框显示的调用cancel() (也许通过对话框上的一个“取消”按钮)。当一个对话框被取消时,这个OnDismissListener 依然会被通知到,但是如果你希望在对话框被显示取消时被通知到(而不是通常的消除方式),那么你应该通过setOnCancelListener()注册一个DialogInterface.OnCancelListener 

 

    目前个人学习发现,一般情况下,调用dialog.cancel()就会触发onCancelLister。而点击AlertDialog的NegativeButton (Cancel/No)是不会触发的。对于setOnCancelListener()要注意的是,这里有两个setOnCancelListener(),但返回值不同:

 

  1. //AlertDialog.Builder调用的 
  2. public AlertDialog.Builder setOnCancelListener (DialogInterface.OnCancelListener onCancelListener) 
  3.  
  4. //Dialog调用的 
  5. public void setOnCancelListener (DialogInterface.OnCancelListener listener) 

 

 

 

警告对话框AlertDialog的使用

 

    为了创建一个警告对话框,使用AlertDialog.Builder 子类。通过AlertDialog.Builder(Context)获取一个构造器然后使用这个类的公共方法来定义警告对话框的所有属性。当得到构造器后,通过create().方法来获取警告对话框对象。有时我是不调用create()的,而是在设置好了后直接调用show()显示AlertDialog。

 

Dialog_button Dialog_button Dialog_button

 

增加按钮Adding buttons

 

    这就是我一开始很想知道的究竟如何添加Yes/No,Ok/Cancel这样的按钮。原来是通过setPositiveButton(…)响应Yes/Ok的点击,setNeutralButton(…)响应中立行为的点击,setNegativeButton(…)响应No/Cancel的点击。注意,只能各自设置一个按钮来响应点击事件。

 

  1. AlertDialog.Builder builder = new AlertDialog.Builder(this); 
  2. builder.setMessage(“Are you sure you want to exit?”
  3.        .setCancelable(false
  4.        .setPositiveButton(“Yes”new DialogInterface.OnClickListener() { 
  5.            public void onClick(DialogInterface dialog, int id) { 
  6.                 MyActivity.this.finish(); 
  7.            } 
  8.        }) 
  9.        .setNegativeButton(“No”new DialogInterface.OnClickListener() { 
  10.            public void onClick(DialogInterface dialog, int id) { 
  11.                 dialog.cancel(); 
  12.            } 
  13.        }); 
  14. AlertDialog alert = builder.create(); 

 

    首先,为这个对话框添加一个消息setMessage(CharSequence)。然后,开始函数链并设置该对话框为不能取消not cancelable (因此用户不能使用返回按钮关闭这个对话框)。对每个按钮,使用任一set…Button() 方法,比如setPositiveButton(),该方法接受按钮名称以及一个定义用户选中按钮后所采取动作的DialogInterface.OnClickListener

 

增加一个列表Adding a list

 

  1. final CharSequence[] items = {“Red”“Green”“Blue”}; 
  2.   
  3. AlertDialog.Builder builder = new AlertDialog.Builder(this); 
  4. builder.setTitle(“Pick a color”); 
  5. builder.setItems(items, new DialogInterface.OnClickListener() { 
  6.     public void onClick(DialogInterface dialog, int item) { 
  7.         Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show(); 
  8.     } 
  9. }); 
  10. AlertDialog alert = builder.create(); 

 

    首先,用setTitle(CharSequence)方法给对话框添加一个标题。然后,添加用setItems()添加一个可选项列表,该列表接受一组显示的items和一个DialogInterface.OnClickListener 来定义用户选中按钮后所采取动作。

 

增加复选框和单选按钮

 

    要在对话框里创建一个多选项列表(checkboxes)或者单选项(radio buttons),可分别调用setMultiChoiceItems() 和setSingleChoiceItems() 方法。如果你在onCreateDialog()回调函数中创建这些可选列表,Android会帮你管理列表状态。只要这个活动是激活的,对话框会记住之前选中的items,但如果用户退出这个活动,用户选择将丢失。

 

    注意: 为了在用户离开或暂停这个活动的时候能够保存选择,你必须通过活动生命期Activity Lifecycle来恰当的保存和恢复设置。为了永久保存选项,即使活动进程被完全终止,你需要使用数据存储Data Storage技术。

 

  1. final CharSequence[] items = {“Red”“Green”“Blue”}; 
  2.   
  3. AlertDialog.Builder builder = new AlertDialog.Builder(this); 
  4. builder.setTitle(“Pick a color”); 
  5. builder.setSingleChoiceItems(items, –1new DialogInterface.OnClickListener() { 
  6.     public void onClick(DialogInterface dialog, int item) { 
  7.         Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show(); 
  8.     } 
  9. }); 
  10. AlertDialog alert = builder.create(); 

 

    setSingleChoiceItems() 的第二个参数是一个checkedItem整型数值,指示了基于0的缺省选择项的位置。“-1”代表不会有默认选择项。

 

 

 

进度对话框Progress Dialog的使用

 

    ProgressDialogAlertDialog类的一个扩展,可以为一个未定义进度的任务显示一个旋转轮形状的进度动画,或者为一个指定进度的任务显示一个进度条。

 

    可以简单地通过调用ProgressDialog.show()方法来显示一个进度对话框,而通过onCreateDialog(int)回调管理这个对话框是可选的,如下所示:

 

  1. ProgressDialog.show(this// context 
  2.     “”// title 
  3.     “Loading. Please wait…”// message 
  4.     true); //进度是否是不确定的,这只和创建进度条有关 

 

进度对话框的缺省类型是一个旋转轮,运行看到的是以下效果:

 

ProgressDialog

 

///////////////////////////////////////////////////////////////setOnDismissListener作用///////////////////////////////////////////////////////////////////////

 

先说下Activity里面有showDialog和onCreateDialog两个函数:

 

在某个地方你想弹出一个dialog,可以这样做,在onCreateDialog里面new一个Diloag,然后在想要触发dialog的地方调用showDialog即可。

 

showDialog(id)先是从缓存也就是hashMap里面查看有木有这个id也就key对应的dialog对象,如果有则取出来,如果没有则new一个dialog对象

 

这样就会产生一个问题,假若说这个dialog自带数据,比方说日期dialog,我修改了原来的日期,但是我点击取消或者点击back键,之后,然后又触发改日期dialog,结果日期

不是原来保存的那个日期,而是刚才我修改之后的日期,这里就出问题了,原因是原来缓存里的 dialog没有置null,再次触发dialog选择了重用以前的对象,所以造成了修改之后没有保存的数据替代保存的数据假象,解决的办法也很简单注册 一个监听器即可setOnDismissListener在里面dismissdialog即可。或者removedialog。这样无论是点击back 键还是点击取消button都不会出现修改之后没有保存的数据成了默认显示在dialog里面

[转载]chrome中remove函数的坑 - 斯诺登 - 博客园

mikel阅读(1388)

[转载]chrome中remove函数的坑 – 斯诺登 – 博客园.

起因

测试的浏览器:ie7 ie8 safari firefox chrome

出现问题代码:

<a href="#" id="test" onclick="remove();">test</a>
<script>
var remove=function(){
    alert('111');
}
</script>

结果:

ie7 ie8 safari firefox 均正常弹出 “111”

chrome <a>元素被移除

http://www.cnblogs.com/danhuang/

经过

首先我怀疑remove是JavaScript保留字(关键字),经证实如我所知remove并非是JavaScript保留字(关键字)

javascript的保留字(关键字)请参考:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Reserved_Words

然后在chrome的界面按下F1到帮助里面去搜,也没搜到相关的东西。于是决定自己研究。。。

删掉javascript代码,打开开发者工具,输入:

window.remove

看一下remove函数有没有被定义了,结果是 undefined

说明没有被定义,其实这步是多余的。。。

http://www.cnblogs.com/danhuang/

继续看<a>元素的__proto__

document.getElementById("test").__proto__

在 __proto__HTMLElement 下的 __proto__Element 下,我们看到了

remove: function remove() { [native code] }

至此,真相浮现出来了

http://www.cnblogs.com/danhuang/

结果

解决办法1:改函数名,什么drop啊,remove1啊之类的,记住别用delete,哈哈~~

解决办法2(推荐):使用命名空间(namespace

<a href="#" onclick="XW.remove();">test</a>
<script>
XW = {};
// add remove() to the custom namespace, not the global namespace
XW.remove = function() {
// some code here
};
</script>

http://www.cnblogs.com/danhuang/

探究

chromium Issue:https://code.google.com/p/chromium/issues/detail?id=170395

webkit changeset:http://trac.webkit.org/changeset/129400

http://www.cnblogs.com/danhuang/

教训

详细整洁而且有意义的命名很重要。

 

转载请注明出处,谢谢。

[转载]【总结】IE和Firefox的Javascript兼容性总结 - 漫凯维奇 - 博客园

mikel阅读(954)

[转载]【总结】IE和Firefox的Javascript兼容性总结 – 漫凯维奇 – 博客园.

  长久以来JavaScript兼容性一直是Web开发者的一个主要问题。在正式规范、事实标准以及各种实现之间的存在的差异让许多开发者日夜煎熬。为此,主要从以下几方面差异总结IE和Firefox的JavaScript兼容性:

一、函数和方法差异

二、样式访问和设置

三、DOM方法及对象引用

四、事件处理

五、其他差异的兼容处理

 

一、函数和方法差异

1. getYear()方法

【分析说明】先看一下以下代码:

var year=new Date().getYear();
document.write(year);

在IE中得到的日期是”2010″,在Firefox中看到的日期是”110″,主要是因为在 Firefox 里面 getYear 返回的是 “当前年份-1900” 的值。

【兼容处理】

加上对年份的判断,如:

var year=new Date().getYear();
year
= (year<1900?(1900+year):year);
document.write(year);

也可以通过 getFullYear getUTCFullYear 去调用:

var year =new Date().getFullYear();
document.write(year);

 

2. eval()函数

【分析说明】在IE中,可以使用eval(“idName”)或getElementById(“idName”)来取得id为idName的 HTML对象;Firefox下只能使用getElementById(“idName”)来取得id为idName的HTML对象。

【兼容处理】统一用getElementById(“idName”)来取得id为idName的HTML对象。

 

3. const声明

【分析说明】在 IE 中不能使用 const 关键字。如:

const constVar =32;

在IE中这是语法错误。

【兼容处理】不使用 const ,以 var 代替。

 

4. var

【分析说明】请看以下代码:

echo=function(str){
document.write(str);
}
pre>

这个函数在IE上运行正常,Firefox下却报错了。

【兼容处理】而在echo前加上var就正常了,这个就是我们提到var的目的。

 

5. const 问题

【分析说明】在 IE 中不能使用 const 关键字。如 const constVar = 32; 在IE中这是语法错误。

【解决方法】不使用 const ,以 var 代替。

 

二、样式访问和设置

1. CSS的”float”属性

【分析说明】Javascript访问一个给定CSS 值的最基本句法是:object.style.property,但部分CSS属性跟Javascript中的保留字命名相同,如”float”,”for”,”class”等,不同浏览器写法不同。

在IE中这样写:

document.getElementById(header).style.styleFloat =left;

在Firefox中这样写:

document.getElementById(header).style.cssFloat =left;

【兼容处理】在写之前加一个判断,判断浏览器是否是IE:

if(document.all){
  document.getElementById(header).style.styleFloat =left;
}
else{
  document.getElementById(header).style.cssFloat =left;
}

 

2. 访问<label>标签中的”for”

【分析说明】和”float”属性一样,同样需要使用不现的句法区分来访问<label>标签中的”for”。

在IE中这样写:

var myObject = document.getElementById(myLabel);
var myAttribute = myObject.getAttribute(htmlFor);

在Firefox中这样写:

var myObject = document.getElementById(myLabel);
var myAttribute = myObject.getAttribute(for);

【兼容处理】解决的方法也是先 判断浏览器类型。

 

3. 访问和设置class属性

【分析说明】同样由于class是Javascript保留字的原因,这两种浏览器使用不同的 JavaScript 方法来获取这个属性。

IE8.0之前的所有IE版本的写法:

var myObject = document.getElementById(header);
var myAttribute = myObject.getAttribute(className);

适用于IE8.0 以及 firefox的写法:

var myObject = document.getElementById(header);
var myAttribute = myObject.getAttribute(class);

另外,在使用setAttribute()设置Class属性的时候,两种浏览器也存在同样的差异。

setAttribute(“className”,value);

这种写法适用于IE8.0之前的所有IE版本,注意:IE8.0也不支持”className”属性了。

setAttribute(“class”,value);适用于IE8.0 以及 firefox。

【兼容处理】

方法一,两种都写上:

var myObject = document.getElementById(header);
myObject.setAttribute(
class,classValue);
myObject.setAttribute(
className,classValue);
 //设置header的class为classValue

方法二,IE和FF都支持object.className,所以可以这样写:

var myObject = document.getElementById(header);
myObject.className
=classValue;//设置header的class为classValue

方法三,先判断浏览器类型,再根据浏览器类型采用对应的写法。

 

4. 对象宽高赋值问题

【分析说明】FireFox中类似 obj.style.height = imgObj.height 的语句无效。

【兼容处理】统一使用 obj.style.height = imgObj.height + ‘px’;

 

三、DOM方法及对象引用

1. getElementById

【分析说明】先来看一组代码:

<!– input对象访问1 –>
<input id=”id”type=”button”
value
=”click me” ōnclick=”alert(id.value)”/>

在Firefox中,按钮没反应,在IE中,就可以,因为对于IE来说,一个HTML 元素的 ID 可以直接在脚本中当作变量名来使用,而Firefox中不可以。

【兼容处理】尽量采用W3C DOM 的写法,访问对象的时候,用document.getElementById(“id”) 以ID来访问对 象,且一个ID在页面中必须是唯一的,同样在以标签名来访问对象的时候,用document.getElementsByTagName(“div”) [0] 。该方式得到较多浏览器的支持。

<!– input对象访问2 –>
<input id=”id”type=”button” value=”click me”
  onclick=”alert(document.getElementById(‘id’).value)” />

 

2. 集合类对象访问

【分析说明】IE下,可以使用()或[]获取集合类对象;Firefox下,只能使用[]获取集合类对象。如:

document.write(document.forms(formName).src);
//该写法在IE下能访问到Form对象的scrc属性

【兼容处理】将document.forms(“formName”)改为 document.forms[“formName”]。统一使用[]获取集合类对象。

 

3. frame的引用

【分析说明】IE可以通过id或者name访问这个frame对应的window对象,而Firefox只可以通过name来访问这个frame对应的window对象。

例如如果上述frame标签写在最上层的window里面的htm里面,那么可以这样访问:

IE: window.top.frameId或者window.top.frameName来访问这个window对象;

Firefox:只能这样window.top.frameName来访问这个window对象。

【兼容处理】使用frame的name来访问frame对象,另外,在IE和Firefox中都可以使用window.document.getElementById(”frameId”)来访问这个frame对象。

 

4. parentElement

【分析说明】IE中支持使用parentElement和parentNode获取父节点。而Firefox只可以使用parentNode。

【兼容处理】因为firefox与IE都支持DOM,因此统一使用parentNode来访问父节点。

 

5. table操作

【分析说明】IE下table中无论是用innerHTML还是appendChild插入<tr>都没有效果,而其他浏览器却显示正常。

【兼容处理】解决的方法是,将<tr>加到table的<tbody>元素中,如下面所示:

var row = document.createElement(tr);
var cell = document.createElement(td);
var cell_text = document.createTextNode(插入的内容);
cell.appendChild(cell_text);
row.appendChild(cell);
document.getElementsByTagName(
tbody)[0].appendChild(row);

 

6. 移除节点removeNode()和removeChild()

【分析说明】appendNode在IE和Firefox下都能正常使用,但是removeNode只能在IE下用。

removeNode方法的功能是删除一个节点,语法为node.removeNode(false)或者node.removeNode(true),返回值是被删除的节点。

removeNode(false)表示仅仅删除指定节点,然后这个节点的原孩子节点提升为原双亲节点的孩子节点。

removeNode(true)表示删除指定节点及其所有下属节点。被删除的节点成为了孤立节点,不再具有有孩子节点和双亲节点。

【兼容处理】Firefox中节点没有removeNode方法,只能用removeChild方法代替,先回到父节点,在从父节点上移除要移除的节点。

node.parentNode.removeChild(node); 
 // 为了在ie和firefox下都能正常使用,取上一层的父结点,然后remove。

 

7. childNodes获取的节点

【分析说明】childNodes的下标的含义在IE和Firefox中不同,看一下下面的代码:

复制代码
<ul id=”main”>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<input type=button value=”click me!” onclick=
“alert(document.getElementById(‘main’).childNodes.length)”>
复制代码

分别用IE和Firefox运行,IE的结果是3,而Firefox则是7。Firefox使用DOM规范,”#text”表示文本(实际是无意义的空格和换行等)在Firefox里也会被解析成一个节点,在IE里只有有实际意义的文本才会解析成”#text”。

【兼容处理】

方法一,获取子节点时,可以通过node.getElementsByTagName()来回避这个问题。但是 getElementsByTagName对复杂的DOM结构遍历明显不如用childNodes,因为childNodes能更好的处理DOM的层次结构。

方法二,在实际运用中,Firefox在遍历子节点时,不妨在for循环里加上:

if(childNode.nodeName==”#text) continue;//或者使用nodeType == 1。

这样可以跳过一些文本节点。

延伸阅读

IE和FireFox中的childNodes区别

 

8. Firefox不能对innerText支持

【分析说明】Firefox不支持innerText,它支持textContent来实现innerText,不过textContent没有像 innerText一样考虑元素的display方式,所以不完全与IE兼容。如果不用textContent,字符串里面不包含HTML代码也可以用 innerHTML代替。也可以用js写个方法实现,可参考《为firefox实现innerText属性》一文。

【兼容处理】通过判断浏览器类型来兼容:

if(document.all){
document.getElementById(
element).innerText =my text;
}
else{
document.getElementById(
element).textContent =my text;
}

 

四、事件处理

如果在使用javascript的时候涉及到event处理,就需要知道event在不同的浏览器中的差异,主要的JavaScript的事件模型有三种(参考《Supporting Three Event Models at Once》),它们分别是NN4、IE4+和W3C/Safar。

 

1. window.event

【分析说明】先看一段代码

function et()
{
alert(event);
//IE: [object]
}

以上代码在IE运行的结果是[object],而在Firefox无法运行。

因为在IE中event作为window对象的一个属性可以直接使用,但是在Firefox中却使用了W3C的模型,它是通过传参的方法来传播事件的,也就是说你需要为你的函数提供一个事件响应的接口。

【兼容处理】添加对event判断,根据浏览器的不同来得到正确的event:

function et()
{
evt
=evt?evt:(window.event?window.event:null); 
   //兼容IE和Firefox
alert(evt);
}

 

2. 键盘值的取得

【分析说明】IE和Firefox获取键盘值的方法不同,可以理解,Firefox下的event.which与IE下的event.keyCode相当。关于彼此不同,可参考《键盘事件中keyCode、which和charCode 的兼容性测试

【兼容处理】

复制代码
function myKeyPress(evt){
//兼容IE和Firefox获得keyBoardEvent对象
evt
= (evt) ? evt : ((window.event) ? window.event : “”
 //兼容IE和Firefox获得keyBoardEvent对象的键值
var key = evt.keyCode?evt.keyCode:evt.which; 
if(evt.ctrlKey && (key ==13|| key ==10)){ 
       //同时按下了Ctrl和回车键
//do something;
}
}
复制代码

 

3. 事件源的获取

【分析说明】在使用事件委托的时候,通过事件源获取来判断事件到底来自哪个元素,但是,在IE下,event对象有srcElement属性,但是没有target属性;Firefox下,even对象有target属性,但是没有srcElement属性。

【兼容处理】

ele=function(evt){ //捕获当前事件作用的对象
evt=evt||window.event;
  return
   (obj=event.srcElement?event.srcElement:event.target;);
}

 

4. 事件监听

【分析说明】在事件监听处理方面,IE提供了attachEvent和detachEvent两个接口,而Firefox提供的是addEventListener和removeEventListener。

【兼容处理】最简单的兼容性处理就是封装这两套接口:

复制代码
function addEvent(elem, eventName, handler) {
if (elem.attachEvent) {
elem.attachEvent(
on+ eventName, function(){
                    handler.call(elem)});
     //此处使用回调函数call(),让this指向elem
  } elseif (elem.addEventListener) {
elem.addEventListener(eventName, handler,
false);
}
}
function removeEvent(elem, eventName, handler) {
if (elem.detachEvent) {
elem.detachEvent(
on+ eventName, function(){
                    handler.call(elem)});
     //此处使用回调函数call(),让this指向elem
  } elseif (elem.removeEventListener) {
elem.removeEventListener(eventName, handler,
false);
}
}
复制代码

需要特别注意,Firefox下,事件处理函数中的this指向被监听元素本身,而在IE下则不然,可使用回调函数call,让当前上下文指向监听的元素。

 

5. 鼠标位置

【分析说明】IE下,even对象有x,y属性,但是没有pageX,pageY属性;Firefox下,even对象有pageX,pageY属性,但是没有x,y属性。

【兼容处理】使用mX(mX = event.x ? event.x : event.pageX;)来代替IE下的event.x或者Firefox下的event.pageX。复杂点还要考虑绝对位置。

复制代码
function getAbsPoint(e){
var x = e.offsetLeft, y = e.offsetTop;
while (e = e.offsetParent) {
x
+= e.offsetLeft;
y
+= e.offsetTop;
}
alert(
x:+ x +,+y:+ y);
}
复制代码

 

五、其他差异的兼容处理

1. XMLHttpRequest

【分析说明】new ActiveXObject(“Microsoft.XMLHTTP”);只在IE中起作用,Firefox不支持,但支持XMLHttpRequest。

【兼容处理】

 

复制代码
function createXHR() {
var xhr=null;
if(window.XMLHttpRequest){
xhr
=new ActiveXObject(Msxml2.XMLHTTP);
}
else{
try {
xhr
=new ActiveXObject(Microsoft.XMLHTTP);
}
catch() {
xhr
=null;
}
}
if(!xhr)return;
return xhr;
}
复制代码

 

2. 模态和非模态窗口

【分析说明】IE中可以通过showModalDialog和showModelessDialog打开模态和非模态窗口,但是Firefox不支持。

【解决办法】直接使用window.open(pageURL,name,parameters)方式打开新窗口。 如果需要传递参数,可以使用frame或者iframe。

 

3. input.type属性问题

IE下 input.type属性为只读,但是Firefox下可以修改

 

4. 对select元素的option操作

设置options,IE和Firefox写法不同:

Firefox:可直接设置

option.text =foooooooo;

IE:只能设置

option.innerHTML =fooooooo;

删除一个select的option的方法:

Firefox:可以

select.options.remove(selectedIndex);

IE7:可以用

select.options[i] =null;

IE6:需要写

select.options[i].outerHTML =null;

 

5. img对象alt和title的解析

【分析说明】img对象有alt和title两个属性,区别在于,alt:当照片不存在或者load错误时的提示。

title:照片的tip说明, 在IE中如果没有定义title,alt也可以作为img的tip使用,但是在Firefox中,两者完全按照标准中的定义使用

在定义img对象时。

【兼容处理】最好将alt和title对象都写全,保证在各种浏览器中都能正常使用 。

 

6. img的src刷新问题

【分析说明】先看一下代码:

<img id=”pic” onclick= “this.src= ‘a.jpg'”
  src=”aa.jpg style=”cursor: pointer”/>

在IE 下,这段代码可以用来刷新图片,但在FireFox下不行。主要是缓存问题。

【兼容处理】在地址后面加个随机数就解决了:

<img id=”pic” onclick= “javascript:this.src=this.src+’?’
     +Math.random()”src=”a.jpg” style=”cursor: pointer”/>

 

总结

IE和Firefox的Javascript方面存在着不少的差异,要做到兼容,我觉得很有必要把一些常见的整理成一个js库,如DOM的操 作,事件的处理,XMLHttpRequest请求等,或者也可以选择使用现有的一些库(如JQuery,YUI,ExtJs等),不过我觉得还是有必要 了解一下这些差异,这样对于我们参加兼容性和可用性代码很有帮助。

办法总比问题多,无论浏览器兼容如何折腾人,做前端开发的总能迎刃而解的!

 

本文地址:http://www.cnblogs.com/wiky/archive/2010/01/09/IE-and-Firefox-Javascript-compatibility.html 

PS:本文由维奇总结,如有转载请注明出处,谢谢!

 

参考资料

Javascript中的常见问题

Javascript兼容多种浏览器

JavaScript兼容性小议

IE & Firefox 兼容性问题

IE与Firefox在JavaScript上的7个不同句法

[转载]仅此一文让你明白ASP.NET MVC原理 - .CPP - 博客园

mikel阅读(996)

[转载]仅此一文让你明白ASP.NET MVC原理 – .CPP – 博客园.

ASP.NET MVC由以下两个核心组成部分构成:

  1. 一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与Action名称;
  2. 一个名为MvcHandler的自定义HttpHandler,用来实现对Controller的激活和Action的执行;

!!阅读本文前请先弄明白ASP.NET执行的流程及httpmodule与httphandler的作用。

下面是进行路由转换时相关类的简化结构图:

 

整个ASP.NET MVC系统的路由信息全部存放在RoteTable这个类的静态变量Routes(为一个RouteDictionary类型)中,网站开始运行时,在Application_Start中对路由进行注册:

RouteTable.Routes.Add("default", 
      new Route{Url="{controller}/{action}"});

 

当一个URL请求到来时,被UrlRoutingModule拦截,拦截后执行流程如下:

  1. 封装当前http上下文,变为HttpContextWrapper对象。
  2. 根据当前的http上下文,从Routes中得到与当前请求URL相符合的RouteData对象。该对象存储有RouteHandler信息。
  3. 把RouteData与http上下文请求封装成一个RequestContext对象。
  4. 根据RequestContext对象,从RouteData的RouteHandler中获取IHttpHandler。
  5. 执行IHttpHandler,进行请求的真正处理。

执行时序图如下图所示:

 

UrlRoutingModule的代码如下:

HttpContextWrapper httpContext = new HttpContextWrapper(HttpContext.Current);
RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
RequestContext requestContext = new RequestContext{ data = routeData, context= httpContext};
IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext);
httpContext.RemapHandler(handler);

 

经过上面最后一步,执行HttpHandle后,程序正式进入Controller激活里面,相关类关系如下图所示:

 

同URL路由一样,MVC初始化时,也需要注册控制器的一些信息,这里是要让框架知道默认的控制器工厂是什么,所以在Application_Start中:

ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory());

程序通过上面的URL路由转换后,进入HttpHandle中,经过以下步骤实现对Controller的激活:

  1. 从Requestcontext封装的RouteData中得到Controller名字。
  2. 通过ControllerBuilder得到当前默认的Controller工厂。
  3. 根据Controller的名字,通过反射创建控制器对象。
  4. 最后执行控制器。执行的实质其实就是执行ActionInvoker.InvokeAction,即根据请求上下文执行相应的Action。

在自定义的MvcHandler中,代码如下:

复制代码
string controllerName =this.Requestcontext.RouteData.Controller;

IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();

IController controller = controllerFactory.CreateController(this.RequestContext,controllerName);

controller.Execute(this.RequestContext);
复制代码

 

以上即为我整理的ASP.NET MVC的两个核心流程,希望新手看的明白,老手多多指点其中的问题,谢谢!!有时间会继续放出MVC其它核心技术。如Model绑定、数据验证等,同时认真向Artech学习!!!!

[转载]Android程序怎么做单元测试 - Powerful Man - 博客园

mikel阅读(1001)

[转载]Android程序怎么做单元测试 – Powerful Man – 博客园.

如何进行Android单元测试

  1. Menifest.xml中加入:

    <application>中加入:

    <uses-library Android:name=”android.test.runner” />

    <application>外面加入:

    <uses-permission android:name=”android.permission.RUN_INSTRUMENTATION” />

    <instrumentation android:name=”android.test.InstrumentationTestRunner” android:targetPackage=”name.feisky.android.test”

    android:label=”Test for my app”/>

 

  1. 编写单元测试代码:必须继承自AndroidTestCase类

    package name.feisky.android.test;

    import android.test.AndroidTestCase;

    import junit.framework.Assert;

    public class MyTest extends AndroidTestCase {

    private static final String Tag=”MyTest”;

    public void testSave() throws Throwable

    {

    int i=4+8;

    Assert.assertEquals(5,i);

    }

    public void testSomethingElse() throws Throwable {

    Assert.assertTrue(1 + 1 == 12);

    }

    }

  2. 执行测试

    IntelliJ中:

    eclipse中:右键 run as Android JUnit Test

    命令行工具:

    adb shell am instrument -w name.feisky.android.test/android.test.InstrumentationTestRunner

也可以新建一个测试项目进行测试

  1. New > Project > Android > Android Test Project.

  1. 添加测试用例类
  2. 添加新类,基类设置为android.test.ActivityInstrumentationTestCase2<HelloAndroid>

  3. 添加构造函数

    添加setUp()方法,这个方法在所有的测试之前进行变量和测试环境的初始化。

    @Override

        protected void setUp() throws Exception {

            super.setUp();

            mActivity = this.getActivity();

            mView = (TextView) mActivity.findViewById(com.example.helloandroid.R.id.textview);

            resourceString = mActivity.getString(com.example.helloandroid.R.string.hello);

        }

  4. 添加testPreconditions()方法,检查初始化环境,只执行一次

    public void testPreconditions() {

          assertNotNull(mView);

        }

  5. 添加单元测试

    public void testText() {

          assertEquals(resourceString,(String)mView.getText());

        }

  6. 测试 Run As… > Android JUnit Test

[转载]程序员困境:底层编码能力正逐步丧失 - 非技术 - ITeye资讯

mikel阅读(915)

[转载]程序员困境:底层编码能力正逐步丧失 – 非技术 – ITeye资讯.

不同的公司环境会培养出不同的员工,程序员也不例外,由于大公司的开发框架十分成熟,稳定,而且充满盈利能力,所以开发者容易陷入拿来主义思维,而底层代码的编写能力正在逐渐退化。

前段时间,@developerworks在微博上向大家推荐了一篇引人深思的文章《程序员困境》,这篇文章的作者描述了在招聘内核程序员时所遇到的一个现象,一些来自大公司的“高级”程序员,却连最简单最基本的问题都无法回答,这不禁引发了作者的思考,下面是笔者的简译:

最近,我 为招聘内核程序员而面试了数万个应聘者,有一些是来自有名的大公司,比如非常有名的芯片/嵌入式系统开发公司。许多人的简历制作的非常完美——涉及的各种 项目、获得的各种奖项等,并有一些人声称,他们在内核研发上有10多年的工作经验,然而,令人惊奇地是,他们却无法回答一些基本的问题:当调用标准的 malloc函数时,内核发生了什么?

别吃惊, 当我让一个应聘者编写一个基于glib哈希函数的LRU缓存框架时,他首先声称自己从未使用过glib,于是我向他演示glib哈希API页面,并且详细 地解释,然而一个小时后,他只写了几行凌乱的代码。我不知道在其它国家是否会有类似的情形,但在中国,或者更具体地说,在北京,这就是现实。那些“高级” 程序员,在有名的外企工作几年后,连一些简单的、基本的问题都无法实现。

为什么?

我愈加思 考就愈加认为,这不仅仅是与程序员自身有关,更与其所在的公司环境有关。这些公司通常会提供稳定的堆栈代码,并且多年来一直沿用着,几行没有任何变化。程 序员整天围绕着这些代码工作,按照已有的思路去开发,无需自己动脑、去思考。如果长期在这样的环境下工作,并且也没有在外部进行提升和开阔,多年以后,你 会发现自己处于很可怜的位置——在公司内部或团队里声称是“专家”,然而不幸地是,你却不能在市场找到一个与之平等的工作。

这就是所 谓的“专家陷阱”。在当程序员的第一天,我们就梦想着成为团队/公司的专家,然而,当这一天到来,大家却陷入了困惑。越深入到当前的代码中,陷的就越深。 渐渐地,我们丧失了那种从头开始创建完整项目的能力,因为现有的代码如此稳定。更糟糕的是,如果我们的主要工作仅仅是维护现有的代码,带有一些细微的功能 添加和改善,一段时间后,无论你读过或学习过多么牛逼的代码,你将会发现自己不再会写代码——甚至是刚毕业那种简单的代码。这就是程序员困境:我们通过编码谋生,但环境却正在摧毁我们这种谋生的能力。

如何跳出困境

对程序员个人来说,首先,做自己的项目;其次不要待在同一团队超过两年时间。

给团队/公司的建议,给员工压力和挑战:轮流工作,让“专家”有机会拓宽自己的技能。定期举行黑客马拉松:这将有助于创建一个拥抱创新和创造的文化氛围。

对此,各位程序员,你们又是持何种观点呢?本文作者主要描述了程序员在大公司所面临的问题,你们身处的环境如何呢?不妨和我们一起分享下吧。

[转载]【推荐】开源来自百度商业前端数据可视化团队的超漂亮动态图表--ECharts - 狼性法则 - 博客园

mikel阅读(844)

[转载]【推荐】开源来自百度商业前端数据可视化团队的超漂亮动态图表–ECharts – 狼性法则 – 博客园.

  本人项目中最近有需要图表的地方,偶然发现一款超级漂亮的动态图标js图表控件,分享给大家,觉得好用的就看一下。更多更漂亮的演示大家可以参考下面两个网址:ECharts官方网址:http://ecomfe.github.io/echarts/index.html,Why ECHarts:http://ecomfe.github.io/echarts/doc/slide/whyEcharts.html#/,官网有示例及各种帮助文档等,第二个网址则是图表形象生动的演示,文章内容均出自于此,有些图片是引用的在线地址可能比较慢一些,耐心等一下。

ECharts

基于Canvas,纯JavaScript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。创新的拖拽重计算、数据视图、值域漫游等特性大大增强了用户体验,赋予了用户对数据进行挖掘、整合的能力。

ECharts (Enterprise Charts 商业产品图表库)

提供商业产品常用图表库,底层基于ZRender,创建了坐标系,图例,提示,工具箱等基础组件,并在此上构建出折线图(区域图)、柱状图(条状图)、散点图(气泡图)、K线图、饼图(环形图)、地图、力导向布局图,同时支持任意维度的堆积和多图表混合展现。

(IE8- supported by excanvas )

ECharts Architecture

特色

我们诚挚邀请你翻阅这份在线文档 《 Why ECharts ? 》 你可以从中更直观的体验到ECharts的特性以及快速浏览到所有图表类型。

*文档中展现的个别特性在IE8-中并没有得到支持,所以建议使用IE9+、chrome、safari、firefox或opear等高级浏览器阅读这份文档。


混搭

混搭的图表会更具表现力也更有有趣味,ECharts提供的图表(共7类11种)支持任意混搭:

折线图(区域图)、柱状图(条状图)、散点图(气泡图)、K线图、 饼图(环形图)、地图、力导布局图。

混搭情况下一个标准图表:包含唯一图例、工具箱、数据区域缩放、值域漫游模块,一个直角坐标系(可包含一条或多条类目轴线,一条或多条值轴线,最多上下左右四条)

拖拽重计算

拖拽重计算特性(专利)带来了数据统计图表从未有过的用户体验,允许用户对统计数据进行有效的提取、整合,甚至在多个图表间交换数据,赋予了用户对数据进行挖掘、整合的能力。

数据视图

如果你所呈现的数据足够让用户所关心,那么他们将不满足于查看可视化的图表,要去逐一迎合他们下载保存,数据分享,加工整合已有数据等等需求?

或许你只要给予一个“,”分隔的数据文本他们就懂了,这就是ECharts的数据视图!当然,你可以重载数据视图的输出方法,用你独特的方式去呈现数据。

如果你的用户足够的高端,你甚至可以打开数据视图的编辑功能,跟拖拽重计算相比,这可是批量的数据修改!

ECharts 数据视图

动态类型切换

很多图表类型本身所表现的能力是相似的,但由于数据差异、表现需求和个人喜好的不同导致最终图表所呈现的张力又大不一样,比如折线图和柱状图的选择总是让人头疼。

ECharts提供了动态类型切换,让用户随心所欲的切换到他所需要的图表类型。

ECharts 动态类型切换

图例开关

多系列数据的同时展现呈现出丰富内容,但如何让用户切换到他所关心的个别系列上?

ECharts提供了方便快捷的图例开关,可以随时切换到你所关心的数据系列。

ECharts 图例开关

数据区域选择

数据可以是无限的,但显示空间总是有限的,数据区域选择组件提供了大数据量中漫游的能力,让用户选择并呈现他所关心的数据区域。

ECharts 数据区域选择

值域漫游

基于坐标的图表(如地图、散点图)通过色彩变化表现数值的大小能直观形象的展示数据分布。

但如何聚焦到我所关心的数值上?我们创造了称为值域漫游的功能,让你可以轻松进行数值筛选。

ECharts 数据区域选择

大规模散点图

如何展现成千上百万的离散数据从而找出他们的分布和聚类?貌似除了用专业的统计工具(如MATLAB)外别无选择?

不,ECharts发明了基于像素的大规模散点图(专利),一个900 x 400的散点区域就能够毫不重复的呈现35万组数据,这对于常规的应用,用现代浏览器就足以轻松展现百万级的散点数据!

ECharts 数据区域选择

标线辅助

趋势线?平均线?未来走势?修正值?有需求用户自然知道用意~

提供标线辅助在K线图中可是必要的功能!是的,K线图我们正在开发中~

多维度堆积

支持多系列,多维度的数据堆积,配合自动伸缩的图形实体和直角坐标系,能呈现出更有内涵的统计图表~

ECharts 多维度堆积

个性化定制

近300个可配置选项结合多级控制设计满足高度定制的个性化需求。

ECharts 个性化定制

事件交互

可以捕获的用户交互和数据变化事件实现图表与外界的联动。

 

使用帮助及更多帮助大家可以参考前文贴出的官网网址。

[转载]代码最简化 - 与您分享我的快乐 ---笨笨 - 博客园

mikel阅读(939)

[转载]代码最简化 – 与您分享我的快乐 —笨笨 – 博客园.

在重构的过程中,站在我个人的角度,针对代码开发的细节,说一说关于代码最简化的小经验。

1、    同一个页面上js/css可能被你引入多次?

Ajax已被大家广泛应用,常常用来加载一些html的片段。Html片段可能有js的处理,这个时候可能在html片段中写一些js操作或者引入 一些js文件,你可有检查,当前页面是否已经加载了该js。如果同一个页面没有被重新加载,不管以前在页面中有的,还是后来html片段中加载的,都会在 当前页面生效。而不是片段中的js作用域只是这个片段。所以js不要重复引入,重复引入很有可能充掉你的全局变量,覆盖原来的同名js方法,也可能造成死 循环等等问题。例如JQuery文件,如果被多次引用就可能造成js死循环,很多原生的JQuery方法不好用。

针对这些问题,我们并不是要完全避免在ajax加载的html片段中不写js。而是根据实际情况做合理的安排。第一,千万不要重复加当前页面的 js;第二,如果ajax加载的片段只会被当前页面加载一次,并且你写的js只是为当前片段服务,那你可以只在片段中引入;第三,当页面中的js超过20 行或者会被多个页面使用的js,建议提取到单独的js文件中;第四,如果js方法是全局的,命名又很大众化像search(), find(),你可要小心了,很可能被别人的js覆盖,你也可能覆盖别人的,怎么办?那就了解一下js面向对象编程吧。建议改成 Student.search(),Student.find()等等

2、    为什么a元素也要写一个onclick,然而它只是一个简单的跳转?

这样写累吗?一个简单的跳转操作,标签本身的功能不能遗忘。用什么标签就应该让它做什么样的事。每当我翻阅这样的代码的时候,都是从链接处着手,然 后顺着onclick事件,找到onclick对应的方法,然后再看做了什么操作。你会发现最后一行是 “location.href=’xxxx/xxx.do?a=” + a + ….”。不觉得有点坑爹吗?我真不知道这种写法的好处。首先这样拼接url如果没有对参数做encodeURIComponent很可能造成参数丢 失;再者让人找的好费神。如果直接写在a标签上href上是不是很清晰呢。如果要在新窗口打开加上“target=”_blank””就够了。

3、    url上为什么是一堆参数?去掉无用的参数吧

可能你会说,我为了下个页面少做查询,我就多传了一些参数。但是这样真的带了好处吗?

暂不说传中文可能乱码的问题,其实这样严重增加了以后维护的难度。明知道下个页面只需要某个参数就够了,然而还是要考察每个参数都是要干嘛用的。思 前想后,明明只需要考虑一个参数的问题,一下子变成了考虑四五个参数,工作量翻了几翻。可曾有感触呢?如果下个页面只是一个通过id的查询就不要再附加的 各种冗余的信息了。除非我们要的内容在当前页面已经有了,而下个页面自己获取真的很影响性能,我们才真的有必要这么做。

4、    为什么要加那么多id在html的元素上?为什么还有重复的?

我想大家都清楚在js里查询某个dom元素使用id是最快的,但是id真的不要用泛滥了。满页都是id,甚至有重复的。这个真的不是好习惯。当维护 的人员想添加一个真正有必要用的id时,发现你已经使用了,换用别的名字又感觉不合适,那些无用的还不敢删除,得费死维护人员多少脑细胞。不要庆幸以后修 改的人不是我们自己。做为一个有责任的开发人员,我们就要对自己写的代码负责。

5、    无用的SQL查询,无用的java方法,无用的http请求,这是要闹哪样?服务器会消化不良的。

一般出现这样的问题是因为修改代码的人添加了新的代码,而没有检查以前的代码是否有用造成。曾记得以前浙师项目就有一个无用的ajax请求,页面一 加载就请求一个公告全表的json数据,有几M那么大,而且没有一点用处。可想而知,得有多么影响性能。要想减少这样的问题发生,就要我们更认真更细心, 修改代码的时候,务必思前想后,去除干净无用的代码。时常检查http请求是否都是你意料之中的请求。

6、    不要继承不该继承的类,也不要实现没有必要实现的接口,而是当需要的时候才这么做,或者你预知到了什么。除非真的有必要这么做。

7、    不得不说的方法命名。

在java的代码中不要轻易使用get和set开头的方法。除非觉得这个方法是当前类的一个属性。一个好的命名方法胜过那堆臭长的注释。别误解,我可不喜欢开发者不写注释,注释该写还是要写的。写注释给人一种安全感,你不觉得吗?

8、    避免长代码文件。

Jsp,js,java都要避免代码太长。对于jsp文件可以使用include进行拆分,如果是tab页每个tab的内容还很多,建议做成两个 jsp页,切换时直接是两个请求。处理问题也就变简单了。对于js的拆分,直接在合适的位置分成多个文件即可,如果是面向对象的js代码,可以考虑使用继 承,直接追加属性等方法解决。Java可以添加辅助的工具类,或者辅助的子类等方法解决。

9、    通用模块设计的时候,应该注意的小问题。

有些通用模块,让调用者用的好辛苦,。通用模块的代码,要合理的设计入口参数。根据调用方的需求合理设计入口参数,不传多余无用的参数。要尽可能的 让调用者做的更少,除了少传参数,你还可以让用户不写html片段,js初始化操作等等。还有就是考虑代码是否会和别人的代码冲突,像html中的 id,js代码(命名要特别注意,最好使用面向对象的方法开法这样的模块),样式表等等。

 

 

总结:

这些都是小问题,或许你不觉得什么,但是放在一起就真的不是小问题了。多一个id,多一个参数,多一个无用查询,多一个无用的请求都会严重的增加以 后维护人员的工作。我们不能完全避免这些小问题,但是只要我们每写一个方法,每写一个SQL,每写一个ajax请求,每写一句代码,都想一想它的必要性, 不写无用的变量定义、无用的判断、无用的循环,这样我可以在很大程序的减少这些小问题的发生。我不是在说最短的代码就是好代码但是臭长的代码绝对不是好代 码。写代码前务必保证自己的思路是清晰的。不要使用怪异的方法,没有绝对的提高性能,甚至连自己都不清楚其中的奥妙,真的就没有必要这样做了。在易懂的前 提下保证代码最优。当性能真的出现问题时再做深度的算法等一系列的优化。

 

本文为作者原创,转载请注明出处,与你分享我的快乐
http://www.cnblogs.com/weirhp