2017-05-15 16:35:13 aoyihenshuang 阅读数 314
  • Android 开发利器----- Android调试技术

    课程会为大家讲解如何实现在Android真机上调试自己的项目, 同样该教程会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项知识。讲解使用调试功能快速在开发中排错,查找问题。

    125 人正在学习 去看看 Frank Lee

第一行代码Android第二章读书笔记

  1. Activity
    1.1 手动创建活动
    1.2 Toast和Menu/销毁活动
  2. Intent
    2.1 显示/隐式
    2.2 传递/返回数据
  3. 活动的生命周期
    3.1 返回栈
    3.2 活动的生命周期/启动模式
    3.3 活动被回收了怎么办?
    3.4 一种知晓当前活动的方法
    3.5 一种随时随地退出程序的方法
  4. 小结

1 Activity

活动的个人定义:一种可以和用户进行交互的窗口
打开一个安卓应用,本质就是使用各种各样的活动和用户进行交互。我把它类比为桌面图形开发中的窗口,如MFC中的Dialog窗口。

1.1手动创建活动

流程:

  1. 创建一个无活动的项目
  2. 在Java下的包中,new一个activity
  3. 在res目录下,新建(或自动创建)一个layout文件夹
  4. 在layout目录下新建(或者自动创建)一个layout源文件
  5. 对layout源文件进行修改
  6. 在AndroidManifest文件中进行注册

关键点:

-1-在activity的注册中,要配置action和category,使之成为主活动

...
        <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
...

-2-一个activity对应一个layout,该代码写在onCreate中

setContentView(R.layout.first_layout);

-3-如书中在layout中添加了一个按钮,那么在activity的代码中也要new一个Button对象,并修改其内部的事件

1.2 Toast和Menu

Toast的个人定义:一个短暂的提示,悬浮出现。例如……

您正在使用2G/3G/4G网络

使用示例:

...//写在onCreate中
Button button1 = (Button) findViewById(R.id.button_1);
button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Toast.makeText(FirstActivity.this, "You clicked me! ", Toast.LENGTH_SHORT).show();
                finish();
...

可以看到,主要的用法就是Toast.makeText(上下文, "String", 时间长度),时间长度可以使用内置的长度。
另外,中间的finish();可以销毁该活动,类似于按下back键。但是,按下back键会先进行过场动画,再销毁;而使用finish();则直接销毁。详见

Menu的个人定义:一个菜单栏。例如最常见的即右上角的···
用法示例:
首先在res目录下new一个名为menu的文件夹,然后在其中new一个目录源文件。内容修改如下:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:id="@+id/add_item"
    android:title="Add"/>
    <item
        android:id="@+id/remove_item"
        android:title="Remove"/>
</menu>

每一个item对应一个选项。
其后,在activity中,重写onCreateOptionsMenu()方法。重写的快捷键是Ctrl+O。

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

继续重写onOptionsSelected():

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){
            case R.id.add_item:
                Toast.makeText(this, "You clicked Add!", Toast.LENGTH_SHORT).show();break;
            case R.id.remove_item:
                Toast.makeText(this, "You clicked Remove!", Toast.LENGTH_SHORT).show();break;
            default:
        }return true;
    }

Menu使用item.getItemId()获取clicked item 的id。
(顺带一提,从notepad++进化到Android Studio感觉好像从汉朝进化到了2017年的上海。)


2 Intent

intent的个人定义:一个通道,用来启动另一个Activity(就目前进度而言)

2.1显式/隐式

显式

在新建了一个活动(SecondActivity)之后,对按钮内的onClick()进行修改。

Button button1 = (Button) findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });

如此,点击按钮以后,将显式地使用“意图”来打开活动。

隐式:
隐式的Intent,指定了一系列的action和category,只有同时满足两者才能完成调用。有点“对号入座”的意思。
在Manifest文件中对SecondActivity标签进行如下修改:

<activity android:name=".SecondActivity">
            <intent-filter>
                <action android:name="com.example.activitytest.ACTION_START"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

<intent-filter>添加了关于action和category的信息。从标签名也能看得出来,这是用于传递Intent信息的。
要使用隐式的Intent,要对按钮的onClick()进行如下修改:

