AndroidStudio新手開發:天氣app(百度地圖api+和風天氣api+城市查詢+折線展示)

1、內容簡介

       學校b測,碰巧選到了app開發,之前只有一些網站開發經歷,第一次接觸安卓端,從配環境查攻略,到運行他人demo一步步理解與修改,到最後實現定位天氣查詢、折線圖展示等功能,手機上安裝了自己的app後頓時成就感十足,寫下此文記錄此次開發的要點與攻略,同時爲其他新手開發者排一些bug。界面展示如下:
在這裏插入圖片描述在這裏插入圖片描述 在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述

2、環境配置

首先下載安裝Android Studio並進行虛擬機配置,推薦參考鏈接如下:AS配置

3、導入他人demo

導入他人AS demo時往往會出現許多令人頭疼的bug,在這裏提醒大家一定要替換demo本身的一些配置信息後再用AS open an exisiting android studio project,推薦參考鏈接如下:導入他人demo
另外,如果出現長時間sync不成功很可能是因爲從google下載被牆的原因,在源目錄下的build.gradle文件中進行阿里雲鏡像替換

 //       google()
    //    jcenter()
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }

4、AS項目分析

成功運行他人demo之後重要的事情就是分析項目的架構了,相關資料鏈接:項目目錄分析

在這裏我再次着重強調幾個文件:
1.app中的build.gradle中引入了所有的包,如果報錯缺少所需的包或版本不匹配時需要在文件中的dependencies中進行修改

  dependencies {

        implementation fileTree(dir: 'libs', include: ['*.jar'])

//    implementation 'com.android.support:appcompat-v7:29.+'

        implementation 'com.android.support.constraint:constraint-layout:1.1.3'

        testImplementation 'junit:junit:4.12'

        androidTestImplementation 'com.android.support.test:runner:1.0.2'

        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

        implementation 'com.github.PhilJay:MPAndroidChart:v3.0.1'

//    implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

    }

2.運行項目後生成的app-debug.apk位於app.build.outputs.apk文件夾中。

3.所有的源碼都位於app.main.src中,其中工作框架在java.activity中,頁面佈局在res.layout中。這兩個文件夾爲項目中的重中之重,涵蓋了項目的運行流程以及頁面設計,需要仔細理解。
在這裏插入圖片描述在這裏插入圖片描述
4.配置文件AndroidManife.XML中定義了app的名稱和圖標可進行修改。

<application
    android:name=".MainApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="陰晴"
    android:roundIcon="@mipmap/ic_launcher"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

5、天氣項目流程

終於進入到了正題,本次開發的天氣app名爲“陰晴”,打開app,系統首先顯示默認城市(西安)的天氣信息,之後用戶可以選擇進行定位查詢或自由選擇城市:定位查詢通過申請定位權限,調用百度地圖api定位當前城市地圖信息,並通過緩存定位城市至sharepreference,調用和風天氣api生成當前城市的天氣信息;自由選擇城市通過數據表查詢,調用和風天氣api生成查詢城市對應天氣信息並顯示。之後將此次天氣信息保存至緩存sharepreference,下次打開app時顯示緩存中城市的天氣信息。頁面顯示上通過保存天氣查詢數據至sharedpreference,通過建立圖表可選擇折線圖顯示近七天最高最低的溫度變化情況。流程圖設計如下:
在這裏插入圖片描述

6、數據探尋

1.天氣信息獲取
此次天氣及空氣質量等信息獲取我們選用和風天氣提供的免費api,和風天氣每天有提供4000次免費的基礎天氣查詢,用來做開發測試足夠使用,且其提供的空氣質量等信息較爲全面,而且和風天氣api接口返回的JSON數據類型也比較簡單,對於我們Android的初學者做項目較爲方便,申請流程官方網站有詳細教程,申請成功後如下,key爲api調用的關鍵信息:
在這裏插入圖片描述
3.2定位信息獲取的api
我們選擇使用了百度提供的免費api接口http://lbsyun.baidu.com/apiconsole/key,因爲Android原生定位API在國產手機中一般被閹割了,或者國內網絡限制的原因,使用Android原生定位API一般是很難獲取到定位信息的,跟手機廠商和網絡環境都有關係。所以這邊爲了避免這種情況的不確定因素,我們選擇了使用百度提供的免費地位接口,其api申請如下:
在這裏插入圖片描述
3.3城市信息獲取
下載省份、城市信息,建立城市列表數據庫,對每個省份、城市進行編號,採用mysql進行查詢讀取,數據列表如下:
在這裏插入圖片描述在這裏插入圖片描述

