[转载]Android AsyncHttpClient 官网的东西 - forever_crying的专栏 - 博客频道 - CSDN.NET

mikel阅读(947)

[转载]AsyncHttpClient 官网的东西 – forever_crying的专栏 – 博客频道 – CSDN.NET.

verview

An asynchronous callback-based Http client for Android built on top of Apache’s HttpClient libraries. All requests are made outside of your app’s main UI thread, but any callback logic will be executed on the same thread as the callback was created using Android’s Handler message passing.

Features

  • Make asynchronous HTTP requests, handle responses in anonymous callbacks
  • HTTP requests happen outside the UI thread
  • Requests use a threadpool to cap concurrent resource usage
  • GET/POST params builder (RequestParams)
  • Multipart file uploads with no additional third party libraries
  • Tiny size overhead to your application, only 25kb for everything
  • Automatic smart request retries optimized for spotty mobile connections
  • Automatic gzip response decoding support for super-fast requests
  • Binary file (images etc) downloading with BinaryHttpResponseHandler
  • Built-in response parsing into JSON with JsonHttpResponseHandler
  • Persistent cookie store, saves cookies into your app’s SharedPreferences

Who is Using It?

Instagram
Instagram is the #1 photo app on Android, with over 10million users
Heyzap
Social game discovery app with millions of users
DoubanFM
Popular personal online music radio service
Pose
Pose is the #1 fashion app for sharing and discovering new styles
Pocket Salsa
Pocket Salsa is the easiest way to learn how to dance salsa.

Send me a message on github to let me know if you are using this library in a released android application!

Installation & Basic Usage

Download the latest .jar file from github and place it in your Android app’s libs/ folder.

Import the http package.

import com.loopj.android.http.*;

Create a new AsyncHttpClient instance and make a request:

AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {
    @Override
    public void onSuccess(String response) {
        System.out.println(response);
    }
});

In this example, we’ll make a http client class with static accessors to make it easy to communicate with Twitter’s API.

import com.loopj.android.http.*;

public class TwitterRestClient {
  private static final String BASE_URL = "http://api.twitter.com/1/";

  private static AsyncHttpClient client = new AsyncHttpClient();

  public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
      client.get(getAbsoluteUrl(url), params, responseHandler);
  }

  public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
      client.post(getAbsoluteUrl(url), params, responseHandler);
  }

  private static String getAbsoluteUrl(String relativeUrl) {
      return BASE_URL + relativeUrl;
  }
}

This then makes it very easy to work with the Twitter API in your code:

import org.json.*;
import com.loopj.android.http.*;

class TwitterRestClientUsage {
    public void getPublicTimeline() throws JSONException {
        TwitterRestClient.get("statuses/public_timeline.json", null, new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(JSONArray timeline) {
                // Pull out the first event on the public timeline
                JSONObject firstEvent = timeline.get(0);
                String tweetText = firstEvent.getString("text");

                // Do something with the response
                System.out.println(tweetText);
            }
        });
    }
}

Check out the AsyncHttpClientRequestParams and AsyncHttpResponseHandlerJavadocs for more details.

This library also includes a PersistentCookieStore which is an implementation of the Apache HttpClient CookieStore interface that automatically saves cookies to SharedPreferences storage on the Android device.

This is extremely useful if you want to use cookies to manage authentication sessions, since the user will remain logged in even after closing and re-opening your app.

First, create an instance of AsyncHttpClient:

AsyncHttpClient myClient = new AsyncHttpClient();

Now set this client’s cookie store to be a new instance of PersistentCookieStore, constructed with an activity or application context (usually this will suffice):

PersistentCookieStore myCookieStore = new PersistentCookieStore(this);
myClient.setCookieStore(myCookieStore);

Any cookies received from servers will now be stored in the persistent cookie store.

To add your own cookies to the store, simply construct a new cookie and call addCookie:

BasicClientCookie newCookie = new BasicClientCookie("cookiesare", "awesome");
newCookie.setVersion(1);
newCookie.setDomain("mydomain.com");
newCookie.setPath("/");
myCookieStore.addCookie(newCookie);

See the PersistentCookieStore Javadoc for more information.

Adding GET/POST Parameters with RequestParams

The RequestParams class is used to add optional GET or POST parameters to your requests.RequestParams can be built and constructed in various ways:

Create empty RequestParams and immediately add some parameters:

RequestParams params = new RequestParams();
params.put("key", "value");
params.put("more", "data");

Create RequestParams for a single parameter:

RequestParams params = new RequestParams("single", "value");

Create RequestParams from an existing Map of key/value strings:

HashMap<String, String> paramMap = new HashMap<String, String>();
paramMap.put("key", "value");
RequestParams params = new RequestParams(paramMap);

See the RequestParams Javadoc for more information.

Uploading Files with RequestParams

The RequestParams class additionally supports multipart file uploads as follows:

Add an InputStream to the RequestParams to upload:

InputStream myInputStream = blah;
RequestParams params = new RequestParams();
params.put("secret_passwords", myInputStream, "passwords.txt");

Add a File object to the RequestParams to upload:

File myFile = new File("/path/to/file.png");
RequestParams params = new RequestParams();
try {
    params.put("profile_picture", myFile);
} catch(FileNotFoundException e) {}

Add a byte array to the RequestParams to upload:

byte[] myByteArray = blah;
RequestParams params = new RequestParams();
params.put("soundtrack", new ByteArrayInputStream(myByteArray), "she-wolf.mp3");

See the RequestParams Javadoc for more information.

Downloading Binary Data with BinaryHttpResponseHandler

The BinaryHttpResponseHandler class can be used to fetch binary data such as images and other files. For example:

AsyncHttpClient client = new AsyncHttpClient();
String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };
client.get("http://example.com/file.png", new BinaryHttpResponseHandler(allowedContentTypes) {
    @Override
    public void onSuccess(byte[] fileData) {
        // Do something with the file
    }
});

See the BinaryHttpResponseHandler Javadoc for more information.

Adding HTTP Basic Auth credentials

Some requests may need username/password credentials when dealing with API services that use HTTP Basic Access Authentication requests. You can use the method setBasicAuth()to provide your credentials.

Set username/password for any host and realm for a particular request. By default the Authentication Scope is for any host, port and realm.

AsyncHttpClient client = new AsyncHttpClient();
client.setBasicAuth("username","password/token");
client.get("http://example.com");

You can also provide a more specific Authentication Scope (recommended)

AsyncHttpClient client = new AsyncHttpClient();
client.setBasicAuth("username","password", new AuthScope("example.com", 80, AuthScope.ANY_REALM));
client.get("http://example.com");

See the RequestParams Javadoc for more information.

Building from Source

To build a .jar file from source, first make a clone of the android-async-http github repository. You’ll then need to copy the local.properties.dist file to local.properties and edit the sdk.dir setting to point to where you have the android sdk installed. You can then run:

ant package

This will generate a file named android-async-http-version.jar.

Reporting Bugs or Feature Requests

Please report any bugs or feature requests on the github issues page for this project here:

https://github.com/loopj/android-async-http/issues

Credits & Contributors