button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent("com.example.activityTest.ACTION_START");
                startActivity(intent);
            }
        });

由于我们的SecondActivity使用了默认的Category,所以没有给Intent附加有关Category的信息。可以这么做:

button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent("com.example.activityTest.ACTION_START");         Intent.addCategory("com.example.activityTest.MY_CATEGORY");
                startActivity(intent);
            }
        });

光是这样还不能正常工作,要在SecondActivity中附加这样一条category标签<category android:name="com.example.activityTest.MY_CATEGORY"/>
写在<intent-filter>标签里。

使用隐式Intent访问网页
隐式Intent还有如下的用法:

 button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse("http://www.bing.com"));
                startActivity(intent);
            }
        });

Intent.ACTION_VIEW是一个内置动作,其常量值为android.intent.action.VIEW,然后通过Uri.perse()方法,将一个网址字符串解析成一个Uri对象,再调用setData方法将这个Uri对象传递进去。
点击该按钮时,将直接打开系统浏览器,访问指定网址。

另外,假设我们新建了一个Activity,打算用该活动打开特定的网页,那么我们还可以在<intent-filter>内添加<data>标签,以实现过滤的目的。详见原书P47或这里

另一个有趣的例子:使用Intent调用系统拨号界面:

 button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent(Intent.ACTION_DIAL);
                intent.setData(Uri.parse("tel:10086"));
                startActivity(intent);
            }
        });

传递数据

在使用Intent启动活动时,可以将要传递的数据暂存在Intent中,启动另一个活动以后,再将这些数据取出来使用。
示例:将一个字符串传递给下一个活动。

button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                String data = "Hello SecondActivity";
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                intent.putExtra( "extra_data", data);
                startActivity(intent);
            }
        });

这里的putExtra()接受两个数据,第一个是key,第二个是传递的数据,有多重重载方式。
然后我们可以在SecondActivity中取出数据:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Intent intent = getIntent();
        String data = intent.getStringExtra("extra_data");
        Log.d("SecondActivity",data);
    }

这里将Intent传来的额外信息,按key获取之后,用log打印了出来。getXxxExtra()有很多种方法,以此类推。

返回数据

如果要返回数据给上一个活动,要使用startActivityForResult()来启动活动。该方法接受两个参数,第一个还是Intent,第二个是请求码,举例:

button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivityForResult(intent,1 );
            }
        });

以上是在FirstActivity中设定好的按钮事件。在SecondActivity中也设定一个按钮事件:

Button button2 = (Button) findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent();
                intent.putExtra("data_return", "Hello FirstActivity");
                setResult(RESULT_OK, intent);
                finish();
            }
        });

其中的关键就是setResult()用于向上一个活动返回数据。该方法接受两个参数,第一个是返回处理结果,一般只使用RESULT_OK和RESULT_CANCELED这两个值。第二个参数用于将带数据的Intent传递回去。然后调用finish()来销毁当前活动。
此时,来自SecondActivity的Intent回到了FirstActivity,重写onActivityResult()来得到返回的参数:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode){
            case 1: 
                if(resultCode == RESULT_OK){
                    String returnedData = data.getStringExtra("data_return");
                    Log.d("FirstActivity", returnedData);
                }
                break;
            default:        
        }
    }

可以看到,流程是这样的:
1. 使用startActivityForResult(intent, 1);指定一个请求码。
2. 使用setResult(RESULT_OK, intent);返回一个执行结果。
3. 在onActivityResult(int requestCode, int resultCode, Intent intent)中对每一个请求码,按照其返回结果做出相应处理。

这里还有一点,假如用户在SecondActivity中没有点击按钮销毁程序,而是直接使用back键(或者轻触返回——魅族大法好),就不会调用setResult(RESULT_OK, intent);方法,就无法返回数据。对此的解决方法是,在SecondActivity中重写onBackPressed()方法,代码如下所示:

@Override
    public void onBackPressed() {
        Intent intent = new Intent();
        intent.putExtra("data_return", "Hello FirstActivity");
        setResult(RESULT_OK, intent);
        finish();
    }

这样,即使用户使用back键返回,也会传递数据给FirstActivity。


3 活动的生命周期

