用雅虎天氣介面和AutoCompleteTextView開發天氣應用(1)
這篇文章是Yahoo天氣API系列文章的第一篇,我們的目標就是使用Yahoo提供的天氣介面獲取當前的氣象情況。之前的兩篇文章已經介紹過了其它的天氣介面,比如openweathermap
介面,如果你感興趣的話,可以看看這兩篇文章文章一和文章二。
這篇文章中,我們主要介紹怎麼用Yahoo的API檢索出各個城市資訊。假設你已經有了一個Yahoo開發者賬戶。如果還沒有,那麼可以通過這個連結註冊一個(註冊連結)。你必須擁有一個appid,雖然它是完全免費的,但是你在使用yahoo的API的時候必須用上它。在分析Yahoo的API的時候,我們順便介紹一些有意思的Android上的控制元件比如AutoCompleteTextView和android上的XML解析器。我們最後要實現一個Android APP,當使用者輸入一部分城市的名字時,就可以顯示出所有和使用者輸入所匹配的城市選項,如下圖所示:
Yahoo Woeid
獲得天氣資訊的第一步就是檢索Woeid,這是Yahoo提供給開發者的一個特殊的ID,用來分辨城市/地區資訊。我們需要根據使用者輸入的城市名稱來獲得這個woeid。
從介面的角度上看,我們希望通過使用者輸入的城市名稱或者城市名稱的部分,加上對應的woeid來獲取與之匹配的城市資訊列表,我們可以使用下面的API來獲取匹配某個公式的城市列表資訊。
http://where.yahooapis.com/v1/places.q(city_name_pattern);count=MAX_RESULT_SIZE?appid=your_app_id
如果你用瀏覽器來執行這個API,那麼你就可以得到一個xml檔案,即匹配city_name_pattern這個式子的城市資訊列表。
在Android中解析Yahoo的xml資料
現在我們要建立一個XML解析器來處理上一步中我們獲取到的資料。首先我們要新建一個資料model(MVC中的模型,也就是javaBean),對於我們這個例子來說,很簡單:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class CityResult { private String woeid; private String cityName; private String country; public CityResult() {} public CityResult(String woeid, String cityName, String country) { this .woeid = woeid; this .cityName = cityName; this .country = country; } // get and set methods @Override public String toString() { return cityName + "," + country; } } |
然後新建一個名為YahooClient
的類進行解析。這個類主要負責遍歷xml資料,然後進行轉換。裡面有一個靜態方法可以接收一個模式(pattern),通過匹配這個模式可以獲取城市資訊列表。一開始,先要開啟一個HTTP連線獲取資料流資訊,然後把這個資料流資訊傳給這個XML解析器,如下:
1
2
3
4
5
|
yahooHttpConn= (HttpURLConnection) ( new URL(query)).openConnection(); yahooHttpConn.connect(); XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); parser.setInput( new InputStreamReader(yahooHttpConn.getInputStream())); |
接下來可以開始轉換資料了。根據我剛才新建的模型類,可以從xml中尋找我們需要的資訊,我們需要Woeid、城市的名字和地區資訊,xml檔案中還有其它不需要關心的資訊,我們可以不用理會。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
int event = parser.getEventType(); CityResult cty = null ; String tagName = null ; String currentTag = null ; // We start parsing the XML while (event != XmlPullParser.END_DOCUMENT) { tagName = parser.getName(); if (event == XmlPullParser.START_TAG) { if (tagName.equals( "place" )) { // place Tag Found so we create a new CityResult cty = new CityResult(); Log.d( "Swa" , "New City found" ); } currentTag = tagName; Log.d( "Swa" , "Tag [" +tagName+ "]" ); } else if (event == XmlPullParser.TEXT) { // We found some text. let's see the tagName to know the tag related to the text if ( "woeid" .equals(currentTag)) cty.setWoeid(parser.getText()); else if ( "name" .equals(currentTag)) cty.setCityName(parser.getText()); else if ( "country" .equals(currentTag)) cty.setCountry(parser.getText()); // We don't want to analyze other tag at the moment } else if (event == XmlPullParser.END_TAG) { if ( "place" .equals(tagName)) result.add(cty); } event = parser.next(); } |
這段程式碼很簡單。第一行獲取第一個xml事件,然後開始遍歷xml資料檔案直到到達文件末尾。在這個方法結束的時候,就可以獲取我們想要得到的城市列表資料了。
AutoCompleteTextView和帶過濾器的陣列介面卡
在我們知道怎麼利用Yahoo的API從xml檔案中獲取資料後,我們就要向使用者展示這些資料。展示資料有很多中方式,我們使用AutoCompleteTextView
。這個控制元件在Android文件中是這樣定義的:“AutoCompleteTextView是一個可編輯文字檢視(View)。當歐諾個戶輸入時會自動提示符合條件的備選項。提示資訊在下拉選單中顯示。使用者可以選中其中一項替換當前的編輯框內容。”,正好符合我們的需求。使用這個控制元件不難,但是使用陣列介面卡加上過濾操作就有點複雜了。通常來說,使用這個控制元件時都是使用靜態的資料,而現在我們需要從遠端伺服器檢索資料。首先要實現一個自定義的介面卡,繼承 ArrayAdapter
十分簡單,示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
private class CityAdapter extends ArrayAdapter<CityResult> { private Context ctx; private List<CityResult> cityList = new ArrayList<CityResult>(); public CityAdapter(Context ctx, List<CityResult> cityList) { super (ctx, R.layout.cityresult_layout, cityList); this .cityList = cityList; this .ctx = ctx; } @Override public CityResult getItem( int position) { if (cityList != null ) return cityList.get(position); return null ; } @Override public int getCount() { if (cityList != null ) return cityList.size(); return 0 ; } @Override public View getView( int position, View convertView, ViewGroup parent) { View result = convertView; if (result == null ) { LayoutInflater inf = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); result = inf.inflate(R.layout.cityresult_layout, parent, false ); } TextView tv = (TextView) result.findViewById(R.id.txtCityName); tv.setText(cityList.get(position).getCityName() + "," + cityList.get(position).getCountry()); return result; } @Override public long getItemId( int position) { if (cityList != null ) return cityList.get(position).hashCode(); return 0 ; } } |
注意:最重要的是我們要從遠端伺服器檢索資料,然後可以通過實現Filterable
介面來處理我們希望得到的資料,所以程式碼還需要:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
private class CityAdapter extends ArrayAdapter<CityResult> implements Filterable { .... @Override public Filter getFilter() { Filter cityFilter = new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint == null || constraint.length() < 2 ) return results; List<CityResult> cityResultList = YahooClient.getCityList(constraint.toString()); results.values = cityResultList; results.count = cityResultList.size(); return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { cityList = (List) results.values; notifyDataSetChanged(); } }; return cityFilter; } .. } |
第四行我們實現了Filter
介面,有兩個方法需要實現,在performFiltering
方法中我們執行HTTP呼叫然後檢索資料。顯然我們這樣會導致ANR問題,因為我們知道不可以在主執行緒中傳送HTTP請求。但是,如果你閱讀performFiltering
方法的文件的話,就會發現這個方法其實是在另外的執行緒中執行的。所以我們不用擔心這個問題。
最後,我們為AutoCompleteTextView設定介面卡然後處理它的使用者點選事件:
1
2
3
4
5
6
7
8
9
|
AutoCompleteTextView edt = (AutoCompleteTextView) rootView.findViewById(R.id.edtCity); CityAdapter adpt = new CityAdapter( this .getActivity(), null ); edt.setAdapter(adpt); edt.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // We handle the onclick event and select the city chosen by the user } }); |
在下一篇文章中我們再介紹怎麼使用Woeid來獲取天氣資料,請繼續期待吧。
本文的原始碼很快就會發布。
————————————————————————————————————————————————————————————
Android 開發 AutoCompleteTextView結合自定義的介面卡,查詢資料庫
這裡沒有用CursorAdapter,而是自己繼承BaseAdapter寫了個介面卡.
與ListView不同,AutoCompleteTextView的介面卡除了繼承BaseAdapter外,還要實現Filterable介面。Filterable介面中有個getFilter方法,用於獲取過濾器,我們需要自己寫個繼承Filter的過濾器,實現資料庫查詢。
程式碼使用了androidannotations.
- import java.util.ArrayList;
- import java.util.List;
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.Filter;
- import android.widget.Filterable;
- import android.widget.ImageView;
- import android.widget.TextView;
- import cn.jtang.discussion.R;
- import cn.jtang.discussion.db.UserDB;
- import cn.jtang.discussion.mode.User;
- import com.googlecode.androidannotations.annotations.Bean;
- import com.googlecode.androidannotations.annotations.EBean;
- import com.googlecode.androidannotations.annotations.RootContext;
- @EBean
- public class LoginUsernameAdapter extends BaseAdapter implements Filterable {
- LayoutInflater mInflater;
- ArrayList<User> users;
- String key;
- @RootContext
- Context context;
- @Bean
- UserDB userDb;
- DBFilter filter;
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- if (users != null && users.size() > 0) {
- return users.size();
- }
- return 0;
- }
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return users.get(position);
- }
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return position;
- }
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- // TODO Auto-generated method stub
- if (mInflater == null) {
- mInflater = LayoutInflater.from(context);
- }
- final User user = users.get(position);
- View view = mInflater.inflate(R.layout.item_actv_username, null);
- TextView tv_username = (TextView) view.findViewById(R.id.tv_username);
- tv_username.setText(user.getUsername());
- ImageView iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
- //新增點選事件
- iv_delete.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- //點選後刪除使用者
- userDb.deleteUser(user);
- users.remove(position);
- notifyDataSetChanged();
- }
- });
- return view;
- }
- /**
- * 獲取過濾器
- */
- @Override
- public Filter getFilter() {
- // TODO Auto-generated method stub
- if (filter == null) {
- filter = new DBFilter();
- }
- return filter;
- }
- /**
- * 資料庫查詢過濾器
- *
- * @author Administrator
- *
- */
- private class DBFilter extends Filter {
- /**
- * 查詢資料庫
- */
- @Override
- protected FilterResults performFiltering(CharSequence prefix) {
- // TODO Auto-generated method stub
- //查詢結果儲存到FilterResults物件裡
- FilterResults results = new FilterResults();
- List<User> queryUsers = userDb.query(prefix.toString());
- results.values = queryUsers;
- results.count = queryUsers.size();
- return results;
- }
- /**
- * 更新UI
- */
- @Override
- protected void publishResults(CharSequence constraint, FilterResults results) {
- // TODO Auto-generated method stub
- List<User> queryUsers = (List<User>) results.values;
- //把結果讀取出複製到users裡
- if (users == null) {
- users = new ArrayList<User>();
- }
- if (users.size() > 0) {
- users.clear();
- }
- if (queryUsers != null && queryUsers.size() > 0)
- for (User user : queryUsers) {
- users.add(user);
- notifyDataSetChanged();
- }
- }
- }
- }
© 2013, 冰凍魚. 請尊重作者勞動成果,複製轉載保留本站連結! 應用開發筆記
http://zymic.iteye.com/blog/743538
AutoCompleteTextView配合自定義的CursorAdapter(setAdapter()),可以幫助我們完成查詢的功能.關鍵就在於類CursorAdapter.
CursorAdapter是繼承自BaseAdapter並且實現了介面Filterable 。所以在我們自己定義的CursorAdapter子類中就不需要在繼承Filterable,但對於JRE是1.5以下的虛擬機器來說。是要重寫方法getFilter()的;
要繼承CursorAdapter類。必須實現的方法有:
1)首先執行的是public Cursor runQueryOnBackgroundThread(CharSequence constraint),constraint就是我們輸入的要查詢的關鍵字;此方法產生查詢到的所有資料的cursor.並將其返回給下一個函式;
2)接下來執行方法public View newView(Context context, Cursor cursor, ViewGroup parent),cursor就是有第一種方法產生的.這個方法主要是產生一個個具體的承載cursor指向的資料的view類,最常見的是TextView;
3)接下來執行方法public void bindView(View view, Context context, Cursor cursor) 。view就是第2步產生的。cursor是第一步產生的。顯而易見,就是將兩者進行繫結。
但要注意的是,2和3是反覆交替執行的。產生多少條資料(cursor.getcount())就執行多少輪。還有一點容易忽視的就是在xml檔案中定義AutoCompleteTextView片段中一定要加入以下程式碼android:completionThreshold="1",他表示你最少要輸入關鍵字的個數;
下面的程式碼以查詢聯絡人為例:
- package com.zymic.home;
- import android.app.Activity;
- import android.content.ContentResolver;
- import android.content.Context;
- import android.database.Cursor;
- import android.os.Bundle;
- import android.provider.Contacts;
- import android.provider.Contacts.People;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.AutoCompleteTextView;
- import android.widget.CursorAdapter;
- import android.widget.Filterable;
- import android.widget.TextView;
- import android.widget.Toast;
- public class AutoTextViewEx extends Activity {
- private AutoCompleteTextView autoTextView;
- private Cursor cursor;
- //
- private static final String[] PEOPLE_PROJECTION = new String[] {
- Contacts.People._ID,
- Contacts.People.PRIMARY_PHONE_ID,
- Contacts.People.TYPE,
- Contacts.People.NUMBER,
- Contacts.People.LABEL,
- Contacts.People.NAME,
- };
- //
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //
- autoTextView=(AutoCompleteTextView)findViewById(R.id.autotextview);
- cursor=getApplicationContext().getContentResolver().query(People.CONTENT_URI, PEOPLE_PROJECTION, null, null, null);
- MyAdapter adapter=new MyAdapter(this,cursor);
- autoTextView.setAdapter(adapter);
- }
- //
- public class MyAdapter extends CursorAdapter implements Filterable{
- Cursor cursor;
- public MyAdapter(Context context, Cursor c) {
- super(context, c);
- // TODO Auto-generated constructor stub
- }
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- ((TextView)view).setText(cursor.getString(5));
- Toast.makeText(getApplicationContext(), "bindView", Toast.LENGTH_SHORT).show();
- }
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater)context.getSystemService (
- Context.LAYOUT_INFLATER_SERVICE);
- TextView view=(TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, null);
- //view.setText(cursor.getString(5));
- Toast.makeText(getApplicationContext(), "newView", Toast.LENGTH_SHORT).show();
- return view;
- }
- @Override
- public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
- //UPPER(Contacts.People.NAME)
- String where="UPPER("+Contacts.People.NAME+") GLOB ?";
- String[]to=new String[]{"*"+constraint.toString().toUpperCase()+"*"};
- cursor=getApplicationContext().getContentResolver().query(People.CONTENT_URI, PEOPLE_PROJECTION,
- where, to, null);
- //System.out.println(cursor);
- Toast.makeText(getApplicationContext(), "runQuery", Toast.LENGTH_SHORT).show();
- return cursor;
- }
- }
- }