James Smith (http://github.com/loopj)
Creator and Maintainer
Micah Fivecoate (http://github.com/m5)
Major Contributor, including the original RequestParams
The Droid Fu Project (https://github.com/kaeppler/droid-fu)
Inspiration and code for better http retries
Rafael Sanches (http://blog.rafaelsanches.com)
Original SimpleMultipartEntity code
Anthony Persaud (http://github.com/apersaud)
Added support for HTTP Basic Authentication requests.
Linden Darling (http://github.com/coreform)
Added support for binary/image responses

License

The Android Asynchronous Http Client is released under the Android-friendly Apache License, Version 2.0. Read the full license here:

http://www.apache.org/licenses/LICENSE-2.0

About the Author

James Smith, British entrepreneur and developer based in San Francisco.

I’m the co-founder of Bugsnag with Simon Maynard, and from 2009 to 2012 I led up the product team as CTO of Heyzap.

[转载]如何发布ios企业应用 - 浩GE - 博客园

mikel阅读(981)

[转载]如何发布ios企业应用 – 浩GE – 博客园.

ios企业应用可以不经过app store发布,而可以直接在企业内部安装使用。当然,发布企业应用首先需要花$299申请成为企业开发账号。下面介绍企业开发账号如何发布企业应用:

首先,用企业账号登录apple开发者中心,点击member center,点击certificates, identifiers & profiles,进入以下界面:

选择左边菜单栏certificates下的production,然后再点击右上角的“+”按钮添加证书:

选择In house and Ad hoc选项,点击continue,之后会出现页面要求你上传csr文件用来生成证书。csr文件可以用钥匙串访问工具(keychain access)来生成,首先打开钥匙串访问,点击菜单偏好设置,按下图设置:

设置完毕后,点击菜单->证书助理->从证书颁发机构请求证书:

填入电子邮件,需要与appid一致,选择存储到磁盘,勾选让我指定密钥对信息,点击继续后csr文件就已经生成存储在磁盘上。

回到apple开发者中心,上传生成的csr文件:

点击generate,生成证书文件:

点击download下载,然后双击安装证书,就可以在钥匙串工具中看到证书已被按装。再点击done按钮。

接着,要开始生成distribution版的描述文件,在网页中点击左边菜单栏的privisioning profiles下的distribution:

选择In house后,点击continue:

选择wildcard app id,点击continue:

选择你刚才生成的发布版证书,点击continue:

命名privisoning profile的名称后,点击generate生成即可,生成后也可直接下载privisioning profile文件了。

现在所需要生成的文件都生成完了,用xcode打开想要发布的项目,打开build setting界面:

在code signing identity上选择刚才生成的证书,provisioning profiles选择刚才生成的provisioning profiles文件。

随后点击xcode的production菜单,点击archive:

点击左上角的distribute按钮:

点击export:

填写安装包所要保存的地方,并且勾选save for enterprise distribution选项,填入可以提供app下载的网址,这样就可以通过网页下载安装。如果不想通过网页安装(网页安装会自动帮你装上 privisioning profile文件),可以向使用者提供privisioning profiles,让使用者用iphone配置实用工具安装privisioning prifile,然后在用iTunes安装ipa即可。

 

总结:

其实,发布企业ios应用主要分为3个步骤:

1.生成发布版应用证书:这个证书表明你是花了$299的,该证书签名了你的应用是企业应用

2.生成发布版描述文件(provisioning profile):这个文件是根据上面的证书生成的,并且要提供给使用者安装(网页自动安装),授权使用者可以安装其证书签名的企业应用。

3.在xcode中签名证书和提供描述文件发布应用。

[转载]Android Asynchronous HTTPClient的实现和优化-Android开发实例教程-eoe Android开发者社区_Android开发论坛

mikel阅读(872)

[转载]Android Asynchronous HTTPClient的实现和优化-Android开发实例教程-eoe Android开发者社区_Android开发论坛.

大家知道Android对UI线程的反应时间要求很高,超过5秒钟直接ANR掉,根本不给你机会多等。


Android应用与后端系统的交互是最基本的需求之一,如何实现高效的Asynchronous HTTPClient,确保UI线程在启动任务后交由后端异步处理与服务器端的通信,尤为关键。


Google过几个方案,要么太复杂要么不符合要求,基本都淘汰了,最后发现这一版本的实现不错,就拿来用了。
链接:Android Asynchronous HTTPClient tutorial (如果链接错误,请到网上搜索一下)


后来发现了几个严重的问题,罗列如下:
1. 启用单独的线程后,简直如脱缰的野马,难以驾驭。
现象是:在调试的时候经常发现某个线程死掉(比如在服务器down掉的时候,由于线程无法连接而挂掉)
后果是:只能关掉模拟器,甚至还要重启eclipse,否者两者通信出现问题,再也不能继续联机调试


2. 异常的处理非常弱,Activity层难以捕捉并加以处理。
这个问题跟实现的机制有一定的关系,此实现根本就没提供好的异常处理机制,以便捕捉、反馈、处理合理的可预见性的异常,诸如:

1)UnknownHostException – 谁能确保手机的网络连接一直正常,信号一直满格?
2)HttpResponseException – 后端500的错误,说不定就蹦出来了
3)SocketTimeoutException 超时也是太正常不过了,如果人家在荒山野岭(no 3G)摆弄超大的通信请求
4)诸如此类吧

所以改造就再说难免了。下面我贴出相关代码(import就省了吧这里),并加以简单注释说明,方面大家的理解。

首先定义AsyncHttpClient.java。这里的重点是超时的设置。另外我加了个cancelRequest,用 以在切换Activity后取消掉原有Activity发出的所有的异步请求,因为一般情况下,切换了Activity后是不能再更新那个UI了,否则会 抛出异常,直接导致应用crash掉,不过话说回来,这个cancel我发现好像不是那么给力(any feedback?)。

public class AsyncHttpClient {
private static DefaultHttpClient httpClient;

public static int CONNECTION_TIMEOUT = 2*60*1000;
public static int SOCKET_TIMEOUT  = 2*60*1000;

private static ConcurrentHashMap<Activity,AsyncHttpSender> tasks = new ConcurrentHashMap<Activity,AsyncHttpSender>();
  
public static void sendRequest(
   final Activity currentActitity,
   final HttpRequest request,
   AsyncResponseListener callback) {
  
  sendRequest(currentActitity, request, callback, CONNECTION_TIMEOUT, SOCKET_TIMEOUT);
}

public static void sendRequest(
   final Activity currentActitity,
   final HttpRequest request,
   AsyncResponseListener callback,
   int timeoutConnection,
   int timeoutSocket) {
  
  InputHolder input = new InputHolder(request, callback);
  AsyncHttpSender sender = new AsyncHttpSender();
  sender.execute(input);
  tasks.put(currentActitity, sender);
}

public static void cancelRequest(final Activity currentActitity){
  if(tasks==null || tasks.size()==0) return;
  for (Activity key : tasks.keySet()) {
      if(currentActitity == key){
       AsyncTask<?,?,?> task = tasks.get(key);
       if(task.getStatus()!=null && task.getStatus()!=AsyncTask.Status.FINISHED){
        Log.i(TAG, "AsyncTask of " + task + " cancelled.");
        task.cancel(true);
       }
       tasks.remove(key);
      }
  }
}

public static synchronized HttpClient getClient() {
  if (httpClient == null){   
   //use following code to solve Adapter is detached error
   //refer: [url=http://stackoverflow.com/questions/5317882/android-handling-back-button-during-asynctask]http://stackoverflow.com/questions/5317882/android-handling-back-button-during-asynctask[/url]
   BasicHttpParams params = new BasicHttpParams();
   
   SchemeRegistry schemeRegistry = new SchemeRegistry();
   schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
   final SSLSocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
   schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
   
   // Set the timeout in milliseconds until a connection is established.
   HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
   // Set the default socket timeout (SO_TIMEOUT) 
   // in milliseconds which is the timeout for waiting for data.
   HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT);
   
   ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
   httpClient = new DefaultHttpClient(cm, params); 
  }
  return httpClient;
}

}

然后是AsyncHttpSender。这里我用了InputHolder和OutputHolder来进行对象传递,简单包装了下:


/**
* AsyncHttpSender is the AsyncTask implementation
* 
* @author bright_zheng
*
*/
public class AsyncHttpSender extends AsyncTask<InputHolder, Void, OutputHolder> {[/size]
@Override
protected OutputHolder doInBackground(InputHolder... params) {
  HttpEntity entity = null;
  InputHolder input = params[0];
  try {
   HttpResponse response = AsyncHttpClient.getClient().execute((HttpUriRequest) input.getRequest());
   StatusLine status = response.getStatusLine();
   
         if(status.getStatusCode() >= 300) {
          return new OutputHolder(
            new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()),
            input.getResponseListener());
         }
         
   entity = response.getEntity();
   Log.i(TAG, "isChunked:" + entity.isChunked());
            if(entity != null) {
             try{
              entity = new BufferedHttpEntity(entity);
             }catch(Exception e){
              Log.e(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, e.getMessage(), e);
              //ignore?
             }
            }   
  } catch (ClientProtocolException e) {
   Log.e(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, e.getMessage(), e);
   return new OutputHolder(e, input.getResponseListener());
  } catch (IOException e) {
   Log.e(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, e.getMessage(), e);
   return new OutputHolder(e, input.getResponseListener());
  }
  return new OutputHolder(entity, input.getResponseListener());
}

@Override
    protected void onPreExecute(){
  Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "AsyncHttpSender.onPreExecute()");
  super.onPreExecute();
}

@Override
protected void onPostExecute(OutputHolder result) {
  Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "AsyncHttpSender.onPostExecute()");
  super.onPostExecute(result);
  
  if(isCancelled()){
   Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "AsyncHttpSender.onPostExecute(): isCancelled() is true");
   return; //Canceled, do nothing
  }
  
  AsyncResponseListener listener = result.getResponseListener();
  HttpEntity response = result.getResponse();
  Throwable exception = result.getException();
  if(response!=null){
   Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "AsyncHttpSender.onResponseReceived(response)");
   listener.onResponseReceived(response);
  }else{
   Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "AsyncHttpSender.onResponseReceived(exception)");
   listener.onResponseReceived(exception);
  }
}

@Override
    protected void onCancelled(){
  Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "AsyncHttpSender.onCancelled()");
  super.onCancelled();
  //this.isCancelled = true;
}
}

public class OutputHolder{
private HttpEntity response;
private Throwable exception;
private AsyncResponseListener responseListener;

public OutputHolder(HttpEntity response, AsyncResponseListener responseListener){
  this.response = response;
  this.responseListener = responseListener;
}

public OutputHolder(Throwable exception, AsyncResponseListener responseListener){
  this.exception = exception;
  this.responseListener = responseListener;
}
 public HttpEntity getResponse() {
  return response;
}
 public Throwable getException() {
  return exception;
}

public AsyncResponseListener getResponseListener() {
  return responseListener;
}

}

再来看看我们的Call back接口定义, AsyncResponseListener.java:


/**
* The call back interface for  
* 
* @author bright_zheng
*
*/
public interface AsyncResponseListener {
/** Handle successful response */
public void onResponseReceived(HttpEntity response);

/** Handle exception */
public void onResponseReceived(Throwable response);
}

以及抽象Call back的实现,AbstractAsyncResponseListener.java:


