show code block

2019年1月15日 星期二

Android台北景點 專案(一) - retrofit

前言/雜感:

記得以前談過
徐曉冬打爆中國武術太極和永春高手的雜感

最近新聞又出來了,很有感觸啊。
看那徐曉冬根本戲謔,再看看中國武術大師的肚子

各種強烈的對比

中國武術在完全無法充分交流的情況下,真的變成練身體健康的舞蹈了

不甚唏噓

回歸正題

用一個專案講 retrofit 和 observer 觀察者模式

我們拿台北市的公開json資料來串接json

可先行參考台北市政府的openData

今日實作




重點程式碼:




import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;

public interface CallAttractionData {
    @GET("opendata/datalist/apiAccess?scope=resourceAquire&rid=36847f3f-deff-4183-a5bb-800737591de5")
    Call<AttractionsModel> getCall();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://data.taipei/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    CallAttractionData apiService = retrofit.create(CallAttractionData.class);

}

Call<Model> getCall();
Model為你要解析的json格式



實作:


目前會動到的部分


 1、添加權限
 2、import retrofit第三方
 3、使用retrofit
 4、讓json資料呈現在畫面上

添加權限

在manifest 加入網路權限
<uses-permission android:name="android.permission.INTERNET" />


import retrofit第三方 先在APP層的build.gradle加入retrofit


    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.okhttp3:okhttp:3.12.0'

在看圖說故事
添加GsonFormat這邊是使用gsonformat外掛讓你不用再自己寫model。
 再轉到台北市政府openData的資料 打開下方的網址,會看到長這樣
jon資料
此時我們複製results內的item,只要複製前兩個
可以讓剛剛你在android studio內安裝的gsonFormat判斷是一個陣列就行了

使用retrofit 

創建一個AttractionsModel來承接你的json資料
在你的AttractionsModel內按下熱鍵 Alt+insert (mac:command + N)
會跳出懶人框

懶人框

如果剛剛的gsonFormat有安裝成功,會出現上圖的GsonFormat
進入後複製剛剛台北市政府的json資料丟入,
就會幫妳生成完整的資料。
你會發現資料很多都是你用不到的垃圾,或是你看不懂的欄位。
這時候你可以自己過濾篩選,或是改成你想要的欄位名稱
GsonFormat工具
這邊比較需要注意的是,json提供的資料龐大,可以在results內的item數量很多,可以只取兩個就好。
如下方
{
result: {
limit: 1000,
offset: 0,
count: 319,
sort: "",
results: [
{
info: "新北投站下車,沿中山路直走即可到達公車:216、218、223、230、266、602、小6、小7、小9、、小22、小25、小26至新北投站下車",
stitle: "新北投溫泉區",
xpostDate: "2016/07/07",
longitude: "121.508447",
REF_WP: "10",
avBegin: "2010/02/14",
langinfo: "10",
MRT: "新北投",
SERIAL_NO: "2011051800000061",
RowNumber: "1",
CAT1: "景點",
CAT2: "養生溫泉",
MEMO_TIME: "各業者不同,依據現場公告",
POI: "Y",
file: "http://www.travel.taipei/d_upload_ttn/sceneadmin/pic/11000848.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/pic/11002891.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D315/E70/F65/1e0951fb-069f-4b13-b5ca-2d09df1d3d90.JPGhttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D260/E538/F274/e7d482ba-e3c0-40c3-87ef-3f2a1c93edfa.JPGhttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D919/E767/F581/9ddde70e-55c2-4cf0-bd3d-7a8450582e55.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C1/D28/E891/F188/77a58890-7711-4ca2-aebe-4aa379726575.JPG",
idpt: "臺北旅遊網",
latitude: "25.137077",
xbody: "北投溫泉從日據時代便有盛名,深受喜愛泡湯的日人自然不會錯過,瀧乃湯、星乃湯、鐵乃湯就是日本人依照溫泉的特性與療效給予的名稱,據說對皮膚病、神經過敏、氣喘、風濕等具有很好的療效,也因此成為了北部最著名的泡湯景點之一。新北投溫泉的泉源為大磺嘴溫泉,泉質屬硫酸鹽泉,PH值約為3~4之間,水質呈黃白色半透明,泉水溫度約為50-90℃,帶有些許的硫磺味 。目前北投的溫泉旅館、飯店、會館大部分集中於中山路、光明路沿線以及北投公園地熱谷附近,總計約有44家,每一家都各有其特色,多樣的溫泉水療以及遊憩設施,提供遊客泡湯養生,而鄰近的景點也是非常值得造訪,例如被列為三級古蹟的三寶吟松閣、星乃湯、瀧乃湯以及北投第一家溫泉旅館「天狗庵」,都有著深遠的歷史背景,而北投公園、北投溫泉博物館、北投文物館、地熱谷等,更是遊客必遊的景點,來到北投除了可以讓溫泉洗滌身心疲憊,也可以順便了解到北投溫泉豐富的人文歷史。",
_id: 1,
avEnd: "2016/07/07",
address: "臺北市 北投區中山路、光明路沿線"
},
{
info: "捷運站名:雙連站,轉乘紅33(固定班次)於大稻埕碼頭站下車。公車:9、206、274、641、669、704至大稻埕碼頭站及255、518、539至民生西路口站,再沿民生西路底方向步行約10分鐘抵達。 開車:沿著環河北路依大稻埕碼頭入口指引便可抵達。",
stitle: "大稻埕碼頭",
xpostDate: "2015/12/09",
longitude: "121.508274",
REF_WP: "10",
avBegin: "2008/08/02",
langinfo: "10",
MRT: "雙連",
SERIAL_NO: "2011051800000007",
RowNumber: "2",
CAT1: "景點",
CAT2: "藍色公路",
MEMO_TIME: "平常日以團體預約包船為主,例假日行駛固定航次,請洽詢各船公司。 強烈季風、漲退潮水位差影響航行及靠泊安全,當日實際航班得由現場公告或網站預告調整。",
POI: "Y",
file: "http://www.travel.taipei/d_upload_ttn/sceneadmin/pic/11000340.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D906/E6/F186/809f30db-7079-421f-a625-7baa8ec21874.JPGhttp://www.travel.taipei/d_upload_ttn/sceneadmin/pic/11000341.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D878/E420/F173/04765739-d40f-4d13-b271-8d5f9e5f44bd.JPGhttp://www.travel.taipei/d_upload_ttn/sceneadmin/pic/11000342.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D20/E983/F199/866b5059-8fd7-4719-964c-51d2f78675d5.jpghttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C0/D546/E538/F353/ed2464d1-bc28-4790-96cd-5216db2c14f5.JPGhttp://www.travel.taipei/d_upload_ttn/sceneadmin/image/A0/B0/C1/D814/E111/F733/aed9d34d-890c-49fd-83ca-f76f38e4b94b.jpghttp://www.travel.taipei/streams/sceneadmin/video/100C1.mp3",
idpt: "臺北旅遊網",
latitude: "25.056847",
xbody: "大稻埕原是平埔族的居住地,因萬華(艋舺)同安人發生激烈的械鬥,造成族人移至大稻埕定居,開始大稻埕淡水河旁商店和房屋的興建,淡水港開放後,大稻埕在劉銘傳的治理下成為臺北城最繁華的物資集散中心,當中以茶葉、布料為主要貿易交易,當時的延平北路及貴德街一帶便是商業活動的重心,也讓大稻埕早年的歷史多采多姿、令人回味。 ",
_id: 2,
avEnd: "2015/12/09",
address: "臺北市 大同區環河北路一段"
}
]}}

