用雅虎天气接口和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;
- }
- }
- }