AutoCompleteTextView和自定義的CursorAdapter

weixin_33831673發表於2015-02-10

用雅虎天氣介面和AutoCompleteTextView開發天氣應用(1)

這篇文章是Yahoo天氣API系列文章的第一篇,我們的目標就是使用Yahoo提供的天氣介面獲取當前的氣象情況。之前的兩篇文章已經介紹過了其它的天氣介面,比如openweathermap介面,如果你感興趣的話,可以看看這兩篇文章文章一文章二

這篇文章中,我們主要介紹怎麼用Yahoo的API檢索出各個城市資訊。假設你已經有了一個Yahoo開發者賬戶。如果還沒有,那麼可以通過這個連結註冊一個(註冊連結)。你必須擁有一個appid,雖然它是完全免費的,但是你在使用yahoo的API的時候必須用上它。在分析Yahoo的API的時候,我們順便介紹一些有意思的Android上的控制元件比如AutoCompleteTextView和android上的XML解析器。我們最後要實現一個Android APP,當使用者輸入一部分城市的名字時,就可以顯示出所有和使用者輸入所匹配的城市選項,如下圖所示:

android_yahoo_weather_autocomplete_thumb[2]

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.

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. import android.content.Context;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.widget.BaseAdapter;
  9. import android.widget.Filter;
  10. import android.widget.Filterable;
  11. import android.widget.ImageView;
  12. import android.widget.TextView;
  13. import cn.jtang.discussion.R;
  14. import cn.jtang.discussion.db.UserDB;
  15. import cn.jtang.discussion.mode.User;
  16.  
  17. import com.googlecode.androidannotations.annotations.Bean;
  18. import com.googlecode.androidannotations.annotations.EBean;
  19. import com.googlecode.androidannotations.annotations.RootContext;
  20.  
  21. @EBean
  22. public class LoginUsernameAdapter extends BaseAdapter implements Filterable {
  23. LayoutInflater mInflater;
  24. ArrayList<User> users;
  25. String key;
  26. @RootContext
  27. Context context;
  28. @Bean
  29. UserDB userDb;
  30. DBFilter filter;
  31. @Override
  32. public int getCount() {
  33. // TODO Auto-generated method stub
  34. if (users != null && users.size() > 0) {
  35. return users.size();
  36. }
  37. return 0;
  38. }
  39.  
  40. @Override
  41. public Object getItem(int position) {
  42. // TODO Auto-generated method stub
  43. return users.get(position);
  44. }
  45.  
  46. @Override
  47. public long getItemId(int position) {
  48. // TODO Auto-generated method stub
  49. return position;
  50. }
  51.  
  52. @Override
  53. public View getView(final int position, View convertView, ViewGroup parent) {
  54. // TODO Auto-generated method stub
  55. if (mInflater == null) {
  56. mInflater = LayoutInflater.from(context);
  57. }
  58. final User user = users.get(position);
  59.  
  60. View view = mInflater.inflate(R.layout.item_actv_username, null);
  61. TextView tv_username = (TextView) view.findViewById(R.id.tv_username);
  62. tv_username.setText(user.getUsername());
  63. ImageView iv_delete = (ImageView) view.findViewById(R.id.iv_delete);
  64. //新增點選事件
  65. iv_delete.setOnClickListener(new View.OnClickListener() {
  66. @Override
  67. public void onClick(View v) {
  68. // TODO Auto-generated method stub
  69. //點選後刪除使用者
  70. userDb.deleteUser(user);
  71. users.remove(position);
  72. notifyDataSetChanged();
  73. }
  74. });
  75. return view;
  76. }
  77. /**
  78. * 獲取過濾器
  79. */
  80. @Override
  81. public Filter getFilter() {
  82. // TODO Auto-generated method stub
  83. if (filter == null) {
  84. filter = new DBFilter();
  85. }
  86. return filter;
  87. }
  88.  
  89. /**
  90. * 資料庫查詢過濾器
  91. *
  92. * @author Administrator
  93. *
  94. */
  95. private class DBFilter extends Filter {
  96. /**
  97. * 查詢資料庫
  98. */
  99. @Override
  100. protected FilterResults performFiltering(CharSequence prefix) {
  101. // TODO Auto-generated method stub
  102. //查詢結果儲存到FilterResults物件裡
  103. FilterResults results = new FilterResults();
  104. List<User> queryUsers = userDb.query(prefix.toString());
  105. results.values = queryUsers;
  106. results.count = queryUsers.size();
  107. return results;
  108. }
  109.  
  110. /**
  111. * 更新UI
  112. */
  113. @Override
  114. protected void publishResults(CharSequence constraint, FilterResults results) {
  115. // TODO Auto-generated method stub
  116. List<User> queryUsers = (List<User>) results.values;
  117. //把結果讀取出複製到users裡
  118. if (users == null) {
  119. users = new ArrayList<User>();
  120. }
  121. if (users.size() > 0) {
  122. users.clear();
  123. }
  124.  
  125. if (queryUsers != null && queryUsers.size() > 0)
  126. for (User user : queryUsers) {
  127. users.add(user);
  128. notifyDataSetChanged();
  129. }
  130. }
  131.  
  132. }
  133. }
  134.  