7、模塊架構

1.城市信息獲取模塊(cityselcetor activity)
通過數據庫查詢,依次查詢選擇的省份、城市,通過回調函數onActivityResult獲取菜單選擇城市的回調,將該城市名保存至sharedpreference後傳遞至天氣查詢模塊獲得天氣信息。

/**
 * 查詢全國所有的省,從數據庫查詢
 */
private void queryProvinces() {
    ProvinceList.addAll(WeatherDB.loadProvinces(DBManager.getInstance().getDatabase()));
    data.clear();
    for (Province province : ProvinceList) {
        data.add(province.mProName);
    }
    currentLevel = LEVEL_PROVINCE;
    if(mAdapter==null)
    {
        initAdapter();
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
    else mAdapter.notifyDataSetChanged();
}
/**
 * 查詢選中省份的所有城市,從數據庫查詢
 */
private void queryCities() {
    CityList.addAll(WeatherDB.loadCities(DBManager.getInstance().getDatabase(), selectedProvince.mProSort));
    data.clear();
    for (City city:CityList)
    {
        data.add(city.mCityName);
    }
    currentLevel = LEVEL_CITY;
    mAdapter.notifyDataSetChanged();
    mRecyclerView.smoothScrollToPosition(0);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CitySelectorActivity.SelectorSuccessFromMenu) {
        //來自於菜單選擇城市的回調
        if (resultCode == RESULT_OK) {
            if (data == null) {
                return;
            }
            Bundle bundle = data.getExtras();
            //城市名稱
            String cityName = bundle.getString("CityName");
            //獲取到城市名稱後可自行使用...
            UpdataViewForMain(cityName);
        }
    }
}
private void UpdataViewForMain(String cityName)
{
    //關閉側滑欄
    mDrawerLayout.closeDrawer(mNavView);
    //獲取OldCity 以便新城市不可加載時恢復數據
    String OldCity = MySharedpreference.preferences.getString("City",null);
    //保存城市
    editor.putString("City",cityName);
    editor.commit();
    //更新數據
    mMainFragment.UpDataUi(OldCity);
    //RecyclerView回到頂部
    mMainFragment.mRecyclerView.smoothScrollToPosition(0);
}

2.定位信息獲取模塊(Map activity)
通過調用百度地圖api,讀取當前定位並顯示地圖,通過定義方法navigate to移動至當前位置,並通過共享緩存區域sharedpreference將定位得到的城市進行保存,傳遞至天氣查詢模塊獲得當前城市的天氣信息。

private void navigateTo(BDLocation location) {
      if (isFirstLocate) {
          LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
          MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
          baiduMap.animateMapStatus(update);
          update = MapStatusUpdateFactory.zoomTo(16f);
          baiduMap.animateMapStatus(update);
          isFirstLocate = false;
      }
      MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
      locationBuilder.latitude(location.getLatitude());
      locationBuilder.longitude(location.getLongitude());
      MyLocationData locationData = locationBuilder.build();
      baiduMap.setMyLocationData(locationData);
  }

  public class MyLocationListener implements BDLocationListener {
      public String city;

      private SharedPreferences.Editor  editor;       //共享參數

      public void onReceiveLocation(final BDLocation Location) {
          runOnUiThread(new Runnable() {

              @Override
              public void run() {


                  if (Location.getLocType() == BDLocation.TypeGpsLocation || Location.getLocType() == BDLocation.TypeNetWorkLocation) {
                      navigateTo(Location);
                      editor = MySharedpreference.getInstance(MapActivity.this);
                      city = Location.getCity();
                      editor.putString("City", city);
                      editor.commit();
                  }

                  button1 =(Button)findViewById(R.id.button);
                  button1.setOnClickListener(new View.OnClickListener(){
                      @Override
                      public void  onClick(View v){
                          editor = MySharedpreference.getInstance(MapActivity.this);
                          city = Location.getCity();
                          editor.putString("City", city);
                          editor.commit();
                          Intent intent=new Intent(MapActivity.this,MainActivity.class);
                          startActivity(intent);
                      }
                  });
                      }
                  });
              }
          }
      }