Android使用任务(Task)来管理活动,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。

3.1 返回栈

这部分的内容书上说的很详细,就不一一展开了。详见

3.2 活动的生命周期/启动模式

书上很详细。或见此

3.3 活动被回收了怎么办?

重写onSaveInstanceState()回调方法,该方法保证活动在被回收之前一定会被调用。用法如下:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        String tempData = "Something you just typed";
        outState.putString("data_key", tempData);
    }

可以看到,这里也是使用key-value对应的。要使用这里的数据,要在onCreate(Bundle savedInstanceState)中填入savedInstanceState类(自动给你填好了),然后在方法中调用savedInstanceState.getString()

@Override
    protected void onCreate(final Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        if(savedInstanceState != null){
            String tempdata = savedInstanceState.getString("data_key");
            Log.d("SecondActivity", tempdata);
        }
    }

3.4 一种知晓当前活动的方法

本书中讲述了一种知晓当前所处哪一个活动的方法。
很简单,就是新建一个BaseActivity类,直接继承我们之前用的AppCompatActivity,并且重写其onCreate()方法,在其中使用Log.d("TAG", getClass().getSimpleName());静态方法直接打印出当前活动名即可。

3.5 一种随时随地退出程序的方法

其思路是新建一个类,用一个专门的List< Activity >容器来管理所有活动,并且写一个静态的add()remove()方法,还有一个终极finishAll()方法

在我们的BaseActivity类中,重写onCreate()方法,使其调用add()方法;重写onDestory()方法,使其调用remove()方法。当想要完全关闭程序的时候,直接调用finishAll()方法就可以了。


4 小结

这是《第一行代码Android 第二版》的读书笔记,本书写的非常通俗易懂,对新人十分友好,我的读书笔记几乎就是原书的摘抄——我认为这样做效率不高,又兼这是本人所写第一篇博客,行文组织方面尚不熟悉,以后将会用更加概括性的语言来叙述,并适当减少示例代码。
读书笔记从第二章开始,第一张过于琐碎,讲出来反而没有书上清楚,故不提。

2017-12-25 15:31:57 wangliu1102 阅读数 957
  • Android 开发利器----- Android调试技术

    课程会为大家讲解如何实现在Android真机上调试自己的项目, 同样该教程会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项知识。讲解使用调试功能快速在开发中排错,查找问题。

    125 人正在学习 去看看 Frank Lee

CoolWeather项目实战(第一部分)–>遍历全国省市县


GitHub上源码的链接:https://github.com/wangliu1102/CoolWeather

一、功能需求

(1)可以罗列出全国所有的省、市、县; 
(2)可以查看全国任意城市的天气信息; 
(3)可以自由地切换城市,去查看其它城市的天气;
(4)提供手动更新以及后台自动贡献天气的功能。

服务器接口:
1、遍历省、市、县:
(1)http://guolin.tech/api/china 列出中国所有的省份 。
(2) http://guolin.tech/api/china/16 列出该id所属省份的所有市(16是江苏省)。
(3)http://guolin.tech/api/china/16/116 列出该id所属市的所有县(116是苏州),县级的信息包含weather_id,用于查询天气。

2、遍历天气:
http://guolin.tech/api/weather?cityid=CN101190401&key=28c01281607a4a9b92195626fb49a4a1 (使用和风天气获取任意城市的天气信息,个人申请的和风天气账号对应的key)

3、必应网站每日一图的接口:
http://guolin.tech/api/bing_pic

需求和所需服务器接口知道了,剩下的就是开始编写代码啦!!!

二、创建数据库和表

项目创建就不多做描述了,使用AndroidStduio创建一个CoolWeather的项目,我的代码路径是com.wl.android.coolweather。

首先,创建数据库和表:
1、在com.wl.android.coolweather包下新建几个包,如下图所示:
这里写图片描述
其中,db用于存放数据库模型相关代码,gson存放GSON模型相关代码,service存放服务相关代码,util存放工具类相关代码。

2、给项目添加相关依赖库,编辑app/build.gradle文件(现在在AndroidStudio2.3.3版本上动态搜索添加依赖库失效,不知道是不是网络原因,故手动编辑添加依赖库):