© 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",他表示你最少要輸入關鍵字的個數;

        下面的程式碼以查詢聯絡人為例:

 

Java程式碼  收藏程式碼
  1. package com.zymic.home;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.ContentResolver;  
  5. import android.content.Context;  
  6. import android.database.Cursor;  
  7. import android.os.Bundle;  
  8. import android.provider.Contacts;  
  9. import android.provider.Contacts.People;  
  10. import android.view.LayoutInflater;  
  11. import android.view.View;  
  12. import android.view.ViewGroup;  
  13. import android.widget.AutoCompleteTextView;  
  14. import android.widget.CursorAdapter;  
  15. import android.widget.Filterable;  
  16. import android.widget.TextView;  
  17. import android.widget.Toast;  
  18.   
  19. public class AutoTextViewEx extends Activity {  
  20.     private AutoCompleteTextView autoTextView;  
  21.     private Cursor cursor;  
  22.     //  
  23.     private static final String[] PEOPLE_PROJECTION = new String[] {  
  24.         Contacts.People._ID,  
  25.         Contacts.People.PRIMARY_PHONE_ID,  
  26.         Contacts.People.TYPE,  
  27.         Contacts.People.NUMBER,  
  28.         Contacts.People.LABEL,  
  29.         Contacts.People.NAME,  
  30.     };  
  31.     //  
  32.     @Override  
  33.     public void onCreate(Bundle savedInstanceState) {  
  34.         super.onCreate(savedInstanceState);  
  35.         setContentView(R.layout.main);  
  36.         //  
  37.         autoTextView=(AutoCompleteTextView)findViewById(R.id.autotextview);  
  38.         cursor=getApplicationContext().getContentResolver().query(People.CONTENT_URI, PEOPLE_PROJECTION, null, null, null);  
  39.         MyAdapter adapter=new MyAdapter(this,cursor);  
  40.         autoTextView.setAdapter(adapter);  
  41.     }  
  42.     //  
  43.     public class MyAdapter extends CursorAdapter implements Filterable{  
  44.         Cursor cursor;  
  45.         public MyAdapter(Context context, Cursor c) {  
  46.             super(context, c);  
  47.             // TODO Auto-generated constructor stub  
  48.         }  
  49.   
  50.         @Override  
  51.         public void bindView(View view, Context context, Cursor cursor) {  
  52.             ((TextView)view).setText(cursor.getString(5));  
  53.             Toast.makeText(getApplicationContext(), "bindView", Toast.LENGTH_SHORT).show();  
  54.         }  
  55.   
  56.         @Override  
  57.         public View newView(Context context, Cursor cursor, ViewGroup parent) {  
  58.             LayoutInflater inflater = (LayoutInflater)context.getSystemService (  
  59.               Context.LAYOUT_INFLATER_SERVICE);  
  60.             TextView view=(TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, null);  
  61.             //view.setText(cursor.getString(5));  
  62.             Toast.makeText(getApplicationContext(), "newView", Toast.LENGTH_SHORT).show();  
  63.             return view;  
  64.         }  
  65.   
  66.         @Override  
  67.         public Cursor runQueryOnBackgroundThread(CharSequence constraint) {  
  68.             //UPPER(Contacts.People.NAME)  
  69.             String where="UPPER("+Contacts.People.NAME+") GLOB ?";  
  70.             String[]to=new String[]{"*"+constraint.toString().toUpperCase()+"*"};  
  71.             cursor=getApplicationContext().getContentResolver().query(People.CONTENT_URI, PEOPLE_PROJECTION,  
  72.                     where, to, null);  
  73.             //System.out.println(cursor);  
  74.             Toast.makeText(getApplicationContext(), "runQuery", Toast.LENGTH_SHORT).show();  
  75.             return cursor;  
  76.         }  
  77.           
  78.           
  79.     }  
  80. }  

 

 

 

相關文章