/**
* Abstract Async Response Listener implementation
* 
* Subclass should implement at lease two methods.
* 1. onSuccess() to handle the corresponding successful response object
* 2. onFailure() to handle the exception if any
* 
* @author bright_zheng
*
*/
public abstract class AbstractAsyncResponseListener implements AsyncResponseListener{
public static final int RESPONSE_TYPE_STRING = 1;
public static final int RESPONSE_TYPE_JSON_ARRAY = 2;
public static final int RESPONSE_TYPE_JSON_OBJECT = 3;
public static final int RESPONSE_TYPE_STREAM = 4;
private int responseType;

public AbstractAsyncResponseListener(){
  this.responseType = RESPONSE_TYPE_STRING; // default type
}

public AbstractAsyncResponseListener(int responseType){
  this.responseType = responseType;
}

public void onResponseReceived(HttpEntity response){
  try {
   switch(this.responseType){
          case RESPONSE_TYPE_JSON_ARRAY:{
           String responseBody = EntityUtils.toString(response); 
           Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "Return JSON String: " + responseBody);
           JSONArray json = null;
           if(responseBody!=null && responseBody.trim().length()>0){
            json = (JSONArray) new JSONTokener(responseBody).nextValue();
           }
        onSuccess(json);
           break;
          }
          case RESPONSE_TYPE_JSON_OBJECT:{
           String responseBody = EntityUtils.toString(response); 
           Log.i(<SPAN style="BACKGROUND-COLOR: #ffffff">TAG</SPAN>, "Return JSON String: " + responseBody);
           JSONObject json = null;
           if(responseBody!=null && responseBody.trim().length()>0){
            json = (JSONObject) new JSONTokener(responseBody).nextValue();
           }
        onSuccess(json); 
           break;
          }
          case RESPONSE_TYPE_STREAM:{
           onSuccess(response.getContent());
           break;
          }
          default:{
           String responseBody = EntityUtils.toString(response);
           onSuccess(responseBody);
          }         
   }
     } catch(IOException e) {
      onFailure(e);
     } catch (JSONException e) {
      onFailure(e);
  } 
}

public void onResponseReceived(Throwable response){
  onFailure(response);
}

protected void onSuccess(JSONArray response){}

protected void onSuccess(JSONObject response){}

protected void onSuccess(InputStream response){}

protected void onSuccess(String response) {}[/size]
protected void onFailure(Throwable e) {}
}

这样我们使用起来就非常清晰、简单了。

下面贴个简单的客户端用法代码片段:
1、这个是把服务器端响应当stream用的,用以诸如文件、图片下载之类的场景:


AsyncHttpClient.sendRequest(this, request,  
          new AbstractAsyncResponseListener(AbstractAsyncResponseListener.RESPONSE_TYPE_STREAM){
   
   @Override
   protected void onSuccess(InputStream response){
    Bitmap bmp = null;
    try {
     //bmp = decodeFile(response, _facial.getWidth());
     bmp = BitmapFactory.decodeStream(response);
     
     //resize to fit screen
     bmp = resizeImage(bmp, _facial.getWidth(), true);
           
     candidateCache.put(candidate_id, bmp);
           ((ImageView) v).setImageBitmap(bmp);
           
           dialog.dismiss();
    } catch (Exception e) {
     onFailure(e);
    }
   }
   
   @Override
   protected void onFailure(Throwable e) {
    Log.i(TAG, "Error: " + e.getMessage(), e);
    updateErrorMessage(e);
    
    dialog.dismiss();
   }
  });

2、这个是把服务器端响应当JSON用的,用以诸如获取基本文本信息之类的场景:


// Async mode to get hit result
AsyncHttpClient.sendRequest(this, request, 
          new AbstractAsyncResponseListener(AbstractAsyncResponseListener.RESPONSE_TYPE_JSON_ARRAY){
   @Override
   protected void onSuccess(JSONArray response){
    Log.i(TAG, "UploadAndMatch.onSuccess()...");
    candidates = response;
    if(candidates!=null && candidates.length()>0){
           hit_count = candidates.length();
              Log.i(TAG, "HIT: " + hit_count);
              updateStatus(String.format(context.getString(R.string.msg_got_hit), hit_count));
           
     //update UI
           refreshCurrentUI(1);
          }else{
              Log.i(TAG, "No HIT!");
              updateStatus(context.getString(R.string.msg_no_hit));
           
     //update UI
           refreshCurrentUI(0);
          }
   }
   
   @Override
   protected void onFailure(Throwable e) {
    Log.e(TAG, "UploadAndMatch.onFailure(), error: " + e.getMessage(), e);
    updateErrorMessage(e);
    //update UI
          refreshCurrentUI(-1);
   }
   
  });

取自:http://www.iteye.com/topic/1117362

[转载]查看html元素绑定的事件与方法的利器 - 孟晨 - 博客园

mikel阅读(1557)

[转载]查看html元素绑定的事件与方法的利器 – 孟晨 – 博客园.

     WEB标准提倡结构、表现和行为相 分离,现在越来越多采用这种表现和行 为的方式,但它也为我们开发调试带来一些问题,网页载入一堆JavaScript,,我们很难搞清楚最后在哪些元素的哪个动作绑定了事件,尤其是 JavaScript加载事件的方式五花八门,可以透过JQuery、element.click = function() { }、element.addEventListener()…,很难由单一处找出所有事件。而理不清事件来龙去脉,要追踪某个点击动作背后的行为就变得有 些困难,直到我们遇到以下两种利器。

chrome开发者工具有查看HTML元素绑定事件的功能,如下图所示:

      但这种方式查看事件的方法还是有点困难,直到遇到它chrome的插件 Chrome Web Store – Visual Event

Visual Event的运作原理,在于其熟知主要JavaScript库(例如: JQuery、YUI、ExtJS)事件机制,可深入其中撷取事件,并将其标注在对象元素上。目前支持的JS库包括:

DOM 0 events
jQuery 1.2+
YUI 2
MooTools 1.2+
Prototype 1.6+
Glow
在启用Visual Event后,有绑定事件元素将被标上蓝色区块,滑鼠停留时会显示事件的细节。如下图所示:

[转载]Android开发环境配置 - 寒风AC米兰 - 博客园

mikel阅读(1042)

[转载]Android开发环境配置 – 寒风AC米兰 – 博客园.

由于公司项目需要,最近转做Android开发,这里我来介绍一下Android开发环境的配置过程。

首先,需要下载所需要的软件工具,如下所示:

1、Java:开发基础环境,版本为Java SE 7,JDK和JRE这两个都要下载的

JDK:http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

JRE: http://www.oracle.com/technetwork/java/javase/downloads/jre7-downloads-1880261.html

 

2、Eclipse:开发工具,支持多语言开发,是开发Java,Android等项目的利器

http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/kepler/R/eclipse-standard-kepler-R-win32-x86_64.zip

 

3、Android SDK Tool:安卓软件开发工具包,可以下载开发时需要的软件包,框架,工具等信息

http://developer.android.com/sdk/index.html#download

其次,就是安装和环境配置:

第一步:JAVA环境搭建

  1. 安装JDK与JRE进行安装(例如:d:\Program Files\Java\;在安装JDK的过程中会弹出让你安装JRE,不要选择与JDK装在同一目录)
  2. 安装完成后,右击“我的电脑”,点击“属性”,单击“高级系统设置”,点击“环境变量”,如图:

3、在“系统变量”中,设置3项属性,JAVA_HOM,PATH,CLASSPATH(大小写无所谓),若已存在则点击“编辑”,不存在则点击“新建”:

3.1、JAVA_HOME指明JDK安装路径,比如d:\Program Files\Java\jdk1.6.0_10,如图:

3.2、Path项下添加:;%JAVA_HOME%\bin; %JAVA_HOME%\jre\bin;E:\AndroidIDE\sdk\platform-tools

3.3、在CLASSPATH项添加:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar(要加.表示当前路径),如下图所示:

4、在“用户变量”中增加Path项,添加Android SDK Tools的目录,比如E:\AndroidIDE\sdk\tools,如下图所示:

5、检验JAVA环境是否配置OK, “开始”->“运行”,键入“cmd”,键入命令java -version 或 javac,出现以下画面,说明环境变量配置成功。

6、检验Android环境是否配置OK, “开始”->“运行”,键入“cmd”,键入命令android -h,出现以下画面,说明环境变量配置成功。

 

第二步:Eclipse与Android SDK Tool安装

  1. Eclipse的安装就是压缩包解压到当前文件夹即可,解压完成后就可以直接运行了,如果出现错误信息并且内容为A Java Runtime Environment (JRE) or Java Development Kit (JDK) must be available in order to run Ec等等信息是时,则为JAVA环境没有配置对,请检查第一步。
  2. 打开installer_r22.0.1-windows.exe,自定义安装目录。
  3. 安装完毕之后会看到这样一个界面。
  4. 点击进行全选,点击Install安装。
  5. 开始下载文件并开始安装,漫长的等待。 
  6. 完成后出现一个日志,如图这个错误是就是在下载安装API的时候,ADB服务在运行,停止失败,没有影响。
  7. 完成后接下来装AVD(虚拟机,程序,通过它来调试程序),打开目录下AVD Manager.exe。
  8. 点击New后出现下列图片。
  9. 详细配置自行选择,填写完成之后点击OK会将填写信息以对话框的形式进行确认。

  10. 点击Start开始运行虚拟机,第一次虚拟机运行有些慢。

[转载]Android系列---JSON数据解析 - xiaoluo501395377 - 博客园

mikel阅读(1170)

[转载]Android系列—JSON数据解析 – xiaoluo501395377 – 博客园.

上篇随笔详细介绍了三种解析服务器端传过来的xml数据格式,而对于服务器端来说,返回给客户端的数据格式一般分为html、xml和json这三 种格式,那么本篇随笔将讲解一下json这个知识点,包括如何通过json-lib和gson这两个json解析库来对解析我们的json数据,以及如何 在我们的Android客户端解析来自服务器端的json数据,并更新到UI当中。

一、什么是json

json(JavaScript Object Notation)是一种轻量级的数据交换格式,相比于xml这种数据交换格式来说,因为解析xml比较的复杂,而且需要编写大段的代码,所以客户端和服 务器的数据交换格式往往通过json来进行交换。尤其是对于web开发来说,json数据格式在客户端直接可以通过JavaScript来进行解析。

