一步一步教你写一个快递查询APP(适合新手)

前言:

水平:自学Android十五天,以前有过混日子的编程经验。

目标: 《第一行代码》学完之后,总想写个APP,天气的APP写了个初版,后面再说,今天演示的是制作快递查询APP的整个经过。

适合人群:新手

工具:Android Studio 2.2.2

  • 新建一个工程,选择Empty Actvity即可,其他默认。

1.布局

actvity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.blade.kuaidisearch.MainActivity">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="16sp"
    android:text="@string/input_postid" />

<LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="45dp">

    <EditText
        android:id="@+id/postid"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="@layout/edit_bg"
        android:hint="@string/input_your_courier_number"
        android:layout_height="35dp" />

    <!--android:entries="@array/kdcompany"-->
    <Spinner
        android:id="@+id/spinner"
        android:entries="@array/kdcompany"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    </Spinner>
</LinearLayout>

<Button
    android:id="@+id/query"
    android:text="@string/btn_query"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<ListView
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>
</LinearLayout>

涉及到的相应要修改的文件,res->value->strings.xml文件

<resources>
<string name="app_name">KuaiDISearch</string>
<string name="input_postid">请输入快递单号:</string>
<string name="btn_query">查询</string>
<string name="input_your_courier_number">input your Courier number</string>
</resources>

看默认的输入框不爽,OK,我们来修改一下样式。在layout文件下新建一个名为edit_bg.xml的文件。

然后天剑EditText的android:background=”@layout/edit_bg”

<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <solid
            android:color="#EFEFEF"/>
        <corners
            android:radius="3dip"
            />
        <stroke
            android:width="0.5px"
            android:color="#505050"/>
    </shape>
</item>
</layer-list>
  • 下拉框的数据绑定,本文是通过xml文件。

在values下新建一个名为arrays.xml的文件,

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="kdcompany">
    <item>申通</item>
    <item>EMS</item>
    <item>顺丰</item>
    <item>圆通</item>
    <item>中通</item>
    <item>韵达</item>
    <item>天天</item>
    <item>汇通</item>
    <item>全峰</item>
    <item>德邦</item>
    <item>宅急送</item>
</string-array>
</resources>
  • 我只找到十一家,其他的我暂时还没看。

  • 然后设置Spinner的entries属性。

    android:entries=”@array/kdcompany”

  • 最后我们还要设置每一个item的样式。

  • 在layout下新建一个名为item_list.xml文件。(csdn的markdown编辑器不知道为啥不显示我在markdownpad2里编辑内容,大家直接看源码吧。)

整体布局就是垂直方向线性布局,在第二层,嵌套一个水平方向的线性布局。

涉及到的控件:一个TextView,EditText,Spinner,Button,ListView

知识点:

  • LinearLayout,vertical,horizontal,layout_weight
  • 效果图:
    这里写图片描述

2.ListView使用方法

听说ListView是安卓开发常用的控件,看起来好像是。
我们的第一版的需求不要太复杂,把快递查询的结果分成两列,一列显示时间,一列显示内容(快递到哪儿了)

  • 运行一下程序,结果报错了:

Your content must have a ListView whose id attribute is ‘android.R.id.list’

原来在layout_main.xml中ListView的id需要使用系统的。

这一步需要注意的问题是ListView 控件的id要使用Android系统内置的 android:id=”@android:id/list”

忘了说了,我们让我们的MainActivity继承ListActivity,即extends ListActivity。

3.接口数据

昨天夜里一点逛知乎,看到了一个快递的免费接口,这让我马上想到做出这个应用。

http://www.kuaidi100.com/query?type=快递公司代号&postid=快递单号

快递公司代号:申通=”shentong” EMS=”ems” 顺丰=”shunfeng” 圆通=”yuantong” 中通=”zhongtong” 韵达=”yunda” 天天=”tiantian” 汇通=”huitongkuaidi” 全峰=”quanfengkuaidi” 德邦=”debangwuliu” 宅急送=”zhaijisong”

示例:http://www.kuaidi100.com/query?type=tiantian&postid=XXXXXXXXXXX

导入了org.apache.http.legacy还是无效呢,因为有两个版本的。
还要设置targetSdkVersion与legacy包相匹配。

4.解析JSON数据

4.1 Button的点击事件

先声明:

private Button query;

在onCreate方法里赋值:

query = (Button)findViewById(R.id.query);

用匿名内部类来写点击事件(其他写法,可以参考我的博文Button点击事件的四种写法)

    query.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            sendRequest();
        }
    });

点击按钮之后,我们想服务器(接口所在的)发送请求,sendRequest方法就是干这个的。

