第一個(gè)安卓應(yīng)用開發(fā)記錄

一、項(xiàng)目框架的搭建

1、在git上新建一個(gè)coolweather的倉庫
2、新建一個(gè)Coolweather項(xiàng)目,添加到git版本管理
3、在java中新建幾個(gè)包,db:用于存放數(shù)據(jù)庫模型相關(guān)的代碼,gson:用于存放GSon相關(guān)的代碼,service:用于存放服務(wù)相關(guān)的代碼,util包用于存放工具相關(guān)代碼
4、添加項(xiàng)目依賴的第三方,在app/build.gradle
依賴:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    implementation 'org.litepal.android:core:2.0.0'
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.github.bumptech.glide:glide:4.7.1'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

倉庫添加

allprojects {
    repositories {
        mavenCentral()
        google()
        jcenter()
    }
}

說明:
implementation 'org.litepal.android:core:2.0.0'是數(shù)據(jù)庫操作的框架
implementation 'com.squareup.okhttp3:okhttp:3.10.0'網(wǎng)絡(luò)請求框架
implementation 'com.google.code.gson:gson:2.8.5' JSON解析框架
implementation 'com.github.bumptech.glide:glide:4.7.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'為加載圖片的框架

二、數(shù)據(jù)庫表的設(shè)計(jì)

1、數(shù)據(jù)獲取
省份獲取接口:
http://guolin.tech/api/china
獲取的數(shù)據(jù)如下:
[{"id":1,"name":"北京"},{"id":2,"name":"上海"}]
省下面對應(yīng)的市獲取接口
http://guolin.tech/api/china/22,其中22為省份的id,數(shù)據(jù)結(jié)構(gòu)如下:
[{"id":191,"name":"長沙"},{"id":192,"name":"湘潭"},{"id":193,"name":"株洲"},{"id":194,"name":"衡陽"},{"id":195,"name":"郴州"},{"id":196,"name":"常德"},{"id":197,"name":"益陽"},{"id":198,"name":"婁底"},{"id":199,"name":"邵陽"},{"id":200,"name":"岳陽"},{"id":201,"name":"張家界"},{"id":202,"name":"懷化"},{"id":203,"name":"永州"},{"id":204,"name":"吉首"}]
獲取區(qū)對應(yīng)的信息
http://guolin.tech/api/china/22/191,其中191為市的id
[{"id":1422,"name":"長沙","weather_id":"CN101250101"},{"id":1423,"name":"寧鄉(xiāng)","weather_id":"CN101250102"},{"id":1424,"name":"瀏陽","weather_id":"CN101250103"},{"id":1425,"name":"馬坡嶺","weather_id":"CN101250104"},{"id":1426,"name":"望城","weather_id":"CN101250105"}]
最后通過weather_id 獲取天氣信息,使用和風(fēng)天氣的api
https://console.heweather.com/register?role=personal,注冊免費(fèi)

天氣信息的獲取
https://free-api.heweather.com/s6/weather/now?location=CN101250101&key=22c4dc1c0a3245c7a97e7c2543b9781a
數(shù)據(jù)如下:
{"HeWeather6":[{"basic":{"cid":"CN101250101","location":"長沙","parent_city":"長沙","admin_area":"湖南","cnty":"中國","lat":"28.19408989","lon":"112.98227692","tz":"+8.00"},"update":{"loc":"2018-07-06 16:49","utc":"2018-07-06 08:49"},"status":"ok","now":{"cloud":"75","cond_code":"104","cond_txt":"陰","fl":"31","hum":"85","pcpn":"0.0","pres":"1000","tmp":"29","vis":"13","wind_deg":"293","wind_dir":"西北風(fēng)","wind_sc":"3","wind_spd":"19"}}]}

2、數(shù)據(jù)庫表實(shí)現(xiàn)
新建Province類 繼承LitePalSupport

public class Province extends LitePalSupport {
    private int id;
    private String provinceName;
    private int provinceCode;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getProvinceName() {
        return provinceName;
    }