json一共有两种数据结构,一种是以 (key/value)对形式存在的无序的jsonObject对象,一个对象以“{”(左花括号)开始,“}”(右花括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’ 对”之间使用“,”(逗号)分隔。

例如:{“name”: “xiaoluo”}, 这就是一个最简单的json对象,对于这种数据格式,key值必须要是string类型,而对于value,则可以是string、number、object、array等数据类型:

另一种数据格式就是有序的value的集合,这种形式被称为是jsonArray,数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。

 

更多的有关json数据格式可以参加json的官网,http://www.json.org/json-zh.html

二、解析json数据格式

这里将使用两种json的解析库来对我们的json数据格式进行解析以及生成我们的json数据格式。

1.json-lib(http://json-lib.sourceforge.net/)

使用json-lib来进行解析,我们需要引入第三方的包,因为json-lib分为了两个版本,一个版本是针对于jdk1.3的,一个版本是针对于jdk1.5的,这里我们下载jdk1.5的这个json-lib包,其中还需要引入其他的几个jar包:

下载好这几个jar包后,加入到classpath中即可。我们来看看json-lib给我们提供的API。

我们最常用的两个类就是  JSONObject和JSONArray这两个类,分别代表了json对象和json数组,这两个类都实现了 JSON 这个接口,下面我们通过几个小例子来看看如何将我们常见的几种数据格式转换成我们的json对象(我们一般称之为JSON数据的序列化)以及再将json 对象在转换成我们的数据格式(称之为反序列化)。

①简单的javabean的序列化和反序列化

public class Person
{
private int id;
private String name;
private String address;

public Person()
{
}

public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public String getAddress()
{
return address;
}

public void setAddress(String address)
{
this.address = address;
}

public Person(int id, String name, String address)
{
super();
this.id = id;
this.name = name;
this.address = address;
}

@Override
public String toString()
{
return "Person [id=" + id + ", name=" + name + ", address=" + address
+ "]";
}

}

首先我们定义一个简单的javabean对象,然后将一个Person对象转换成json对象,然后再将这个json对象反序列化成我们的Person对象。

我们先定义一个JsonTools类,这个类有两个静态方法,我们可以通过这两个方法来得到一个JSON类型的字符串对象,以及一个JSON对象

public class JsonTools
{
/**
* 得到一个json类型的字符串对象
* @param key
* @param value
* @return
*/
public static String getJsonString(String key, Object value)
{
JSONObject jsonObject = new JSONObject();
//put和element都是往JSONObject对象中放入 key/value 对
// jsonObject.put(key, value);
jsonObject.element(key, value);
return jsonObject.toString();
}

/**
* 得到一个json对象
* @param key
* @param value
* @return
*/
public static JSONObject getJsonObject(String key, Object value)
{
JSONObject jsonObject = new JSONObject();
jsonObject.put(key, value);
return jsonObject;
}

}

我们可以直接通过 JSONObject jsonObject = new JSONObject(); 这个方法就可以得到一个json对象,然后通过element()或者是put()方法来给我们的json对象添加key/value对。我们先来看看第一个例子,实现一个简单的Person对象和json对象的转换

Person person = new Person(1, "xiaoluo", "广州");
// 将Person对象转换成一个json类型的字符串对象
String personString = JsonTools.getJsonString("person", person);
System.out.println(personString.toString());

我们看看控制台的输出:

{"person":{"address":"广州","id":1,"name":"xiaoluo"}}

整个外面的大括号是一个json对象,里面有一对key/value,其中里面的{“address”:”广州”,”id”:1,”name”:”xiaoluo”}就是我们转换成的json字符串对象

再来看看如何将json对象转换成我们的bean对象

JSONObject jsonObject = JsonTools.getJsonObject("person", person);
// 通过JSONObject的toBean方法可以将json对象转换成一个javabean
JSONObject personObject = jsonObject.getJSONObject("person");
Person person2 = (Person) JSONObject.toBean(personObject, Person.class);
System.out.println(person2);
Person [id=1, name=xiaoluo, address=广州]

②转换List类型的对象

@Test
public void testPersonsJson()
{
List persons = new ArrayList();
Person person = new Person(1, "xiaoluo", "广州");
Person person2 = new Person(2, "android", "上海");
persons.add(person);
persons.add(person2);
String personsString = JsonTools.getJsonString("persons", persons);
System.out.println(personsString);

JSONObject jsonObject = JsonTools.getJsonObject("persons", persons);
// List相当于一个JSONArray对象
JSONArray personsArray = (JSONArray)jsonObject.getJSONArray("persons");
List persons2 = (List) personsArray.toCollection(personsArray, Person.class);
System.out.println(persons2);
}
{"persons":[{"address":"广州","id":1,"name":"xiaoluo"},{"address":"上海","id":2,"name":"android"}]}
[Person [id=1, name=xiaoluo, address=广州], Person [id=2, name=android, address=上海]]

③List<Map<String, String>>类型的json对象转换

@Test
public void testMapJson()
{
List&lt;Map&lt;String, String&gt;&gt; list = new ArrayList&lt;Map&lt;String, String&gt;&gt;();
Map&lt;String, String&gt; map1 = new HashMap&lt;String, String&gt;();
map1.put("id", "001");
map1.put("name", "xiaoluo");
map1.put("age", "20");
Map&lt;String, String&gt; map2 = new HashMap&lt;String, String&gt;();
map2.put("id", "002");
map2.put("name", "android");
map2.put("age", "33");
list.add(map1);
list.add(map2);
String listString = JsonTools.getJsonString("list", list);
System.out.println(listString);

JSONObject jsonObject = JsonTools.getJsonObject("list", list);
JSONArray listArray = jsonObject.getJSONArray("list");
List&lt;Map&lt;String, String&gt;&gt; list2 = (List&lt;Map&lt;String, String&gt;&gt;) listArray.toCollection(listArray, Map.class);
System.out.println(list2);
}
{"list":[{"id":"001","age":"20","name":"xiaoluo"},{"id":"002","age":"33","name":"android"}]}
[{id=001, name=xiaoluo, age=20}, {id=002, name=android, age=33}]

通过上面的例子,我们可以了解了如何通过json-lib这个解析库来实现javabean、List、Map等数据和json数据的互相转换

2.gson(http://code.google.com/p/google-gson/)

下面我们来看看Google提供的gson这个json解析库,同样我们需要去下载gson这个jar包,导入到我们的项目中

使用gson,我们可以非常轻松的实现数据对象和json对象的相互转化,其中我们最常用的就是两个方法,一个是fromJSON(),将json 对象转换成我们需要的数据对象,另一个是toJSON(),这个就是将我们的数据对象转换成json对象。下面我们也通过一个综合的例子来看看gson的 使用方法:

public class JsonService
{
public Person getPerson()
{
Person person = new Person(1, "xiaoluo", "广州");
return person;
}

public List getPersons()
{
List persons = new ArrayList();
Person person = new Person(1, "xiaoluo", "广州");
Person person2 = new Person(2, "android", "上海");
persons.add(person);
persons.add(person2);
return persons;
}

public List getString()
{
List list = new ArrayList();
list.add("广州");
list.add("上海");
list.add("北京");
return list;
}

public List&lt;Map&lt;String, String&gt;&gt; getMapList()
{
List&lt;Map&lt;String, String&gt;&gt; list = new ArrayList&lt;Map&lt;String, String&gt;&gt;();
Map&lt;String, String&gt; map1 = new HashMap&lt;String, String&gt;();
map1.put("id", "001");
map1.put("name", "xiaoluo");
map1.put("age", "20");
Map&lt;String, String&gt; map2 = new HashMap&lt;String, String&gt;();
map2.put("id", "002");
map2.put("name", "android");
map2.put("age", "33");
list.add(map1);
list.add(map2);
return list;
}
}
public static void main(String[] args)
{
Gson gson = new Gson();
JsonService jsonService = new JsonService();
Person person = jsonService.getPerson();
System.out.println("person: " + gson.toJson(person));
// 对于Object类型,使用 fromJson(String, Class)方法来将Json对象转换成Java对象
Person person2 = gson.fromJson(gson.toJson(person), Person.class);
System.out.println(person2);
System.out.println("------------------------------------------------");

List persons = jsonService.getPersons();
System.out.println("persons: " + gson.toJson(persons));
/*
* 对于泛型对象,使用fromJson(String, Type)方法来将Json对象转换成对应的泛型对象
* new TypeToken&lt;&gt;(){}.getType()方法
*/
List persons2 = gson.fromJson(gson.toJson(persons), new TypeToken&lt;List&gt;(){}.getType());
System.out.println(persons2);
System.out.println("------------------------------------------------");

List list = jsonService.getString();
System.out.println("String----&gt;" + gson.toJson(list));
List list2 = gson.fromJson(gson.toJson(list), new TypeToken&lt;List&gt;(){}.getType());
System.out.println("list2----&gt;" + list2);
System.out.println("------------------------------------------------");

List&lt;Map&lt;String, String&gt;&gt; listMap = jsonService.getMapList();
System.out.println("Map----&gt;" + gson.toJson(listMap));
List&lt;Map&lt;String, String&gt;&gt; listMap2 = gson.fromJson(gson.toJson(listMap), new TypeToken&lt;List&lt;Map&lt;String, String&gt;&gt;&gt;(){}.getType());
System.out.println("listMap2----&gt;" + listMap2);
System.out.println("------------------------------------------------");
}

看看控制台的输出:

person: {"id":1,"name":"xiaoluo","address":"广州"}
Person [id=1, name=xiaoluo, address=广州]
------------------------------------------------
persons: [{"id":1,"name":"xiaoluo","address":"广州"},{"id":2,"name":"android","address":"上海"}]
[Person [id=1, name=xiaoluo, address=广州], Person [id=2, name=android, address=上海]]
------------------------------------------------
String----&gt;["广州","上海","北京"]
list2----&gt;[广州, 上海, 北京]
------------------------------------------------
Map----&gt;[{"id":"001","age":"20","name":"xiaoluo"},{"id":"002","age":"33","name":"android"}]
listMap2----&gt;[{id=001, age=20, name=xiaoluo}, {id=002, age=33, name=android}]
------------------------------------------------

三、在Android客户端解析服务器端的json数据

下面我们来完成一个综合的例子,Android客户端通过一个AsyncTask异步任务请求服务器端的某些数据,然后在解析完这些数据后,将得到的数据内容更新到我们的Spinner这个UI控件当中。

我们首先来看下服务器端的代码:

@WebServlet("/CityServlet")
public class CityServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;

public CityServlet()
{
super();
}

protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
this.doPost(request, response);
}

protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();

String type = request.getParameter("type");
if("json".equals(type))
{
List cities = new ArrayList();
cities.add("广州");
cities.add("上海");
cities.add("北京");
cities.add("湖南");
Map&lt;String, List&gt; map = new HashMap&lt;String, List&gt;();
map.put("cities", cities);
String citiesString = JSON.toJSONString(map);
writer.println(citiesString);
}

writer.flush();
writer.close();
}

}