3.天氣信息獲取模塊(MultipleItemQuickAdapter)
爲了用GSON解析,首先將和風天氣api接口中的JSON數據全部都寫成了實體類(NewWeatherBean),利用模擬請求工具
Rest Client測試api接口工具是否正常,最後用getHeWeather6獲取我們所需的數據。

case MultipleItem.DetailsAirInfo://空氣細節信息
    try {
        helper.setText(R.id.air_aiq_tv, "空氣指數:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getAqi());
        helper.setText(R.id.air_qlty_tv, "空氣質量:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getQlty());
        helper.setText(R.id.air_main_pollutant_tv, "主要污染物:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getMain());
        helper.setText(R.id.air_pm25_tv, "PM2.5指數:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getPm25());
        helper.setText(R.id.air_no2_tv, "二氧化氮指數:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getNo2());
        helper.setText(R.id.air_so2_tv, "二氧化硫指數:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getSo2());
        helper.setText(R.id.air_co_tv, "一氧化碳指數:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getCo());
        helper.setText(R.id.air_o3_tv, "臭氧指數:  " + item.mAirBean
                .getHeWeather6().get(0).getAir_now_city().getO3());
    } catch (Exception e) {
    }
    break;

4.圖表信息繪製模塊(Main2activity)
通過sharedpreference將天氣查詢最近七天的最高溫度和最低溫度獲取的數據進行儲存,在圖表繪製模塊中調用這些數據繪製圖表。

     for (int i = 1; i < 8; i++) {
                            tmp_max[i-1]=item.mNewWeatherBean.getHeWeather6()
                                    .get(0).getDaily_forecast().get(i-1).getTmp_max();
                            tmp_min[i-1]=item.mNewWeatherBean.getHeWeather6()
                                    .get(0).getDaily_forecast().get(i-1).getTmp_min();
                        }
                        SharedPreferences sp = mContext.getSharedPreferences("data", MODE_PRIVATE);
//獲取編輯器
                        SharedPreferences.Editor editor = sp.edit();
//存入String型數據
                        editor.putString("tmp_max0", tmp_max[0]);
                        editor.putString("tmp_max1", tmp_max[1]);
                        editor.putString("tmp_max2", tmp_max[2]);
                        editor.putString("tmp_max3", tmp_max[3]);
                        editor.putString("tmp_max4", tmp_max[4]);
                        editor.putString("tmp_max5", tmp_max[5]);
                        editor.putString("tmp_max6", tmp_max[6]);
                        editor.putString("tmp_min0", tmp_min[0]);
                        editor.putString("tmp_min1", tmp_min[1]);
                        editor.putString("tmp_min2", tmp_min[2]);
                        editor.putString("tmp_min3", tmp_min[3]);
                        editor.putString("tmp_min4", tmp_min[4]);
                        editor.putString("tmp_min5", tmp_min[5]);
                        editor.putString("tmp_min6", tmp_min[6]);
private void initData()
{
    editor = MySharedpreference.getInstance(Main2Activity.this);
    ArrayList<WeatherItem> list= new ArrayList<WeatherItem>();
    SharedPreferences sp = getSharedPreferences("data", MODE_PRIVATE);
    list.add(new WeatherItem("1", Float.parseFloat(sp.getString("tmp_max0", ""))));
    list.add(new WeatherItem("2",Float.parseFloat(sp.getString("tmp_max1", ""))));
    list.add(new WeatherItem("3", Float.parseFloat(sp.getString("tmp_max2", ""))));
    list.add(new WeatherItem("4",Float.parseFloat(sp.getString("tmp_max3", ""))));
    list.add(new WeatherItem("5",Float.parseFloat(sp.getString("tmp_max4", ""))));
    list.add(new WeatherItem("6",Float.parseFloat(sp.getString("tmp_max5", ""))));
    list.add(new WeatherItem("7", Float.parseFloat(sp.getString("tmp_max6", ""))));
    chart1.SetTuView(list, "最高溫度:");//單位: 攝氏度
chart1.invalidate();
    ArrayList<WeatherItem> list1= new ArrayList<WeatherItem>();
    list1.add(new WeatherItem("",Float.parseFloat(sp.getString("tmp_min0", ""))));
    list1.add(new WeatherItem("",Float.parseFloat(sp.getString("tmp_min1", ""))));
    list1.add(new WeatherItem("", Float.parseFloat(sp.getString("tmp_min2", ""))));
    list1.add(new WeatherItem("",Float.parseFloat(sp.getString("tmp_min3", ""))));
    list1.add(new WeatherItem("", Float.parseFloat(sp.getString("tmp_min4", ""))));
    list1.add(new WeatherItem("",Float.parseFloat(sp.getString("tmp_min5", ""))));
    list1.add(new WeatherItem("",Float.parseFloat(sp.getString("tmp_min6", ""))));
   chart2.SetTuView(list1, "最低溫度:");
  chart2.invalidate();
}

8、sharedpreference

爲了保存軟件的設置參數,Android平臺爲我們提供了一個SharedPreferences接口,它是一個輕量級的存儲類,特別適合用於保存軟件配置參數。使用SharedPreferences保存數據,其背後是用xml文件存放數據,文件存放在/data/data//shared_prefs目錄下。由於本次項目未採用服務器數據庫保存數據,sharedpreference在各個activiti之間將獲取的城市信息進行傳遞就顯得尤爲重要。除了上述提到選擇查詢某地後將該地通過儲存至sharedpreference傳遞定位信息給天氣查詢模塊、定位時通過sharedpreference傳遞定位信息給天氣查詢模塊、天氣查詢獲得氣溫數據後通過sharedpreference傳遞至圖表繪製模塊之外,每次下拉刷新、打開app時,都會讀取sharedpreference中的城市信息進行更新,保證每次進入app時都能獲取到上次退出前最後查詢的城市天氣,十分方便。讀寫格式如下:

//寫操作
SharedPreferences sp = mContext.getSharedPreferences("data", MODE_PRIVATE);
//獲取編輯器
SharedPreferences.Editor editor = sp.edit(); 
//存入String型數據,此處kindItemBean.getName()爲所需要的傳遞的數據
editor.putString("goodsname", kindItemBean.getName()); 
//提交修改,否則不生效
editor.commit();     

//讀操作
SharedPreferences sp = getSharedPreferences("data", MODE_PRIVATE);
//第二個參數爲缺省值,如果不存在該key,返回缺省值
String goodsname = sp.getString("goodsname", "");

9、常見bug

1.模擬器中定位地不會變一直在北京
解決:一般爲模擬器定位問題,手機端能定位即可,一定要在手機安裝app進行嘗試。
2.閃退後空指針異常
解決:我們經常會遇見打開app直接閃退的情形,在AS下方命令窗選擇run可看到閃退原因,一般爲空指針異常,出現如下代碼提示:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'xxxxxxxxxxxx' on a null object reference

出現這種現象基本均爲流程和視圖不匹配,比如說添加控件卻沒有在layout中進行定義,將控件添加到一個空的地方即爲空指針異常,因此需仔細覈對layout與activity的關聯性即可找出問題。

10、源代碼

此次項目是在網上開源demo上進行修改與添加功能而得,如有侵權請及時告知。由於學校測試還未結束,暫不公開源代碼,若有需要可私信博主,等測試結束後會進行開源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章