    public void setProvinceName(String provinceName) {
        this.provinceName = provinceName;
    }

    public int getProvinceCode() {
        return provinceCode;
    }

    public void setProvinceCode(int provinceCode) {
        this.provinceCode = provinceCode;
    }
}

City類

public class City extends LitePalSupport {
    private  int id;
    private String cityName;
    private int cityCode;
    private  int provinceId;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }

    public int getCityCode() {
        return cityCode;
    }

    public void setCityCode(int cityCode) {
        this.cityCode = cityCode;
    }

    public int getProvinceId() {
        return provinceId;
    }

    public void setProvinceId(int provinceId) {
        this.provinceId = provinceId;
    }
}

Country類

public class Country extends LitePalSupport {
    private int id;
    private String countryName;
    private String weatherId;
    private int cityId;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCountryName() {
        return countryName;
    }

    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }

    public String getWeatherId() {
        return weatherId;
    }

    public void setWeatherId(String weatherId) {
        this.weatherId = weatherId;
    }

    public int getCityId() {
        return cityId;
    }

    public void setCityId(int cityId) {
        this.cityId = cityId;
    }
}

在app/src/main 目錄在新建assets 文件,在改目錄下新建一個(gè)litepal.xml文件,編輯內(nèi)容如下:

<?xml version="1.0" encoding="utf-8"?>

<litepal>
    <!--
        Define the database name of your application.
        By default each database name should be end with .db.
        If you didn't name your database end with .db,
        LitePal would plus the suffix automatically for you.
        For example:
        <dbname value="demo" />
    -->
    <dbname value="cool_weather" />

    <!--
        Define the version of your database. Each time you want
        to upgrade your database, the version tag would helps.
        Modify the models you defined in the mapping tag, and just
        make the version value plus one, the upgrade of database
        will be processed automatically without concern.
            For example:
        <version value="1" />
    -->
    <version value="1" />


    <list>
        <mapping class="com.example.yangjie.coolweather.db.Province" />
        <mapping class="com.example.yangjie.coolweather.db.City" />
        <mapping class="com.example.yangjie.coolweather.db.Country" />
    </list>

    <!--
        Define where the .db file should be. "internal" means the .db file
        will be stored in the database folder of internal storage which no
        one can access. "external" means the .db file will be stored in the
        path to the directory on the primary external storage device where
        the application can place persistent files it owns which everyone
        can access. "internal" will act as default.
        For example:
        <storage value="external" />
    -->

</litepal>

最后需要在配置一下LitePalApplication,修改如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yangjie.coolweather">

    <application
        android:name="org.litepal.LitePalApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3、自定義Fragment

首先定義一個(gè)線性布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#fff"
    >
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        >

        <TextView
            android:id="@+id/title_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textColor="#fff"
            android:textSize="20sp"
            android:text="標(biāo)題"
            />

        <Button
            android:id="@+id/btn_back"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:background="@drawable/btn_back"

            />
    </RelativeLayout>

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

這里自定義了導(dǎo)航條,所以需要修改style.xml文件,修改如下:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

新建一個(gè)ChooseAreaFragment繼承Fragment,代碼如下:

public class ChooseAreaFragment extends Fragment {
    public static final  int LEVEL_PROVINCE = 0;
    public static final  int LEVEL_CITY = 1;
    public static final  int LEVEL_COUNTRY = 2;
    private static final  String TYPE_PROVINCE = "province";
    private static final  String TYPE_City = "city";
    private static final  String TYPE_Country = "country";


    private ProgressDialog progressDialog;
    private TextView titleText;
    private Button backButton;
    private ListView listView;
    private ArrayAdapter<String> adapter;
    private List<String> dataList = new ArrayList<>();

    private List<Province> provinceList;
    private List<City> cityList;
    private List<Country> countryList;