如果客户端请求的参数是type=json,则响应给客户端一个json数据格式

接着来看看客户端的代码,首先看看客户端的布局文件,其实就是一个按钮和一个Spinner控件,当点击按钮后,通过http协议请求服务器端的数据,然后在接收到后再更新我们的Spinner控件的数据


&nbsp;

&nbsp;

&nbsp;

在Android客户端写一个解析json数据格式的类:

public class JsonUtils
{
/**
* @param citiesString 从服务器端得到的JSON字符串数据
* @return 解析JSON字符串数据,放入List当中
*/
public static List parseCities(String citiesString)
{
List cities = new ArrayList();

try
{
JSONObject jsonObject = new JSONObject(citiesString);
JSONArray jsonArray = jsonObject.getJSONArray("cities");
for(int i = 0; i &lt; jsonArray.length(); i++)
{
cities.add(jsonArray.getString(i));
}
}
catch (Exception e)
{
e.printStackTrace();
}

return cities;
}
}

当然我们的HttpUtils类也不可少:

public class HttpUtils
{
/**
* @param path 请求的服务器URL地址
* @param encode 编码格式
* @return 将服务器端返回的数据转换成String
*/
public static String sendPostMessage(String path, String encode)
{
String result = "";
HttpClient httpClient = new DefaultHttpClient();
try
{
HttpPost httpPost = new HttpPost(path);
HttpResponse httpResponse = httpClient.execute(httpPost);
if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
{
HttpEntity httpEntity = httpResponse.getEntity();
if(httpEntity != null)
{
result = EntityUtils.toString(httpEntity, encode);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
httpClient.getConnectionManager().shutdown();
}

return result;
}
}

最后来看看我们的MainActivity类:

public class MainActivity extends Activity
{
private Spinner spinner;
private Button button;
private ArrayAdapter adapter;
private ProgressDialog dialog;
private final String CITY_PATH_JSON = "http://172.25.152.34:8080/httptest/CityServlet?type=json";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

spinner = (Spinner)findViewById(R.id.spinner);
button = (Button)findViewById(R.id.button);
dialog = new ProgressDialog(MainActivity.this);
button.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
dialog.setTitle("提示信息");
dialog.setMessage("loading......");
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setCancelable(false);

new MyAsyncTask().execute(CITY_PATH_JSON);
}
});
}

public class MyAsyncTask extends AsyncTask&lt;String, Void, List&gt;
{
@Override
protected void onPreExecute()
{
dialog.show();
}
@Override
protected List doInBackground(String... params)
{
List cities = new ArrayList();
String citiesString = HttpUtils.sendPostMessage(params[0], "utf-8");
// 解析服务器端的json数据
cities = JsonUtils.parseCities(citiesString);return cities;
}
@Override
protected void onPostExecute(List result)
{
adapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_spinner_item, result);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
dialog.dismiss();
}
}

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

}

当然别往了开启我们的网络授权

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

最后我们来看看效果图:

这样我们就完成了客户端与服务器端通过json来进行数据的交换

 

总结:本篇随笔主要讲解了JSON这种轻量级的数据交换格式的概念,以及讲解了两种解析json数据的解析类(json-lib以及gson),最后通过一个小例子实现了在Android客户端和服务器端使用json这种数据格式来进行数据的交换。

[转载]Android系列--DOM、SAX、Pull解析XML - xiaoluo501395377 - 博客园

mikel阅读(829)

[转载]Android系列–DOM、SAX、Pull解析XML – xiaoluo501395377 – 博客园.

本篇随笔将详细讲解如何在Android当中解析服务器端传过来的XML数据,这里将会介绍解析xml数据格式的三种方式,分别是DOM、SAX以及PULL。

一、DOM解析XML

我们首先来看看DOM(Document Object Model)这种方式解析xml,通过DOM解析xml在j2ee开发中非常的常见,它将整个xml看成是一个树状的结构,在解析的时候,会将整个xml 文件加载到我们的内存当中,然后通过DOM提供的API来对我们的xml数据进行解析,这种方式解析xml非常的方便,并且我们可以通过某个节点访问到其 兄弟或者是父类、子类节点。那么通过DOM来解析xml的步骤是怎样的呢?

1.首先通过DocumentBuilderFactory这个类来构建一个解析工厂类,通过newInstance()的方法可以得到一个DocumentBuilderFactory的对象。

2.通过上面的这个工厂类创建一个DocumentBuilder的对象,这个类就是用来对我们的xml文档进行解析,通过DocumentBuilderFactory的newDocumentBuilder()方法

3.通过创建好的 DocumentBuilder 对象的 parse(InputStream) 方法就可以解析我们的xml文档,然后返回的是一个Document的对象,这个Document对象代表的就是我们的整个xml文档。

4.得到了整个xml的Document对象后,我们可以获得其下面的各个元素节点(Element),同样每个元素节点可能又有多个属性(Attribute),根据每个元素节点我们又可以遍历该元素节点下面的子节点等等。

在这里要说明一下,在DOM的API当中,Node这个接口代表了我们整个的DOM对象的最初数据类型,它代表了整个document树中的每一个 单一节点。所有实现了Node这个接口的对象都可以处理其孩子节点,当然,并不是每个节点都有children,例如TextNode(文本节点),通过 Node的 nodeName、nodeValue、attributes这三个属性,我们可以很方便的得到每个Node节点的节点名字、节点的值、节点属性等,下面 我们来看看不同类型的Node节点其nodeName、nodeValue、attributes三个属性分别代表的是什么:

Interface nodeName nodeValue attributes
Attr same as Attr.name same as Attr.value null
CDATASection "#cdata-section" same as CharacterData.data, the content of the CDATA Section null
Comment "#comment" same as CharacterData.data, the content of the comment null
Document "#document" null null
DocumentFragment "#document-fragment" null null
DocumentType same as DocumentType.name null null
Element same as Element.tagName null NamedNodeMap
Entity entity name null null
EntityReference name of entity referenced null null
Notation notation name null null
ProcessingInstruction same as ProcessingInstruction.target same as ProcessingInstruction.data null
Text "#text" same as CharacterData.data, the content of the text node null

其实我们用的最多的就是Element和Text,通过Element的nodeName属性可以得到这个节点的标签名,Text对象的nodeValue得到的就是元素节点的文本值内容,下面我们来看看一个通过DOM解析xml的一个代码案例:

首先我们构建一个xml的文档,这个文档等下会放在我们的服务器上,通过http协议来得到这个xml文档,然后在我们的Android客户端对其进行解析

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<persons>
    <person id="1">
        <name>小罗</name>
        <age>21</age>
    </person>
    <person id="2">
        <name>android</name>
        <age>15</age>
    </person>
</persons>
复制代码

下面我们来看看DOM解析服务器端xml的工具类:

复制代码
public class DomParserUtils
{
    public static List<Person> parserXmlByDom(InputStream inputStream) throws Exception
    {
        List<Person> persons = new ArrayList<Person>();
        //    得到一个DocumentBuilderFactory解析工厂类
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //    得到一个DocumentBuilder解析类
        DocumentBuilder builder = factory.newDocumentBuilder();
        //    接收一个xml的字符串来解析xml,Document代表整个xml文档
        Document document = builder.parse(inputStream);
        //    得到xml文档的根元素节点
        Element personsElement = document.getDocumentElement();
        //    得到标签为person的Node对象的集合NodeList
        NodeList nodeList = personsElement.getElementsByTagName("person");
        for(int i = 0; i < nodeList.getLength(); i++)
        {
            Person person = new Person();
            //    如果该Node是一个Element
            if(nodeList.item(i).getNodeType() == Document.ELEMENT_NODE)
            {
                Element personElement = (Element)nodeList.item(i);
                //    得到id的属性值
                String id = personElement.getAttribute("id");
                person.setId(Integer.parseInt(id));

                //    得到person元素下的子元素
                NodeList childNodesList = personElement.getChildNodes();
                for(int j = 0; j < childNodesList.getLength(); j++)
                {
                    if(childNodesList.item(j).getNodeType() == Document.ELEMENT_NODE)
                    {
                        //    解析到了person下面的name标签
                        if("name".equals(childNodesList.item(j).getNodeName()))
                        {
                            //    得到name标签的文本值
                            String name = childNodesList.item(j).getFirstChild().getNodeValue();
                            person.setName(name);
                        }
                        else if("address".equals(childNodesList.item(j).getNodeName()))
                        {
                            String age = childNodesList.item(j).getFirstChild().getNodeValue();
                            person.setAge(Integer.parseInt(age));
                        }
                    }
                }

                persons.add(person);
                person = null;
            }
        }
        return persons;
    }
}
复制代码