dependencies {
    ...
    compile 'org.litepal.android:core:1.4.1'
    compile 'com.squareup.okhttp3:okhttp:3.4.1'
    compile 'com.google.code.gson:gson:2.7'
    compile 'com.github.bumptech.glide:glide:3.7.0'
}

其中,LitePal用于对数据库进行操作,OkHttp用于进行网络请求,GSON用于解析JSON数据,Glide用于加载和展示图片。

3、在db包下建立实体类Province、City、County,用于映射分别存放省、市
县数据的三张数据库表:province、city、county。代码如下所示:

package com.wl.android.coolweather.db;

import org.litepal.crud.DataSupport;

/**
 * Created by D22397 on 2017/12/25.
 *
 * 数据表 省:province 对应实体类
 *
 */

public class Province extends DataSupport {

    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;
    }
}
package com.wl.android.coolweather.db;

import org.litepal.crud.DataSupport;

/**
 * Created by D22397 on 2017/12/25.
 *
 * 数据表 市:city 对应实体类
 *
 */

public class City extends DataSupport {

    private int id;

    private String cityName; // 市的名字

    private int cityCode; // 市的代号

    private int provinceId; // 当前市所属省的id

    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;
    }
}
package com.wl.android.coolweather.db;

import org.litepal.crud.DataSupport;

/**
 * Created by D22397 on 2017/12/25.
 *
 * 数据表 县:county 对应实体类
 *
 */

public class County extends DataSupport {

    private int id;

    private String countyName; // 县的名字

    private String weatherId; // 县所对应天气的id

    private int cityId; // 当前县所属市的id

    public int getId() {
        return id;
    }

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

    public String getCountyName() {
        return countyName;
    }

    public void setCountyName(String countyName) {
        this.countyName = countyName;
    }

    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;
    }
}

注意:LitePal中每个实体类都必须继承DataSupport类,方便用于增删改查操作。

4、配置litepal.xml文件,右击app/src/main目录–>New–>Directory,创建一个assets目录,在该目录下新建文件litepal.xml,编辑文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <dbname value="cool_weather"/>

    <version value="1"/>

    <list>
        <mapping class="com.wl.android.coolweather.db.Province"/>
        <mapping class="com.wl.android.coolweather.db.City"/>
        <mapping class="com.wl.android.coolweather.db.County"/>
    </list>
</litepal>

同时,配置一下LitePalApplication,修改AndroidManiFest.xml,代码如下所示:

   <application
        android:name="org.litepal.LitePalApplication"
        android:allowBackup="true"
        android:icon="@mipmap/logo"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
       ...
   </application>

三、遍历全国省市县数据

全国所有省市县数据都是从服务器上获取的,所以需要同服务器进行交互,在util包下新建工具类HttpUtil,代码如下图所示:

package com.wl.android.coolweather.util;

import okhttp3.OkHttpClient;
import okhttp3.Request;

/**
 * Created by D22397 on 2017/12/26.
 * <p>
 * 服务器交互工具类:HttpUtil
 */

public class HttpUtil {

    /**
     * http请求方法
     *
     * @param address 请求地址
     * @param callback 回调处理服务器响应
     */
    public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(address).build();
        client.newCall(request).enqueue(callback);
    }
}

因为返回的数据都是JSON格式的数据,再新建一个工具类Utility,用于解析和处理这种JSON格式的数据,代码如下图所示:

package com.wl.android.coolweather.util;

import android.text.TextUtils;

import com.google.gson.Gson;
import com.wl.android.coolweather.db.City;
import com.wl.android.coolweather.db.County;
import com.wl.android.coolweather.db.Province;
import com.wl.android.coolweather.gson.Weather;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Created by D22397 on 2017/12/26.
 * <p>
 * 解析json数据类 :Utility
 */

public class Utility {