    private Province selectedProvince;
    private City selectedCity;
    private int currentLevel;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.choose_area,container,false);
        titleText = (TextView)view.findViewById(R.id.title_text);
        backButton = (Button)view.findViewById(R.id.btn_back);
        listView = (ListView)view.findViewById(R.id.listView);

        adapter = new ArrayAdapter<>(getContext(),android.R.layout.simple_list_item_1,dataList);
        listView.setAdapter(adapter);
        return view;

    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                if (currentLevel == LEVEL_PROVINCE){
                    selectedProvince = provinceList.get(i);
                    queryCities();
                }else if (currentLevel == LEVEL_CITY){
                    selectedCity = cityList.get(i);
                    queryCounties();
                }
            }
        });

        backButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (currentLevel == LEVEL_COUNTRY){
                    queryCities();
                }else if (currentLevel == LEVEL_CITY){
                    queryProinces();
                }
            }


        });

        queryProinces();
    }

    private void queryCounties() {

        titleText.setText(selectedCity.getCityName());
        backButton.setVisibility(View.VISIBLE);
        countryList = LitePal.where("cityid = ?",String.valueOf(selectedCity.getId())).find(Country.class);
        if (countryList.size()>0){
            dataList.clear();
            for (Country country:countryList){
                dataList.add(country.getCountryName());
            }
            adapter.notifyDataSetChanged();;
            listView.setSelection(0);
            currentLevel = LEVEL_COUNTRY;
        }else {

            int provinceCode = selectedProvince.getProvinceCode();
            int cityCode = selectedCity.getCityCode();
            String address = "http://guolin.tech/api/china/" + provinceCode + "/" + cityCode;
            queryFromServer(address,TYPE_Country);
        }
    }

    private void queryCities() {
        titleText.setText(selectedProvince.getProvinceName());
        backButton.setVisibility(View.VISIBLE);
        cityList = LitePal.where("provinceid = ?",String.valueOf(selectedProvince.getId())).find(City.class);
        if (cityList.size()>0){
            dataList.clear();
            for (City city:cityList){
                dataList.add(city.getCityName());
            }
            adapter.notifyDataSetChanged();;
            listView.setSelection(0);
            currentLevel = LEVEL_CITY;
        }else {

            int provinceCode = selectedProvince.getProvinceCode();
            String address = "http://guolin.tech/api/china/" + provinceCode;
            queryFromServer(address,TYPE_City);
        }
    }
    private void queryProinces() {
        titleText.setText("中國");
        backButton.setVisibility(View.GONE);
        provinceList = LitePal.findAll(Province.class);
        if (provinceList.size()>0){
            dataList.clear();
            for (Province province:provinceList){
                dataList.add(province.getProvinceName());
            }
            adapter.notifyDataSetChanged();
            listView.setSelection(0);
            currentLevel = LEVEL_PROVINCE;
        }else {
            String address = "http://guolin.tech/api/china";
            queryFromServer(address,TYPE_PROVINCE);
        }
    }

    private void queryFromServer(String address,final String type){
        showProgressDialog();

        HttpUtil.sendHttpRequest(address, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
               getActivity().runOnUiThread(new Runnable() {
                   @Override
                   public void run() {
                       closeProgressDialog();
                       Toast.makeText(getContext(),"加載失敗",Toast.LENGTH_SHORT).show();;
                   }
               });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //在這里解析數(shù)據(jù)
                String responseText = response.body().string();

                Log.d(TAG, "onResponse: " + responseText);
                boolean result = false;
                if (type.equals(TYPE_PROVINCE)){
                    result = Utility.handleProvinceResponse(responseText);
                }else if(type.equals(TYPE_City)){
                    result = Utility.handleCityResponse(responseText,selectedProvince.getId());
                }else if(type.equals(TYPE_Country)){
                    result = Utility.handleCountryResponse(responseText,selectedCity.getId());
                }

                if (result){
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            closeProgressDialog();
                            if (type.equals(TYPE_PROVINCE)){
                                queryProinces();
                            }else if(type.equals(TYPE_City)){
                                queryCities();
                            }else if(type.equals(TYPE_Country)){
                                queryCounties();
                            }
                        }
                    });
                }
            }
        });
    }


    private void showProgressDialog(){
        if (progressDialog == null){
            progressDialog = new ProgressDialog(getActivity());
            progressDialog.setMessage("正在加載");
            progressDialog.setCanceledOnTouchOutside(false);
        }
        progressDialog.show();
    }

    private void closeProgressDialog(){
        if (progressDialog!=null){
            progressDialog.dismiss();
        }
    }
}