通过DOM解析xml的好处就是,我们可以随时访问到某个节点的相邻节点,并且对xml文档的插入也非常的方便,不好的地方就是,其会将整个xml 文档加载到内存中,这样会大大的占用我们的内存资源,对于手机来说,内存资源是非常非常宝贵的,所以在手机当中,通过DOM这种方式来解析xml是用的比 较少的。

二、SAX解析XML

SAX(Simple API for XML),接着我们来看看另一种解析xml的方式,通过sax来对xml文档进行解析。

SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于Android等移动设备。 SAX解析XML文件采用的是事件驱动,也就是说,它并不需要解析完整个文档,在按内容顺序解析文档的过程中,SAX会判断当前读到的字符是否合法XML 语法中的某部分,如果符合就会触发事件。所谓事件,其实就是一些回调(callback)方法,这些方法(事件)定义在ContentHandler接 口。下面是一些ContentHandler接口常用的方法:

复制代码
startDocument()
当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。

endDocument()
和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。

startElement(String namespaceURI, String localName, String qName, Attributes atts)
当读到一个开始标签的时候,会触发这个方法。namespaceURI就是命名空间,localName是不带命名空间前缀的标签名,qName是带命名空间前缀的标签名。通过atts可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,当遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。

endElement(String uri, String localName, String name)
这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。

characters(char[] ch, int start, int length)
这个方法用来处理在XML文件中读到的内容,第一个参数用于存放文件的内容,后面两个参数是读到的字符串在这个数组中的起始位置和长度,使用new String(ch,start,length)就可以获取内容。
复制代码

上面提到了重要的一点,sax解析xml是基于事件流的处理方式的,因此每解析到一个标签,它并不会记录这个标签之前的信息,而我们只会知道当前这个表情的名字和它的属性,至于标签里面的嵌套,上层标签的名字这些都是无法知道的。

sax解析xml最重要的步骤就是定义一个我们自己的Handler处理类,我们可以让其继承 DefaultHandler 这个类,然后在里面重写其回调方法,在这些回调方法里来做我们的xml解析

下面我们就通过一个实例来看看如果通过SAX来解析xml,首先定义一个我们自己的Handler类:

复制代码
public class MyHandler extends DefaultHandler
{
    private List<Person> persons;
    private Person person;
    //    存放当前解析到的标签名字
    private String currentTag;
    //    存放当前解析到的标签的文本值
    private String currentValue;

    public List<Person> getPersons()
    {
        return persons;
    }

    //    当解析到文档开始时的回调方法
    @Override
    public void startDocument() throws SAXException
    {
        persons = new ArrayList<Person>();
    }

    //    当解析到xml的标签时的回调方法
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException
    {
        if("person".equals(qName))
        {
            person = new Person();
            //    得到当前元素的属性值
            for(int i = 0; i < attributes.getLength(); i++)
            {
                if("id".equals(attributes.getQName(i)))
                {
                    person.setId(Integer.parseInt(attributes.getValue(i)));
                }
            }
        }
        //    设置当前的标签名
        currentTag = qName;
    }

    //    当解析到xml的文本内容时的回调方法
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException
    {
        //    得到当前的文本内容
        currentValue = new String(ch,start, length);
        //    当currentValue不为null、""以及换行时
        if(currentValue != null && !"".equals(currentValue) && !"\n".equals(currentValue))
        {
            //    判断当前的currentTag是哪个标签
            if("name".equals(currentTag))
            {
                person.setName(currentValue);
            }
            else if("age".equals(currentTag))
            {
                person.setAge(Integer.parseInt(currentValue));
            }
        }
        //    清空currentTag和currentValue
        currentTag = null;
        currentValue = null;
    }

    //    当解析到标签的结束时的回调方法
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException
    {
        if("person".equals(qName))
        {
            persons.add(person);
            person = null;
        }
    }
}
复制代码

接着看看SAX解析xml的Util类:

复制代码
public class SaxParserUtils
{
    public static List<Person> parserXmlBySax(InputStream inputStream) throws Exception
    {
        //    创建一个SAXParserFactory解析工厂类
        SAXParserFactory factory = SAXParserFactory.newInstance();
        //    实例化一个SAXParser解析类
        SAXParser parser = factory.newSAXParser();
        //    实例化我们的MyHandler类
        MyHandler myHandler = new MyHandler();
        //    根据我们自定义的Handler来解析xml文档
        parser.parse(inputStream, myHandler);

        return myHandler.getPersons();
    }
}
复制代码

三、PULL解析XML

最后来介绍第三种解析xml的方式,pull。pull解析和sax解析类似,都是基于事件流的方式,在Android中自带了pull解析的jar包,所以我们不需要导入第三方的jar包了。

Pull解析器和SAX解析器的区别

Pull解析器和SAX解析器虽有区别但也有相似性。他们的区别为:SAX解析器的工作方式是自动将事件推入注册的事件处理器进行处理,因此你不能控制事件的处理主动结束;

而Pull解析器的工作方式为允许你的应用程序代码主动从解析器中获取事件,正因为是主动获取事件,因此可以在满足了需要的条件后不再获取事件,结束解析。这是他们主要的区别。

而他们的相似性在运行方式上,Pull解析器也提供了类似SAX的事件(开始文档START_DOCUMENT和结束文档 END_DOCUMENT,开始元素START_TAG和结束元素END_TAG,遇到元素内容TEXT等),但需要调用next() 方法提取它们(主动提取事件)。

Android系统中和Pull方式相关的包为org.xmlpull.v1,在这个包中提供了Pull解析器的工厂类 XmlPullParserFactory和Pull解析器XmlPullParser,XmlPullParserFactory实例调用 newPullParser方法创建XmlPullParser解析器实例,接着XmlPullParser实例就可以调用getEventType() 和next()等方法依次主动提取事件,并根据提取的事件类型进行相应的逻辑处理。

下面我们就来通过一个代码来看看pull解析xml的步骤:

复制代码
public class PullParserUtils
{
    public static List<Person> parserXmlByPull(InputStream inputStream) throws Exception
    {
        List<Person> persons = null;
        Person person = null;

        //    创建XmlPullParserFactory解析工厂
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        //    通过XmlPullParserFactory工厂类实例化一个XmlPullParser解析类
        XmlPullParser parser = factory.newPullParser();
        //    根据指定的编码来解析xml文档
        parser.setInput(inputStream, "utf-8");

        //    得到当前的事件类型
        int eventType = parser.getEventType();
        //    只要没有解析到xml的文档结束,就一直解析
        while(eventType != XmlPullParser.END_DOCUMENT)
        {
            switch (eventType)
            {
                //    解析到文档开始的时候
                case XmlPullParser.START_DOCUMENT:
                     persons = new ArrayList<Person>();
                break;
                //    解析到xml标签的时候
                case XmlPullParser.START_TAG:
                     if("person".equals(parser.getName()))
                     {
                         person = new Person();
                         //    得到person元素的第一个属性,也就是ID
                         person.setId(Integer.parseInt(parser.getAttributeValue(0)));
                     }
                     else if("name".equals(parser.getName()))
                     {
                         //    如果是name元素,则通过nextText()方法得到元素的值
                         person.setName(parser.nextText());
                     }
                     else if("age".equals(parser.getName()))
                     {
                         person.setAge(Integer.parseInt(parser.nextText()));
                     }
                break;
                //    解析到xml标签结束的时候
                case XmlPullParser.END_TAG:
                     if("person".equals(parser.getName()))
                     {
                         persons.add(person);
                         person = null;
                     }
                break;
            }
            //    通过next()方法触发下一个事件
            eventType = parser.next();
        }

        return persons;
    }
}
复制代码

最后我们再编写一个HttpUtils类来访问我们的服务器端的xml文档:

复制代码
public class HttpUtils
{
    public static InputStream httpMethod(String path, String encode)
    {
        HttpClient httpClient = new DefaultHttpClient();

        try
        {
            HttpPost httpPost = new HttpPost(path);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
            {
                HttpEntity httpEntity = httpResponse.getEntity();
                return httpEntity.getContent();
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            httpClient.getConnectionManager().shutdown();
        }

        return null;
    }
}
复制代码

最后来看看我们的Android应用程序的布局文件以及Activity类的代码:

复制代码
public class MainActivity extends Activity
{
    private Button button;
    private Button button2;
    private Button button3;
    private final String PATH = "http://172.25.152.34:8080/httptest/person.xml";
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button)findViewById(R.id.button1);
        button2 = (Button)findViewById(R.id.button2);
        button3 = (Button)findViewById(R.id.button3);

        ButtonOnClickListener listener = new ButtonOnClickListener();
        button.setOnClickListener(listener);
        button2.setOnClickListener(listener);
        button3.setOnClickListener(listener);
    }

    class ButtonOnClickListener implements OnClickListener
    {
        @Override
        public void onClick(View v)
        {
            Button button = (Button)v;
            switch (button.getId())
            {
                case R.id.button1:
                    //    启动一个新线程解析xml
                    class MyThread1 extends Thread
                    {
                        @Override
                        public void run()
                        {
                            InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");
                            List<Person> persons = null;
                            try
                            {
                                persons = DomParserUtils.parserXmlByDom(inputStream);
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }
                            System.out.println("dom --->>" + persons);
                        }
                    }
                    new MyThread1().start();
                break;
                case R.id.button2:
                    //    启动一个新线程解析xml
                    class MyThread2 extends Thread
                    {
                        @Override
                        public void run()
                        {
                            InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");
                            List<Person> persons = null;
                            try
                            {
                                persons = SaxParserUtils.parserXmlBySax(inputStream);
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }
                            System.out.println("sax --->>" + persons);
                        }
                    }
                    new MyThread2().start();
                break;
                case R.id.button3:
                    //    启动一个新线程解析xml
                    class MyThread3 extends Thread
                    {
                        @Override
                        public void run()
                        {
                            InputStream inputStream = HttpUtils.httpMethod(PATH, "utf-8");
                            List<Person> persons = null;
                            try
                            {
                                persons = PullParserUtils.parserXmlByPull(inputStream);
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }
                            System.out.println("pull: --->>" + persons);
                        }
                    }
                    new MyThread3().start();
                break;
            }
        }
    }

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

}
复制代码