自己在後方加入 ] 和 } 收尾 model就完成了 接下來我們來使用Retrofit吧!

 
public interface CallAttractionData {
    @GET("opendata/datalist/apiAccess?scope=resourceAquire&rid=36847f3f-deff-4183-a5bb-800737591de5")
    Call<AttractionsModel> getCall();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://data.taipei/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    CallAttractionData apiService = retrofit.create(CallAttractionData.class);

}
CallAttractionData
這邊講解一下
這邊是你在MainActivity.java的呼叫使用方式 retrofit.create(XXXX.class).getCall().enqueue(new CallBack{....})
XXXX.class 為你寫@GET的地方
以這篇文章為例 就會是CallAttractionData

 再來看看URL 網址:
https://data.taipei/opendata/datalist/apiAccess?scope=resourceAquire&rid=36847f3f-deff-4183-a5bb-800737591de5
通常一家公司的url前方都是固定的,像是台北市的url,開頭都會是
https://data.taipei/
所以GET內就是裝固定url後方的變數
此時拿台北市的例子來說就是
看起來就是上面CallAttractionData的樣子了

 如何在MainActivity.java使用retrofit 如同上述說的 retrofit.create(XXXX.class).getCall().enqueue(new CallBack{....})


設一個Log來看看吧
確定有獲得參數 retrofit有幫你獲取了json


再來看看整個MainActivity.java
 
public class MainActivity extends AppCompatActivity {

    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        text = findViewById(R.id.showtext);

        CallAttractionData.apiService.getCall().enqueue(new Callback<AttractionsModel>() {
            @Override
            public void onResponse(Call<AttractionsModel> call, @NonNull Response<AttractionsModel> response) {
                if (!response.isSuccessful())
                    return;

                AttractionsModel model = response.body();

                Log.d("MainActivity", "main = " + model.getResult().getCount());
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < model.getResult().getCount(); i++) {
                    sb.append("景點: - " + model.getResult().getResults().get(i).getAttractionTitle() + "\n");
                    sb.append("介紹: - " + model.getResult().getResults().get(i).getTourist() + "\n");
                    sb.append("-------------------------- \n");
                    text.setText(sb);
                }
            }

            @Override
            public void onFailure(Call<AttractionsModel> call, Throwable t) {

            }
        });

    }
}
xml畫面單純用scrollview包住
activity_main.xml
activity_main.xml
 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/showtext"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />


    </ScrollView>

</RelativeLayout>


run一下吧!
畫面會長得跟一開始我貼的gif一樣



總結:

之後再來講Observer觀察者模式

github code :
https://github.com/nikeru8/TaipeiTourist

協程(coroutine) - 協程為什麼要學它?

 Coroutine 協程 再強調一次 協程就是由kotlin官方所提供的線程api //Thread Thread { }.start() //Executor val execu...