    /**
     * 解析和处理服务器返回的省级数据
     *
     * @param response 服务器响应的数据
     * @return
     */
    public static boolean handleProvinceResponse(String response) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONArray allProvinces = new JSONArray(response);
                for (int i = 0; i < allProvinces.length(); i++) {
                    JSONObject provinceObject = allProvinces.getJSONObject(i);
                    Province province = new Province();
                    province.setProvinceName(provinceObject.getString("name"));
                    province.setProvinceCode(provinceObject.getInt("id"));
                    province.save();
                }
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 解析和处理服务器返回的市级数据
     *
     * @param response   服务器响应的数据
     * @param provinceId 市所属省的id
     * @return
     */
    public static boolean handleCityResponse(String response, int provinceId) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONArray allCities = new JSONArray(response);
                for (int i = 0; i < allCities.length(); i++) {
                    JSONObject cityObject = allCities.getJSONObject(i);
                    City city = new City();
                    city.setCityName(cityObject.getString("name"));
                    city.setCityCode(cityObject.getInt("id"));
                    city.setProvinceId(provinceId);
                    city.save();
                }
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 解析和处理服务器返回的县级数据
     *
     * @param response 服务器响应的数据
     * @param cityId   县所属市的id
     * @return
     */
    public static boolean handleCountyResponse(String response, int cityId) {
        if (!TextUtils.isEmpty(response)) {
            try {
                JSONArray allCounties = new JSONArray(response);
                for (int i = 0; i < allCounties.length(); i++) {
                    JSONObject countyObject = allCounties.getJSONObject(i);
                    County county = new County();
                    county.setCountyName(countyObject.getString("name"));
                    county.setWeatherId(countyObject.getString("weather_id"));
                    county.setCityId(cityId);
                    county.save();
                }
                return true;
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

}

自定义标题栏,不需要原生的ActionBar了,修改res/values/styles.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>

工具类就这么多了,现在开始编写界面了,遍历全国省市县数据的功能在后面会复用到,所以这里把它写到碎片中,这样复用的时候直接在布局里引用碎片就可以了。在res/layout 目录下新建choose_area.xml布局,代码如下:

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

    <!--自定义标题栏-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <TextView
            android:id="@+id/title_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:hint="@string/hint_text"
            android:textColor="#fff"
            android:textSize="20sp"/>

        <!--返回上一级的按钮-->
        <Button
            android:id="@+id/back_button"
            android:layout_width="25dp"
            android:layout_height="25dp"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            android:layout_marginStart="10dp"
            android:background="@drawable/ic_back"/>
    </RelativeLayout>

    <!--显示省市县的数据,ListView可以给每个子项添加一条分割线-->
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>
</LinearLayout>

接下来,新建ChooseAreaFragment类,继承Fragment,代码如下所示:

package com.wl.android.coolweather;

import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.wl.android.coolweather.db.City;
import com.wl.android.coolweather.db.County;
import com.wl.android.coolweather.db.Province;
import com.wl.android.coolweather.util.HttpUtil;
import com.wl.android.coolweather.util.Utility;

import org.litepal.crud.DataSupport;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

/**
 * Created by D22397 on 2017/12/26.
 * <p>
 * 用于遍历省市县数据的碎片类:ChooseAreaFragment
 */

public class ChooseAreaFragment extends Fragment {

    public static final int LEVEL_PROVINCE = 0; // 省级别

    public static final int LEVEL_CITY = 1; // 市级别

    public static final int LEVEL_COUNTY = 2; // 县级别

    private ProgressDialog mProgressDialog; // 请求服务器加载数据时的进度对话框

    private TextView mTitleTextView;

    private Button mBackButton;

    private ListView mListView;

    private ArrayAdapter<String> mAdapter; // ListView适配器

    private List<String> mDataList = new ArrayList<>(); // 保存省市县的名字

    private List<Province> mProvinceList; // 省列表

    private List<City> mCityList; // 市列表

    private List<County> mCountyList; // 县列表

    private Province mSelectedProvince; // 选中的省份

    private City mSelectedCity; // 选中的城市

    private int currentLevel; // 当前选中的级别

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.choose_area, container, false);
        mTitleTextView = (TextView) view.findViewById(R.id.title_text);
        mBackButton = (Button) view.findViewById(R.id.back_button);
        mListView = (ListView) view.findViewById(R.id.list_view);
        mAdapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, mDataList);
        mListView.setAdapter(mAdapter);
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (currentLevel == LEVEL_PROVINCE) {
                    mSelectedProvince = mProvinceList.get(position);
                    queryCities();
                } else if (currentLevel == LEVEL_CITY) {
                    mSelectedCity = mCityList.get(position);
                    queryCounties();
                } 
                }
            }
        });

        mBackButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (currentLevel == LEVEL_COUNTY) {
                    queryCities();
                } else if (currentLevel == LEVEL_CITY) {
                    queryProvinces();
                }
            }
        });

        queryProvinces(); // 初始化先加载省份
    }

    /**
     * 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器查询
     */
    private void queryProvinces() {
        mTitleTextView.setText("中国");
        mBackButton.setVisibility(View.GONE);
        mProvinceList = DataSupport.findAll(Province.class);
        if (mProvinceList.size() > 0) {
            mDataList.clear();
            for (Province province : mProvinceList) {
                mDataList.add(province.getProvinceName());
            }
            mAdapter.notifyDataSetChanged();
            mListView.setSelection(0);
            currentLevel = LEVEL_PROVINCE;
        } else {
            String address = "http://guolin.tech/api/china";
            queryFromServer(address, "province");
        }
    }

    /**
     * 查询选中省内所有的市,优先从数据库查询,如果没有查询到再去服务器查询
     */
    private void queryCities() {
        mTitleTextView.setText(mSelectedProvince.getProvinceName());
        mBackButton.setVisibility(View.VISIBLE);
        mCityList = DataSupport.where("provinceid = ?",
                String.valueOf(mSelectedProvince.getId())).find(City.class);
        if (mCityList.size() > 0) {
            mDataList.clear();
            for (City city : mCityList) {
                mDataList.add(city.getCityName());
            }
            mAdapter.notifyDataSetChanged();
            mListView.setSelection(0);
            currentLevel = LEVEL_CITY;
        } else {
            int provinceCode = mSelectedProvince.getProvinceCode();
            String address = "http://guolin.tech/api/china/" + provinceCode;
            queryFromServer(address, "city");
        }
    }

    /**
     * 查询选中市内所有的县,优先从数据库查询,如果没有查询到再去服务器查询
     */
    private void queryCounties() {
        mTitleTextView.setText(mSelectedCity.getCityName());
        mBackButton.setVisibility(View.VISIBLE);
        mCountyList = DataSupport.where("cityid = ?",
                String.valueOf(mSelectedCity.getId())).find(County.class);
        if (mCountyList.size() > 0) {
            mDataList.clear();
            for (County county : mCountyList) {
                mDataList.add(county.getCountyName());
            }
            mAdapter.notifyDataSetChanged();
            mListView.setSelection(0);
            currentLevel = LEVEL_COUNTY;
        } else {
            int provinceCode = mSelectedProvince.getProvinceCode();
            int cityCode = mSelectedCity.getCityCode();
            String address = "http://guolin.tech/api/china/" + provinceCode + "/" + cityCode;
            queryFromServer(address, "county");
        }
    }

    /**
     * 根据传入的地址和类型从服务器上查询省市县数据
     *
     * @param address 地址
     * @param type    类型(省、市、县)
     */
    private void queryFromServer(String address, final String type) {
        showProgressDialog(); // 请求服务器加载数据时显示进度框
        HttpUtil.sendOkHttpRequest(address, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 通过runOnUiThread()方法回到主线程处理UI
                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 {
                String responseText = response.body().string();
                boolean result = false;
                if ("province".equals(type)) {
                    result = Utility.handleProvinceResponse(responseText);
                } else if ("city".equals(type)) {
                    result = Utility.handleCityResponse(responseText, mSelectedProvince.getId());
                } else if ("county".equals(type)) {
                    result = Utility.handleCountyResponse(responseText, mSelectedCity.getId());
                }

                if (result) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            closeProgressDialog(); // 数据加载完成关闭进度框
                            if ("province".equals(type)) {
                                queryProvinces();
                            } else if ("city".equals(type)) {
                                queryCities();
                            } else if ("county".equals(type)) {
                                queryCounties();
                            }
                        }
                    });
                }
            }
        });
    }

    /**
     * 显示进度框
     */
    private void showProgressDialog() {
        if (mProgressDialog == null) {
            mProgressDialog = new ProgressDialog(getActivity());
            mProgressDialog.setMessage("正在加载...");
            /**
             * dialog.setCancelable(false);dialog弹出后点击屏幕或物理返回键,dialog不消失
             * dialog.setCanceledOnTouchOutside(false);dialog弹出后点击屏幕,dialog不消失;点击物理返回键dialog消失
             */
            mProgressDialog.setCanceledOnTouchOutside(false);
        }
        mProgressDialog.show();
    }

    /**
     * 关闭进度框
     */
    private void closeProgressDialog() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
    }
}