//发送Http请求
private void sendRequest(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            try{
                HttpClient httpClient = new DefaultHttpClient();
                //处理快递单号
                HttpGet httpGet = new HttpGet(http://www.kuaidi100.com/query?type=shunfeng&postid=XXXXXXX);//XXXXXX处填写快递单号
                HttpResponse httpResponse = httpClient.execute(httpGet);
                if(httpResponse.getStatusLine().getStatusCode() == 200){
                    //请求和响应都成功了
                    HttpEntity entity = httpResponse.getEntity();
                    String response = EntityUtils.toString(entity,"utf-8");
                    parseJSON(response);
                }
            }catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }).start();
}

在这里补一句,HttpClient需要自己在libs添加一个jar包。自己通过在windows上搜索,找到org.apache.http.legacy.jar 还要注意一个版本的问题,你的build.gradle(Module:app)里的targetSdkVersion是24,那么你就在例如D:\Android\sdk\platforms\android-24\optional文件夹下找。

android-24,我之前找了个android-23的,出不来

接受到的JSON数据如下:

"message": "ok",
"nu": "XXXXXXXXXXX",
"ischeck": "0",
"condition": "00",
"com": "tiantian",
"status": "200",
"state": "0",
"data": [
    {
        "time": "2016-12-18 18:57:54",
        "ftime": "2016-12-18 18:57:54",
        "context": "快件已签收,签收人是乡镇代理点,签收网点是XXXXXX",
        "location": ""
    },
  • 先声明一个list和一个HashMap,哈希表是按照键值对来储存数据了。

ArrayList

4.2 到了关键部分了,就是解析JSON数据,有很多种方式,其他方式可以上网搜一搜。

//解析JSON数据
private void parseJSON(String jsonData){
    try{
        //我们需要解析数组data的数据,其中time和context就是需要的字段
        JSONArray jsonArray = new JSONObject(jsonData).getJSONArray("data");
        for (int i = 0;i<jsonArray.length();i++){
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String time = jsonObject.getString("time");
            String context = jsonObject.getString("context");
            Log.d("data","time is " + time);
            Log.d("context","context is " + context);

            map = new HashMap<String,String>();
            map.put("time",time);
            map.put("context",context);
            list.add(map);
        }

        //在子线程向ListView里添加数据
        new Thread(){
            public void run(){
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        SimpleAdapter simpleAdapter = new SimpleAdapter(MainActivity.this,list,
                                R.layout.item_list,form,to);
                        setListAdapter(simpleAdapter);
                        //假如没有数据,提醒用户
                        if(lv.getCount() == 0){
                            Toast.makeText(MainActivity.this,"还没有数据",Toast.LENGTH_LONG).show();
                        }
                    }
                });
            }
        }.start();

    }catch (Exception e)
    {
        e.printStackTrace();
    }
}
  • 假如没有在子线程更新view,就会报错,如下:

Only the original thread that created a view hierarchy can touch its views.

5.下拉框

到这里,应该可以查询到单条数据了,

回到上面的sendRequest方法。可以看到我们刚才发送请求的地址是:

//处理快递单号

HttpGet httpGet = new HttpGet(http://www.kuaidi100.com/query?

type=shunfeng&postid=XXXXXXX);//XXXXXX处填写快递单号
  • 现在我们要实现输入框和下拉框的功能,为了能让大家看得更直观,我就先只传了一个具体的链接。
  • 需要发送的链接里包含两个参数,一个是快递公司的代号,我们可以获得下拉框选中的值得到,另一个是快递单号,可以通过获取输入框的值得到。

声明:

private EditText postid;
private Spinner spinner;

赋值

    postid = (EditText)findViewById(R.id.postid);
    spinner = (Spinner)findViewById(R.id.spinner);

需要修改一下sendRequest方法。