最后我们来看看控制台的输出:

 

 

总结:dom方式解析xml,比较简单,并可以访问兄弟元素,但是需要将整个xml文档加载到内存中,对于android设备来说,不推荐使用dom的方式解析xml。

sax和pull都是基于事件驱动的xml解析器,在解析xml时并不会加载整个的xml文档,占用内存较少,因此在android开发中建议使用sax或者pull来解析xml文档。

[转载]关于WordPress搬家方法步骤的整理 - Xiao.Yang - 博客园

mikel阅读(1117)

[转载]关于WordPress搬家方法步骤的整理 – Xiao.Yang – 博客园.

最近准备更换自己的博客服务器,所以需要将原来服务器上的所有东西都搬到新的服务器.为了数据的安全,在网上找了很多的资料.
现在整理一下整个搬家过程的操作步骤.
下面进入正题:

1.测试环境
这次我使用的示例服务器是:
原服务器(需要导出数据):http://www.kilu.de (位于德国的老牌服务器提供商,其免费空间支持的东西很全面.包括FTP,数据库的管理都支持)
新服务器(需要导入数据):http://www.yunshangdian.com (即:新浪云 SinaAppEngine)(空间需要付费,尤其要支持FTP的话,需要更高些的费用)
如果你的服务器不是以上这两个,也不用着急,几乎所有的服务器的原理都是相同的.

2.准备
1)首先你需要确认你的新旧服务器都支持FTP,以及数据库的导入导出.
上面已经说过,如果是SAE的服务器的话,FTP的支持需要付费的.对于SAE的收费,这里教一招,你可以只开通一个月”基础级”空间,这样就可以使用FTP了,之后续费就可以只续普通空间的费用.
2)你需要安装一个FTP上传下载的工具.这样的软件很多.这里使用”FileZilla”.

3.原服务器的数据导出
数据的导出分为两部分:
1)数据库的导出:
①登入原系统的数据库管理界面.

②然后选择相应的数据库.再点击”导出”.

③在”导出”页面,将表全选,然后点击右下角的”执行”.

然后浏览器会提示你下载导出的”.SQL”文件.保存待用.

2)整站代码的导出:
①首先你需要获取原服务器的FTP信息,以下是我的原服务器的信息.
②然后使用FileZilla连接上述服务器,将”www”文件夹下所有文件下载到本地的文件夹.本地文件夹暂且起名为”WordPressSource”.

这个过程可能需要几分钟到十几分钟.下载好以后,代码部分的导出工作就已经完成了.
以上,将原服务器的数据导出的工作就全部完成了.

4.新服务器的数据导入
相应的,新服务器数据的导入工作也是分为两步:
1)数据库的导入
①数据库的导入之前需要特别注意一点.就是在原来的数据库中有很多链接资源,这些资源都指向原有的服务器.所以需要在导入数据之前要把数据库文件中的原服务器域名替换成新服务器的域名.
当然,如果你在原服务器中使用的域名是自己绑定的,并且已经将这个域名绑定到了新的服务器中.那么就无需替换了.
下面进行域名的替换.
打开原服务器导出的”.spl”文件.输入原来的域名,再输入新的域名进行替换.替换完成后保存.

②然后打开SAE的管理页面点”数据库管理”.

③再点击”用phpMyAdmin管理”进入数据库管理界面.
这里要特别注意一点,就是需要将”用户名”,”密码”,”数据库名”对应的信息保存备用.

④在数据库管理界面点击”import”.

⑤再点击”Browse”选择刚才修改过的”.spl”文件.并点击”Go”执行导入操作.

到此为止,数据库的导入工作就算完成了.

2)整站代码的导入
①在导入整站代码之前首先需要修改一个名为”wp-config.php”的文件内容,这个文件是WordPress连接数据库的配置文件,需要将里面的三个信息修改成新服务器的数据库信息.即替换”用户名”,”密码”,”数据库名”为新数据库的对应的信息.(上面红色字体声明保存的信息)
将下图红色部分替换成新数据库的对应信息,注意要用单引号括住相应的字段.修改完成以后保存.

②然后,你要获取新服务器的FTP信息.进入云商店自己的应用,然后点击”FTP设置”就可以查看自己的FTP信息了.(前面说过,这个FTP支持需要支付额外的费用,否则看不了)

③记录自己的FTP信息.密码就是云商店的密码.

④用”FileZilla”登录上述的服务器.并将原服务器下载下来的代码文件(WordPressSource中的所有文件)上传到FTP的根目录下.这个过程又可能会花费几分钟到十几分钟的时间.

上传完成后,所有WordPress搬家的工作就全部结束了.理论上此时再访问新的站点,你的博客就会完整的运行在新的系统上了.

说在后面的话:
其实整个过程非常的简单,无非就是数据的导入导出,只不过有两个地方要修改文件,所以请注意.
一个地方是将数据库导出的文件中的原服务器域名替换成新服务器域名;
还有一个是将”wp-config.php”文件中的数据库信息修改为新数据库信息.
还有,虽然上述整个操作过程是几乎没有风险的,因为原来服务器的数据是没有被修改的.但是这里也要提示一下,千万不要把数据从原服务器导出以后就顺便把原服务器上的数据给删了.至少等新的服务器运行一段时间以后确定没有任何问题以后再删也不迟.
数据是无价的.

希望以上对大家有所帮助,如有错误请大家多多指正.

[转载]Android自定义Button按钮显示样式 - Glan - 博客园

mikel阅读(865)

[转载]Android自定义Button按钮显示样式 – Glan – 博客园.

现在的用户对APP的外观看得很重要,如果APP内所有元件都用Android默认样式写,估计下面评论里就有一堆在骂UI丑的。今天学习自定义Button按钮样式。Button样式修改的是Button的背景(Background)属性。
首先写一个定义Button样式的XML文件:
新建Android XML文件,类型选Drawable,根结点选selector,文件名就buton_style吧。
程序自动给我们刚刚建的文件里加了selector结点,我们只需要在selector结点里写上三种状态时显示的背景图片(按下、获取焦点,正常)。
代码如下:

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:state_pressed="true" android:drawable="@drawable/play_press" /> 
    <item android:state_focused="true" android:drawable="@drawable/play_press" /> 
    <item android:drawable="@drawable/play" /> 
</selector>  

我这里获取焦点跟点击时显示的是同一张图片,必须严格照上面的顺序写,不可倒。
接下来只要在布局时写Button控件时应用到Button的Background属性即可。
Xml代码

<Button android:id="@+id/button1" 
    android:layout_width="wrap_content" android:layout_height="wrap_content" 
    android:background="@drawable/button_style" 
></Button> 

再加上一种自定义样式方法,上面的是用图片,其实我们可以直接通过定义xml文件来实现不同的样式:
在上面的源代码基础上,只需要修改button_style文件,同样三种状态分开定义:
Xml代码

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:state_pressed="true"> 
        <shape> 
            <gradient android:startColor="#0d76e1" android:endColor="#0d76e1" 
                android:angle="270" /> 
            <stroke android:width="1dip" android:color="#f403c9" /> 
            <corners android:radius="2dp" /> 
            <padding android:left="10dp" android:top="10dp" 
                android:right="10dp" android:bottom="10dp" /> 
        </shape> 
    </item> 
  
    <item android:state_focused="true"> 
        <shape> 
            <gradient android:startColor="#ffc2b7" android:endColor="#ffc2b7" 
                android:angle="270" /> 
            <stroke android:width="1dip" android:color="#f403c9" /> 
            <corners android:radius="2dp" /> 
            <padding android:left="10dp" android:top="10dp" 
                android:right="10dp" android:bottom="10dp" /> 
        </shape> 
    </item> 
  
    <item> 
        <shape> 
            <gradient android:startColor="#000000" android:endColor="#ffffff" 
                android:angle="180" /> 
            <stroke android:width="1dip" android:color="#f403c9" /> 
            <corners android:radius="5dip" /> 
            <padding android:left="10dp" android:top="10dp" 
                android:right="10dp" android:bottom="10dp" /> 
        </shape> 
    </item> 
</selector>  

gradient 主体渐变 startColor开始颜色,endColor结束颜色 ,angle开始渐变的角度(值只能为90的倍数,0时为左到右渐变,90时为下到上渐变,依次逆时针类推)
stroke 边框 width 边框宽度,color 边框颜色
corners 圆角 radius 半径,0为直角
padding text值的相对位置

[转载]Android Gallery组件详解-Android开发实例教程-eoe Android开发者社区_Android开发论坛

mikel阅读(705)

[转载]Android Gallery组件详解-Android开发实例教程-eoe Android开发者社区_Android开发论坛.

Gallery组件主要用于横向显示图像 列表,不过按常规做法。Gallery组件只能有限地显示指定的图像。也就是说,如果为Gallery组件指定了10张图像,那么当Gallery组件显 示到第10张时,就不会再继续显示了。这虽然在大多数时候没有什么关系,但在某些情况下,我们希望图像显示到最后一张时再重第1张开始显示,也就是循环显 示。要实现这种风格的Gallery组件,就需要对Gallery的Adapter对象进行一番改进。

       Gallery组件的传统用法