修改activity_main.xml中的代码,引用ChooseAreaFragment,如下所示:

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

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

</FrameLayout>

注意:请求服务器需要网络,故在AndroidManifest.xml文件中要声明权限:

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

好了,到这一步,我们遍历全国所有省市县的功能就完成啦,运行一下吧!!!
这里写图片描述
这里写图片描述
这里写图片描述

2017-10-13 21:03:42 weixin_37967139 阅读数 140
  • Android 开发利器----- Android调试技术

    课程会为大家讲解如何实现在Android真机上调试自己的项目, 同样该教程会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项知识。讲解使用调试功能快速在开发中排错,查找问题。

    125 人正在学习 去看看 Frank Lee
学习郭霖的第一行代码,写博客记录一下,尝试写写博客,听说写博客对身体好~~~
OK,开始!
Android默认没有启用gradle warpper的方式,点击File—>Settings->Build,Execution,Deployment->Gradle
app目录下:res目录:drawable用来放图片,mipmap文件夹下放图标。只给一张图的话,把图片放到drawable-xxhdpi文件夹下。
全局build.gradle文件:
    使用classpath声明一个Gradle插件。因为Gradle不是专门为构建Android项目而开发的,想用它来构建Android项目,需要声明

com.android.tools.build:gradle:2.2.0这个插件
app目录下的build.gradle文件:
compileSdkVersion:用于指定项目的编译版本
buildToolsVersion:用于指定项目构建工具的版本
applicationId:用于指定项目的包名