在activity_main.xml文件中添加Fragment布局如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">

    <fragment
        android:id="@+id/choose_area_fragment"
        android:name="com.example.yangjie.coolweather.ChooseAreaFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</FrameLayout>

三、展示天氣數(shù)據(jù)

效果如下:


780FA754-69A3-440F-AFC3-297954E7F328.png

將上圖的界面拆分成三個(gè)布局:1、導(dǎo)航部分(展示城市和更新時(shí)間)
2、現(xiàn)在天氣信息 3、未來三天信息展示
1、導(dǎo)航部分,新建一個(gè)title.xml,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    android:id="@+id/relativeLayout">


    <Button
        android:id="@+id/nav_button"
        android:layout_width="32dp"
        android:layout_height="30dp"
        android:background="@drawable/home"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/title_city"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="180dp"
        android:layout_marginStart="180dp"
        android:layout_marginTop="8dp"
        android:text="北京"
        android:textColor="#fff"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <TextView
        android:id="@+id/title_update_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="10dp"
        android:textColor="#fff"
        android:textSize="16sp"
        android:text="2018-7-10"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
    
</android.support.constraint.ConstraintLayout>

2、天氣信息部分

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_margin="15dp"
    >

    <TextView
        android:id="@+id/tmp_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:textColor="#fff"
        android:textSize="60sp"
        />

    <TextView
        android:id="@+id/weather_info_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:textSize="20sp"
        android:textColor="#fff"
        />

</LinearLayout>

3、未來天氣布局forcast.xml代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_margin="15dp"
    android:background="#8000"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginTop="15dp"
        android:textColor="#fff"
        android:text="預(yù)報(bào)"
        android:textSize="20sp"
        />

    <LinearLayout
        android:id="@+id/forecast_layout"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
    </LinearLayout>


</LinearLayout>

forecast_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="15dp"
    >

    <TextView
        android:id="@+id/info_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:textColor="#fff"
        />

    <TextView
        android:id="@+id/tem_min"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:textColor="#fff"
        />


    <TextView
        android:id="@+id/tmp_max"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:textColor="#fff"
        />

    <TextView
        android:id="@+id/date_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="2"
        android:textColor="#fff"
        />

</LinearLayout>

四、生成APK文件

https://blog.csdn.net/wyg1230/article/details/77529465/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,030評(píng)論 25 709
  • 第十三章主要是編寫了一個(gè)功能完善的天氣預(yù)報(bào)程序 一、功能需求分析 ??在開始編碼之前,我們先對程序進(jìn)行需求分析,首...
    radish520like閱讀 1,165評(píng)論 0 0
  • 【1分鐘AI】 1、全國首創(chuàng)無人機(jī)流動(dòng)診所正式投用 2、搜狗推出「唇語識(shí)別」技術(shù) 垂直場景下90%正確率 3、工信...
    Yetta000閱讀 308評(píng)論 0 0
  • 聚集智慧, 聚集熱誠, 聚集決心, 聚集資源, 只為結(jié)果的呈現(xiàn), 早安[太陽][太陽][太陽]
    奔波男生閱讀 104評(píng)論 0 0
  • 愛一場,恨一世,可惜未白頭。 北地山河粗獷,民風(fēng)彪悍,就是女子也能上馬開弓,追著爺們打! 而有一地確以盛產(chǎn)美人聞名...
    冰冰泉閱讀 618評(píng)論 18 44

友情鏈接更多精彩內(nèi)容