用雅虎天气接口和AutoCompleteTextView开发天气应用(1)

2014/03/20 | 分类: ANDROID开发 | 2 条评论 | 标签: 天气安卓开发

分享到:5
本文由 伯乐在线 - chris 翻译。未经许可,禁止转载!
英文出处:survivingwithandroid。欢迎加入翻译小组

这篇文章是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.

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

© 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",他表示你最少要输入关键字的个数;

下面的代码以查询联系人为例:

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

AutoCompleteTextView和自定义的CursorAdapter的更多相关文章

  1. 5.AutoCompleteTextView、自定义广播

    新建信息 布局:自动出来的是系统的组件,里面是listview,写ontextchanglis也行 <LinearLayout xmlns:android="http://schema ...

  2. 【Android】13.2 使用自定义的CursorAdapter访问SQLite数据库

    分类:C#.Android.VS2015: 创建日期:2016-02-26 一.简介 SQliteDemo1的例子演示了SimpleCursorAdapter的用法,本节我们将使用用途更广的自定义的游 ...

  3. AutoCompleteTextView自动补全文本框

    AutoCompleteTextView的作用是在输入框中输入我们想要输入的信息,就会出现其他与其相关的提示信息 下面是实例代码: MainActivity.java package com.shao ...

  4. Android系统对话框——自定义关闭

    Android系统对话框--自定义关闭 Dialog是我们在项目中经常用到的,5.x以后的Dialog也很好看,很安卓风,Android也给我们提供了新的包,低版本可以显示一样的效果.我们在使用的导入 ...

  5. Andorid-15k+的面试题。

    andorid开发也做了3年有余了,也面试很多加企业,借此机会分享一下,我们中遇到过的问题以及解决方案吧,希望能够对正在找工作的andoird程序员有一定的帮助. 特别献上整理过的50道面试题目 1. ...

  6. 【Android Developers Training】 101. 显示快速联系人挂件(Quick Contact Badge)

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. android AutoCompleteTextView 自定义BaseAdapter

    最近项目中需要做搜索功能,实现类似 Google.Baidu 搜索的 下拉提示效果.Android为我们提供了 AutoCompleteTextView 控件来完成此功能. 网上好多例子都是简单使用 ...

  8. android开发之自定义AutoCompleteTextView

    AutoCompleteTextView,很多人都用过,有些情况下使用Google提供的ArrayAdapter作为适配器就可以完成需求,但是在实际开发中,我们经常需要开发自定义适配器来完成开发工作. ...

  9. AutoCompleteTextView 简单用法 实现自定义list adapter

    网上有不少教程,那个提示框字符集都是事先写好的,例如用一个String[] 数组去包含了这些数据,但是,我们也可以吧用户输入的作为历史记录保存       下面先上我写的代码:import andro ...

随机推荐

  1. 使用while和read命令读取文件内容

    转:使用while和read命令读取文件内容 1.准备数据文件 $cat a.txt 200:2 300:3 400:4 500:5 2.用while循环从文件中读取数据 #!/bin/ksh whi ...

  2. [HNOI2018]排列[堆]

    题意 给定一棵树,每个点有点权,第 \(i\) 个点被删除的代价为 \(w_{p[i]}\times i\) ,问最小代价是多少. 分析 与国王游戏一题类似. 容易发现权值最小的点在其父亲选择后就会立 ...

  3. getUserMedia API及HTML5 调用摄像头和麦克风

    getUserMedia API简介 HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头.视频.音频.地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件 ...

  4. Redis常用操作-------Set(集合)

    1.SADD key member [member ...] 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略. 假如 key 不存在,则创建一个 ...

  5. swap函数

    #include<iostream> using namespace std; void swap(int& a,int& b){ int t=a; a=b; b=t; } ...

  6. mac安装sublime text 3,含注册码

    软件下载地址: https://www.sublimetext.com/3 注册码如下: —– BEGIN LICENSE —– TwitterInc 200 User License EA7E-89 ...

  7. DockerHub使用简介

    常用的Docker镜像文件都有,就不用自己费劲的一点点配置了,这才是Docker的真正目的.就像Ghost里边含office,直接还原,不用一台台机器安装呢,省时省力,与高效工作的理念相契合. 至于, ...

  8. Java的Vector源码阅读

    * The {@code Vector} class implements a growable array of * objects. Like an array, it contains comp ...

  9. Web接口测试-HttpClient

    要实现Web接口测试的自动化有许多方式,比如利用Jmeter.Loadrunner等测试工具都能够实现接口的自动化测试,我们也可以利用一些开源的框架来实现接口的自动化测试,比如我们现在要说的这个Htt ...

  10. C#微信公众号开发入门教程

    首先打开开发文档: 微信公众号开发者文档:http://mp.weixin.qq.com/wiki/home/index.html 一.创建测试账号 可以先申请一个开发者测试账号