这就是感觉第一章比较重要的东西

2017-11-20 21:29:32 xhbxhbsq 阅读数 216
  • Android 开发利器----- Android调试技术

    课程会为大家讲解如何实现在Android真机上调试自己的项目, 同样该教程会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项知识。讲解使用调试功能快速在开发中排错,查找问题。

    125 人正在学习 去看看 Frank Lee

最近在看Android 第一行代码-第二版书籍,整理下这本书的具体内容,内容如下:


2015-11-21 11:18:31 z81002623 阅读数 384
  • Android 开发利器----- Android调试技术

    课程会为大家讲解如何实现在Android真机上调试自己的项目, 同样该教程会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项知识。讲解使用调试功能快速在开发中排错,查找问题。

    125 人正在学习 去看看 Frank Lee

1:android王国的历史与android系统架构、开发版本、android应用开发特色

android应用开发特色:

1)四大组件:活动(Activity)、服务(Service)、广播接收器(BroadcastReceiver)、内容提供者(contentProvider)

分别代表应用门面、腼腆的后台支持、收发各种广播消息、在应用程序间共享数据

2)丰富的系统控件

3)SQLite数据库  轻量级、运算极快的嵌入式关系型数据库。支持SQL语法+android封装好的API方便操作,存储+读取数据便利;

4)地理位置定位 通过和系统内置的GPS结合强大的地图功能,走到哪定位到自己的位置。使LBS领域潜力无限

5)强大的多媒体 音乐、视频、录音、拍照、闹钟等等。在程序中通过代码来实现控制,让咱们的应用丰富多彩。

6)传感器 加速度传感器、方向传感器

2:搭建开发环境

1)必备软件  JDK+Android SDK+Eclipse/AndroidStudio+ADT(Android Development Tools)

2) 搭建开发环境 SDK工具包(包含Android SDK、Eclipse、ADT插件集成包)

SDK Menager 管理Android SDK,通过它可以方便下载SDK

AVD:通过它方便创建模拟器,配置模拟器属性

3:创建第一个Android项目、 并分析此程序的结构、以及各种关联逻辑

AndroidMainifest.xml资源文件配置清单-->HelloWorlActivity.java活动-->hello_world_layout布局--其他资源比如string.xml等



没有更多推荐了,返回首页