透過Open data取得固定測速照相地點 – 使用Fast Android Networking、JavaBean、泛型Generics與自訂Adapter等

透過Open data取得固定測速照相地點 – 使用Fast Android Networking、JavaBean、泛型Generics與自訂Adapter等

不知大家有沒有這樣的經驗,經過某個路口,咦?剛剛好像有一道光…… 該不會被狗仔拍了?!啊~  不是啦!該不會又要捐國庫了?此時真希望能立馬查看剛剛經過的路口有沒有測速照相機。

這回,除了使用台北市政府開放平台,要介紹給各位另一個比OkHttp更厲害的第三方類別庫 Fast Android Networking。相信使用過OkHttp的朋友都知道它的好用和強大,而今竟有一款類別庫敢說它比OkHttp好用,就讓筆者小試一下。

話說在前,此案例寫法須具備一點Java基礎,JavaBean、泛型Generics,在此不細述Java觀念,提醒想成為Android開發人員的朋友,若想要完成稍有競爭力的Android APP,Java基礎知識是非常重要的。

Fast Android Networking

  • Fast Android Networking 是以 OkHttp 和 Okio 為基礎的第三方類別庫,同時具備了OkHttp高效和Okio節能(memory)的優點。
  • 支援將JSON轉成JavaBean /List<JavaBean>
  • 可擴充OkHttp
  • 支援目前所知任何種類的資料傳輸,包括檔案上傳、下載、multipart
  • 最重要的 ……. 使用Builder設計模式  (就是那顆包子有沒有?),非常直覺化,簡單易上手。

 

牛刀小試 – 帳號密碼登入檢查

在build.gradle加入Fast Android Networking類別庫

compile 'com.amitshekhar.android:android-networking:0.2.0'

AndroidManifest.xml內加入網路權限

<uses-permission android:name="android.permission.INTERNET" />

請自行設計內含帳號、密碼、登入按鍵之登入畫面,Componet Tree如下:

mag_speed_camera_05

送出帳密到伺服器驗證時,先檢查帳號密碼是否空白。在這裡我們發揮TextInputLayout元件的特性,將錯誤訊息顯示給使用者知曉。

        if (TextUtils.isEmpty(edit_ac.getText().toString())){
            edit_ac.setError("帳號不可為空白");
            return;
        }
        if (TextUtils.isEmpty(edit_pw.getText().toString())){
            edit_pw.setError("密碼不可為空白");
            return;
        }

執行後畫面如下,這樣是不是簡單又漂亮呢?

mag_speed_camera_06

請記得,開始使用AndroidNetworking前,要先在Application層級初始化AndroidNetworking。

AndroidNetworking.initialize(getApplicationContext());

接著要送出驗證了,在實務運用上,密碼驗證大部份採用post傳遞參數

AndroidNetworking.post("http://atm201605.appspot.com/login")
                .addQueryParameter("uid", edit_ac.getText().toString())
                .addQueryParameter("pw", edit_pw.getText().toString())
                .build()
                .getAsString(new StringRequestListener() {
                    @Override
                    public void onResponse(String response) {
                        if ("1".equals(response)){
                            setResult(RESULT_OK); finish(); 
                        } 
                    } 
             
                 });

第2-3行:addQueryParameter方法加入參數

第5行:getAsString 代表response為字串,此例回傳”0″或”1″,”1″代表帳號/密碼驗證成功,返回主畫面。

接著,開始進入本文主題。

 

台北市政府開放平台 — 共通性

在開始取回資料前,我們先觀察兩個Data.Taipei回傳的資料,分別取自「臺北市固定測速照相地點表」「臺北捷運列車到站站名」

mag_speed_camera_08    mag_speed_camera_07

從這兩個回傳JSON可知,我們要取得的實際內容為最內層紅框。

如果,在取得網路資料時,能同時將回傳最內層資料幫我放到List<JavaBean>裡,那不是美呆了嗎?

那麼,設計三層class來接收資料怎麼樣?

mag_speed_camera_09

不囉嗦,直接生出JavaBean如下:

public class SpeedCamera {
    String _id;
    String no;
    String functions;
    String road;
    String location;
    String area;
    String direction;
    String speed_limit;
     
    以下略
    (Constructor)

    (Getter and Setter)
}

但是這樣還不夠,回傳的內容可是有三層紅框呢!再加兩個如何?

public class Results {
    int offset;
    int limit;
    int count;
    String sort;
    List<SpeedCamera> results;

    (略)

}
public class Result {
    Results result;

    (略)
}

看起來似乎好多了,只要new Result(),整串肉粽就出來了。

等等!可是我這支APP還要用到其他開放平台的內容,限定了最內層class為SpeedCamera後,那我要取得其他open data時,豈不是每次取資料都要設計三個class?外面兩層的屬性看起來是相同的,能不能重覆使用呢?

泛型

在設計階段不確定會傳進什麼型態的參數,所以我們用T替代 (不要問我為什麼是T呀! )

public class Results<T> {
    int offset;
    int limit;
    int count;
    String sort;
    List<T> results;

    (略)

}
public class Result<T> {
    Results<T> result;
   
    (略)
}

這樣一來,就不怕了,只要在新增物件時 new Result<SpeedCamera>() 把最內層的物件告訴Result和Results就行了。

取得資料內容 — 使用 Fast Android Networking 特有方法,將回傳資料轉成JavaBean

完整取得open data範例如下圖,從範例可知,不同的資料內容用不同 RID

mag_speed_camera_10