在实现可循环显示图像的Gallery组件之前先来回顾一下Gallery组件的传统用法。Gallery组件可以横向显示一个图像列表,当单击当前图像 的后一个图像时,这个图像列表会向左移动一格,当单击当前图像的前一个图像时,这个图像列表会向右移动一样。也可以通过拖动的方式来向左和向右移动图像列 表。当前显示的是第1个图像的效果如图1所示。Gallery组件显示到最后一个图像的效果如图2所示。

效果图:


从图2可以看出,当显示到最后一个图像时,列表后面就没有图像的,这也是Gallery组件的基本显示效果。在本文后面的部分将详细介绍如何使Gallery组件显示到最后一个图像时会从第1个图像开始显示。

好了,现在我们来看一下图1和图2的效果是如何做出来的吧。Gallery既然用于显示图像,那第1步就必须要有一些图像文件用来显示。现在可以随意准备 一些图像。在本文的例子中准备了15个jpg文件(item1.jpg至item15.jpg)。将这些文件都放在res\drawable目录中。

下面将这些图像的资源ID都保存在int数组中,代码如下:

private int[] resIds = new int[]{
R.drawable.item1, R.drawable.item2, R.drawable.item3,
R.drawable.item4, R.drawable.item5, R.drawable.item6,
R.drawable.item7, R.drawable.item8, R.drawable.item9,
R.drawable.item10, R.drawable.item11, R.drawable.item12,
R.drawable.item13, R.drawable.item14, R.drawable.item15
};

main.xml文件中配置了一个Gallery组件,代码如下:

<!--?xml version="1.0" encoding="utf-8"?-->

现在在onCreate方法中装载这个组件,代码如下:

public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 装载Gallery组件
Gallery gallery = (Gallery) findViewById(R.id.gallery);
// 创建用于描述图像数据的ImageAdapter对象
ImageAdapter imageAdapter = new ImageAdapter(this);
// 设置Gallery组件的Adapter对象
gallery.setAdapter(imageAdapter);
}

在上面的代码中涉及到一个非常重要的类:ImageAdapter。该类是Android.widget.BaseAdapter的子类,用于描述图像信息。下面先看一下这个类的完整代码。

public class ImageAdapter extends BaseAdapter
{
int mGalleryItemBackground;
private Context mContext;

public ImageAdapter(Context context)
{
mContext = context;
// 获得Gallery组件的属性
TypedArray typedArray = obtainStyledAttributes(R.styleable.Gallery);
mGalleryItemBackground = typedArray.getResourceId(
R.styleable.Gallery_android_galleryItemBackground, 0);
}
// 返回图像总数
public int getCount()
{
return resIds.length;
}
public Object getItem(int position)
{
return position;
}

public long getItemId(int position)
{
return position;
}
// 返回具体位置的ImageView对象
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView imageView = new ImageView(mContext);
// 设置当前图像的图像(position为当前图像列表的位置)
imageView.setImageResource(resIds[position]);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setLayoutParams(new Gallery.LayoutParams(163, 106));
// 设置Gallery组件的背景风格
imageView.setBackgroundResource(mGalleryItemBackground);
return imageView;
}
}

在编写ImageAdapter类时应注意的两点:

1. 在ImageAdapter类的构造方法中获得了Gallery组件的属性信息。这些信息被定义在res\values\attrs.xml文件中,代码如下:

<!--?xml version="1.0" encoding="utf-8"?-->





上面的属性信息用于设置Gallery的背景风格。

2.  在ImageAdapter类中有两个非常重要的方法:getCount和getView。其中getCount方法用于返回图像总数,要 注意的是,这个总数不能大于图像的实际数(可以小于图像的实际数),否则会抛出越界异常。当Gallery组件要显示某一个图像时,就会调用 getView方法,并将当前的图像索引(position参数)传入该方法。一般getView方法用于返回每一个显示图像的组件(ImageView 对象)。从这一点可以看出,Gallery组件是即时显示图像的,而不是一下将所有的图像都显示出来。在getView方法中除了创建了 ImageView对象,还用从resIds数组中获得了相应的图像资源ID来设置在ImageView中显示的图像。最后还设置了Gallery组件的 背景显示风格。

OK,现在来运行这个程序,来回拖动图像列表,就会看到如图1和图2所示的效果了。

       循环显示图像的原理

    循环显示有些类似于循环链表,最后一个结点的下一个结点又是第1个结点。循环显示图像也可以模拟这一点。也许细心的读者从上一节实现的 ImageAdapter类中会发现些什么。对!就是getView方法中的position参数和getCount方法的关系。position参数的 值是不可能超过getCount方法返回的值的,也就是说,position参数值的范围是0至getCount() – 1。
    如果这时Gallery组件正好显示到最后一个图像,position参数值正好为getCount() – 1。那么我们如何再让Gallery显示下一个图像呢?也就是说让position参数值再增1,对!将getCount()方法的返回值也增1。
那 么这里还有一个问题,如果position参数值无限地增加,就意味着resIds数组要不断地增大,这样会大大消耗系统的资源。想到这,就需要解决两个 问题:既要position不断地增加,又让resIds数组中保存的图像资源ID是有限的,该怎么做呢?对于getCount()方法非常好解决,可以 让getCount方法返回一个很大的数,例如,Integer.MAX_VALUE。这时position参数值就可以随着Gallery组件的图像不 断向前移动而增大。现在resIds数组只有15个元素,如果position的值超过数组边界,要想继续循环取得数组中的元素(也就是说,当 position的值是15时,取resIds数组的第0个元素,是16时取第1个元素),最简单的方法就是取余,代码如下:
resIds[position % resIds.length]

 

    在本节对ImageAdapter类做了如下两个改进:
        1.使getCount方法返回一个很大的值。建议返回Integer.MAX_VALUE。
        2.在getView方法中通过取余来循环取得resIds数组中的图像资源ID。
    通过上面两点改进,可以使图像列表在向右移动时会循环显示图像。当然,这种方法从本质上说只是伪循环,也就是说,如果真把图像移动到getCount方法 返回的值那里,那也就显示到最后一个图像的。不过在这里getCount方法返回的是Integer.MAX_VALUE,这个值超过了20亿,除非有人 真想把图像移动到第20亿的位置,否则Gallery组件看着就是一个循环显示图像的组件。

   实现循环显示图像的Gallery组件

在本节将组出与循环显示图像相关的ImageAdapter类的完整代码。读者可以从中看到上一节介绍的两点改进。为了使界面看上去更丰满,本例还在单击 某一个Gallery组件中的图像时在下方显示一个放大的图像(使用ImageSwitcher组件)。本例的显示效果如图3所示。当不断向后移动图像 时,图像可不断显示,读者可以自己运行本例来体验一下。

      在main.xml文件中定义的Gallery和ImageSwitcher组件的代码如下:
<font color="black"><font face="微软雅黑"><font size="3"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Gallery 
android:id="@+id/gallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp" />
<ImageSwitcher 
android:id="@+id/imageswitcher"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp" />
</LinearLayout></font></font></font>

本例中Main类的完整代码如下:

package eoe.demo;
 
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Gallery.LayoutParams;
import android.widget.ViewSwitcher.ViewFactory;
 
public class Main extends Activity implements OnItemSelectedListener,
ViewFactory{
private Gallery gallery;
private ImageSwitcher imageSwitcher;
private ImageAdapter imageAdapter;
 
private int[] resIds = new int[]
{ R.drawable.item1, R.drawable.item2, R.drawable.item3, R.drawable.item4,
R.drawable.item5, R.drawable.item6, R.drawable.item7,
R.drawable.item8, R.drawable.item9, R.drawable.item10,
R.drawable.item11, R.drawable.item12, R.drawable.item13,
R.drawable.item14, R.drawable.item15 };
 
public class ImageAdapter extends BaseAdapter
{
int mGalleryItemBackground;
private Context mContext;
 
public ImageAdapter(Context context)
{
mContext = context;
TypedArray typedArray = obtainStyledAttributes(R.styleable.Gallery);
mGalleryItemBackground = typedArray.getResourceId(
R.styleable.Gallery_android_galleryItemBackground, 0); 
}
// 第1点改进,返回一个很大的值,例如,Integer.MAX_VALUE
public int getCount()
{
return Integer.MAX_VALUE;
}
 
public Object getItem(int position)
{
return position;
}
 
public long getItemId(int position)
{
return position;
}
 
public View getView(int position, View convertView, ViewGroup parent)
{
ImageView imageView = new ImageView(mContext);
// 第2点改进,通过取余来循环取得resIds数组中的图像资源ID
imageView.setImageResource(resIds[position % resIds.length]);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setLayoutParams(new Gallery.LayoutParams(163, 106));
imageView.setBackgroundResource(mGalleryItemBackground);
return imageView;
}
}
 
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,long id)
{
// 选中Gallery中某个图像时,在ImageSwitcher组件中放大显示该图像
imageSwitcher.setImageResource(resIds[position % resIds.length]);
 
}
@Override
public void onNothingSelected(AdapterView<?> parent)
{
}
 
@Override
// ImageSwitcher组件需要这个方法来创建一个View对象(一般为ImageView对象)
// 来显示图像
public View makeView()
{
ImageView imageView = new ImageView(this);
imageView.setBackgroundColor(0xFF000000);
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setLayoutParams(new ImageSwitcher.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
return imageView;
}
 
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gallery = (Gallery) findViewById(R.id.gallery);
imageAdapter = new ImageAdapter(this);
gallery.setAdapter(imageAdapter);
gallery.setOnItemSelectedListener(this);
imageSwitcher = (ImageSwitcher) findViewById(R.id.imageswitcher);
// 设置ImageSwitcher组件的工厂对象
imageSwitcher.setFactory(this);
// 设置ImageSwitcher组件显示图像的动画效果
imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(this,
android.R.anim.fade_in)); imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this,
android.R.anim.fade_out));
 
}
}