//发送Http请求
private void sendRequest(){
    new Thread(new Runnable() {
        @Override
        public void run() {
            try{
                HttpClient httpClient = new DefaultHttpClient();
                //处理快递单号
                String queryString = checkPostid();
                HttpGet httpGet = new HttpGet(queryString);
                HttpResponse httpResponse = httpClient.execute(httpGet);
                if(httpResponse.getStatusLine().getStatusCode() == 200){
                    //请求和响应都成功了
                    HttpEntity entity = httpResponse.getEntity();
                    String response = EntityUtils.toString(entity,"utf-8");
                    parseJSON(response);
                }
            }catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }).start();
}

checkPostid方法就是通过识别快递单号是哪家的快递,给服务器传递响应的请求

//识别快递单号是哪家的快递,给服务器传递响应的请求
private String checkPostid(){
    //基础链接
    String baseStr = "http://www.kuaidi100.com/query?type=";
    String type= "";
    String postidNumber = postid.getText().toString().trim();
    String kuaidiCompany = spinner.getSelectedItem().toString().trim();
    Log.d("post id",postidNumber);
    Log.d("kuaidiCompany id",kuaidiCompany);

    if(kuaidiCompany.equals("申通")){
        type = "shentong";
    }else if(kuaidiCompany.equals("EMS")){
        type = "ems";
    }else if(kuaidiCompany.equals("顺丰")){
        type = "shunfeng";
    }else if(kuaidiCompany.equals("圆通")){
        type = "yuantong";
    }else if(kuaidiCompany.equals("中通")){
        type = "zhongtong";
    }else if (kuaidiCompany.equals("韵达")){
        type = "yunda";
    }else if (kuaidiCompany.equals("天天")){
        type = "tiantian";
    }else if (kuaidiCompany.equals("汇通")){
        type = "huitongkuaidi";
    }else if (kuaidiCompany.equals("全峰")){
        type = "quanfengkuaidi";
    }else if (kuaidiCompany.equals("德邦")){
        type = "debangwuliu";
    }else if(kuaidiCompany.equals("宅急送")){
        type = "zhaijisong";
    }
    Log.d("url",baseStr + type + "&postid=" + postidNumber);
    return baseStr + type + "&postid=" + postidNumber;
}
  • 不要吐槽我的英文,想半天也不知道怎么取个合适的方法名。连工程名也写错了意思。/(ㄒoㄒ)/~~

6.测试

天天,顺丰,中通,韵达都测试过,没有问题。

  • 为了有更好的用户体验,在没有数据的时候,给一个Toast提示一下用户。

    用listview的getCount方法,假如是0,说明没有数据。

                        //假如没有数据,提醒用户
                        if(lv.getCount() == 0){
                            Toast.makeText(MainActivity.this,"还没有数据",Toast.LENGTH_LONG).show();
                        }
    

有时候出现这种情况,那么要去了解status为201是什么意思。今天就算了,都凌晨了。

{“message”:”快递公司参数异常:单号不存在或者已经过期”,”nu”:”“,”ischeck”:”0”,”condition”:”“,”com”:”“,”status”:”201”,”state”:”0”,”data”:[]}

其他的查询快递的APP都是直接输快递单号的,为啥这个不行,因为我在网上找不到快递公司单号的规则啊,就是怎么通过快递单号码来知道它是哪家公司的,这个不像身份证一样那么有规律。

7.问题

  • android没有标题栏
  • 清空android ListView控件的内容

8.后期版本

  • 扫二维码(不过,还是多看看接口的文档,快递单号的问题没解决,扫码暂时也没啥用)
  • 输入单号自动识别快递公司
  • 美化控件和界面
  • 标题栏的问题
  • 菜单栏(先解决标题栏的问题)

9.参考资料以及扩展

  • 接口文档

https://www.kuaidi100.com/openapi/api_post.shtml

  • 美化界面

http://blog.sina.com.cn/s/blog_7256fe8f0101ddmd.html

http://blog.csdn.net/badboy007/article/details/19034609

  • 给EditText设置边框

http://www.cnblogs.com/johnsonwei/p/5785055.html

http://www.jb51.net/article/46652.htm

  • EditText自定义样式

http://blog.csdn.net/jdsjlzx/article/details/25063169

  • layout的五大布局

http://www.tuicool.com/articles/3uUZbmu

  • android开发教程之listview使用方法

http://www.jb51.net/article/46652.htm

  • Android采用ListView实现数据列表显示

http://blog.csdn.net/furongkang/article/details/6819247

  • Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法与有用代码片段

http://blog.csdn.net/shakespeare001/article/details/7926783

  • android Json解析详解(详细代码)

http://blog.csdn.net/onlyonecoder/article/details/8490924

  • Android子线程中更新UI的3种方法

http://gqdy365.iteye.com/blog/2112471

  • Android子线程真的不能更新UI么

http://www.cnblogs.com/lao-liang/p/5108745.html

  • ListView之SimpleAdapter的使用

http://blog.csdn.net/abc5382334/article/details/13503475/

  • 免费实用的API接口

https://zhuanlan.zhihu.com/p/21320392?refer=passer

10.结束语

感谢以上文章的作者,给了我做出第一款APP的资源。

时间已经到了00:43,我还是睡不着。

代码写的烂,需要改进的地方很多。

基础不扎实,还得好好搞搞java。

需要做的事很多,再也没有接口说不会了,说很难了。因为你可以做到的。

一个曾经写过程序又没写了,现在又写了的菜鸟程序员实在是有点啰嗦。

程序的源码我整理一下再上传,方便大家参考,我怕自己没写清楚。
地址(设置了1分的鼓励分):http://download.csdn.net/detail/a1b2c300/9724067

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值