在此不用上圖範例中的方法,改用addQueryParameter方式,結果是相同的,就是改個寫法試試而己 XD

        AndroidNetworking.get("http://data.taipei/opendata/datalist/apiAccess")
                .addQueryParameter("scope", "resourceAquire")
                .addQueryParameter("rid", "5012e8ba-5ace-4821-8482-ee07c147fd0a")
                .setPriority(Priority.HIGH)
                .build()
                .getAsParsed(new TypeToken<Result<SpeedCamera>>() {
                },new ParsedRequestListener<Result<SpeedCamera>>() {
                    @Override
                    public void onResponse(Result<SpeedCamera> response) {
                        Log.d(TAG, "Spead Result = " + response.getResult().getResults().size());
                    }

                    @Override
                    public void onError(ANError anError) {

                    }
                });

第1-3行:傳送網址及參數,此三行同 http://data.taipei/opendata/datalist/apiAccess?scope=resourceAquire&rid=5012e8ba-5ace-4821-8482-ee07c147fd0a ,增加可讀性,日後欲取得其他open data只要替換rid即可。

第4行:優先權

第5行:包子Builder設計模式

第6-7行:請AndroidNetworking取得回傳內容時,順便幫我轉成Result物件,因為使用了泛型,所以要傳進SpeedCamera類別,告訴Results內的List清單內要放哪一種型態的物件。

第10行:response回傳型態為 Result<SpeedCamera>,response.getResult().getResults() 就可以取得最內層List<SpeedCameras>()。

不需要再自己寫程式將JSON轉成JavaBean,是不是超方便的。而且,使用上也非常的直覺,Fast Android Networking 真是好物呀!

清單顯示 – ListView

取回的ArrayList要顯示在ListView上使用ArrayAdapter,在此使用內建2欄顯示的Layout — android.R.layout.simple_list_item_2。要怎麼將幾個欄位串在一起顯示呢?

繼承ArrayAdapter類別,複寫getView方法,Adapter在Android中非常重要,在此不加詳述,若對Adapter有疑慮,請參考Hank老師所寫系列文,內有詳細的說明。

    class MyAdapter extends  ArrayAdapter {
        Context context;
        List<SpeedCamera> list;

        public MyAdapter(Context context, List objects) {
            super(context, android.R.layout.simple_list_item_2, objects);
            this.context = context;
            this.list = objects;
        }

        @NonNull
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (list.size() < 1)
                return null;
            View view = convertView;
            if (view == null){
                view = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_2, parent, false);
            }
            SpeedCamera p = list.get(position);
            if (p!=null) {
                TextView text1 = (TextView) view.findViewById(android.R.id.text1);
                TextView text2 = (TextView) view.findViewById(android.R.id.text2);
                text1.setText(p.getArea() + "區  " + p.getRoad() + p.getLocation() + " " + p.getDirection());
                text2.setText(" 限速" + p.getSpeed_limit() + " " + p.getFunctions() + "照相");

            }
            return view;
        }
    }

將取回的open data List<SpeedCamera>內容 assign 給 speedCameras

//類別變數
List<SpeedCamera> speedCameras = new ArrayList<>();

// AndroidNetWorking 取回資料處
speedCameras = response.getResult().getResults();

別忘了要先設定 ListView和Adapter間的關連。

        ListView paList = (ListView) findViewById(R.id.pa_list);
        MyAdapter adapter = new MyAdapter(this, speedCameras);
        paList.setAdapter(adapter);

執行結果:

mag_speed_camera_01

開放平台資料的關鍵字查詢

查詢方式非常簡單,只要加個參數q就可以了,而關鍵字所過瀘的資料是哪個欄位,請參考API文件。此例,使用q關鍵字過瀘的是area。

        String keyword = ed_key.getText().toString();
        AndroidNetworking.get("http://data.taipei/opendata/datalist/apiAccess")
                .addQueryParameter("scope", "resourceAquire")
                .addQueryParameter("rid", "5012e8ba-5ace-4821-8482-ee07c147fd0a")
                .addQueryParameter("q", keyword)
                .setPriority(Priority.HIGH)
                .build()
                .getAsParsed(new TypeToken<Result<SpeedCamera>>() {
                },new ParsedRequestListener<Result<SpeedCamera>>() {
                    @Override
                    public void onResponse(Result<SpeedCamera> response) {
                        speedCameras.clear();
                        speedCameras.addAll(response.getResult().getResults());
                        adapter.notifyDataSetChanged();
                    }

                    @Override
                    public void onError(ANError anError) {

                    }
                });

第5行:過濾關鍵字,若為空,則查詢全部。

第12行:清空List內容

第13行:使用addAll方法將回傳內容加進原來的List speedCameras內。請注意:若此處直接將List assign給speedCameras,將被視為另一個List。

第14行:通知adapter內容已變,畫面需重新顯示。

執行結果

mag_speed_camera_03  mag_speed_camera_02

版權聲明

本文章版權為郭香宜所有,授權範圍僅限綠豆湯網站使用,除Facebook之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。

相關文章:

This Post Has 5 Comments

  1. Avatar

    您好~
    我在enable proguard的狀況下用Fast Android Networking將資料轉成JavaBean,但返回物件裡的變數都是null..
    disable proguard的話就都正常。試了一些網路上找的proguard config都沒用。
    不知到您有沒有遇到這樣的情形?感謝!

      1. Avatar

        新年快樂,謝謝你提供的資訊呦~

  2. Avatar

    我照著程式碼打,但執行後listview卻是空的

    1. Avatar

      資訊不足無從判斷ListView空的原因,請先確定opendata回傳資料有無內容

發佈留言

×
×

Cart