本市天气(百度定位与车联网之天气查询)

废话不多说,上图:



下面用到的知识有,百度定位及车联网API的使用,当然车联网API看起来高大上,其实我们这里只用来获取车联网中的天气查询功能。其他的功能还有渐变动画及缩放动画,以及定时更新天气及定位信息,存储天气信息到SharedPreference文件中。这些都是在后台执行的哦。


1.获取百度地图密钥并导入开发包


看看这个标题,我们就知道必须要获得自己所在的区域,才知道怎么定位本地,定位本地之后才能确定获取的天气信息。那么用谷歌地图?中国有一个强大的局域网,你不翻墙,貌似不能用把,在中国想要定位无外乎两大厂商,一个高德地图,一个百度地图,你可以根据习惯自己选择自己适合的地图。我们这里用的是百度地图的定位功能。下面我们来使用百度提供的功能。


①申请百度帐号


这个不用多说,不然你怎么管理应用呢?在这个网址注册:http://developer.baidu.com/map/


②创建应用


在第一个步骤中的网址中选择如下所示的信息:



然后点击申请密钥(ak),如图:



点击申请密钥后,得到如下图所示的结果:




那么下一步就是创建应用了。如下:




数字签名的获取方式如下所示:



输入的密钥口令是android,这里没显示,具体原因我也不清楚,不过是一定要输入的。获取的SHA1证书指纹。


那么百度创建应用里面的安全码就是这个指纹+;+包名(当然没有+号),包名在AndroidManifest.xml中的<manifest>标签属性中的package值。


提交后就会得到访问应用的AK:



③下载SDK


如图点击全部下载:



我们只需要定位功能,下载一个开发包就够了,如下:



不会用下载示例代码参考,好了,百度开发者中心的任务到这里就完成了。


④导入开发包


我将BaiduLBS_Android.jar放在app/src/libs中,将所有的so文件放在app/libs中。


打开项目app目录下的build.gragle添加如下代码才算导入所有文件都成功:


sourceSets {
    main {
        jniLibs.srcDirs = ['libs']
    }
}


这个是android标签的直接子标签。当然上面的jar文件还要add as library才能成功。


2.定位到本市(也就是本地)


①添加密钥


在配置文件AndroidManifest.xml中application标签中添加如下代码:


<meta-data
    android:name="com.baidu.lbsapi.API_KEY"
    android:value="qxpnHHLKBgCrTEUNPAwDF7Df" />       //key:开发者申请的key


②在application标签中声明service组件,每个app拥有自己单独的定位service


<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote">
    <intent-filter>
    <action android:name="com.baidu.location.service_v2.2"> </action>
</intent-filter>
</service>


③申明使用权限


<!-- 这个权限用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!-- 这个权限用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<!-- 用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!-- 访问网络,网络定位需要上网-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SD卡读取权限,用户写入离线定位数据-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>


④合理使用定位及天气


我们知道,怎么获取天气,不造成应用程序的卡盾,又可以在用户神不知鬼不觉的时候更新天气呢?


答案是定时更新定位天气信息。


那么广播和Service就可以达到你想要的效果。


我们在Service里面定时定位和更新天气并存储到SharedPreferences文件中。我们在这里设置每8个小时更新一次天气。


我们在启动一个Service执行定位功能。


WeatherService代码如下:


首先定义两个成员变量:


private LocationClient mLocationClient = null;
private BDLocationListener myListener = new MyLocationListener();


当启动Service后会首先执行onCreate(),我们在里面初始化成员变量:


@Override
public void onCreate() {
    super.onCreate();
    mLocationClient = new LocationClient(getApplicationContext());     //声明LocationClient类
    mLocationClient.registerLocationListener(myListener);    //注册监听函数
    initLocation();
}


这里设置了定位的监听函数,当请求定位的时候,在监听函数onReceiveLocation中返回定位的结果信息。


监听函数的代码如下:


public class MyLocationListener implements BDLocationListener {

    @Override
    public void onReceiveLocation(BDLocation location) {
        //Receive Location
        StringBuffer sb = new StringBuffer(256);
        sb.append("time : ");
        sb.append(location.getTime());
        sb.append("\nerror code : ");
        sb.append(location.getLocType());
        sb.append("\nlatitude : ");
        sb.append(location.getLatitude());
        sb.append("\nlontitude : ");
        sb.append(location.getLongitude());
        sb.append("\nradius : ");
        sb.append(location.getRadius());
        if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果
            sb.append("\nspeed : ");
            sb.append(location.getSpeed());// 单位:公里每小时
            sb.append("\nsatellite : ");
            sb.append(location.getSatelliteNumber());
            sb.append("\nheight : ");
            sb.append(location.getAltitude());// 单位:米
            sb.append("\ndirection : ");
            sb.append(location.getDirection());// 单位度
            sb.append("\naddr : ");
            sb.append(location.getAddrStr());
            sb.append("\ndescribe : ");
            sb.append("gps定位成功");

        } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果
            sb.append("\naddr : ");
            sb.append(location.getAddrStr());
            //运营商信息
            sb.append("\noperationers : ");
            sb.append(location.getOperators());
            sb.append("\ndescribe : ");
            sb.append("网络定位成功");
        } else if (location.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果
            sb.append("\ndescribe : ");
            sb.append("离线定位成功,离线定位结果也是有效的");
        } else if (location.getLocType() == BDLocation.TypeServerError) {
            sb.append("\ndescribe : ");
            sb.append("服务端网络定位失败,可以反馈IMEI号和大体定位时间到[email protected],会有人追查原因");
        } else if (location.getLocType() == BDLocation.TypeNetWorkException) {
            sb.append("\ndescribe : ");
            sb.append("网络不同导致定位失败,请检查网络是否通畅");
        } else if (location.getLocType() == BDLocation.TypeCriteriaException) {
            sb.append("\ndescribe : ");
            sb.append("无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机");
        }
        sb.append("\nlocationdescribe : ");
        sb.append(location.getLocationDescribe());// 位置语义化信息
        Log.i("liyuanjinglyj",sb.toString());
        //Log.i("liyuanjinglyj", location.getCity());
        //Log.i("liyuanjinglyj",location.getCity().substring(0,location.getCity().length()-1));
        
        mLocationClient.stop();
    }
}



在后面使用location.getCity()返回你所定位的市级信息,比如你是宜昌市的就返回的是宜昌市。


start:启动定位SDK。 stop:关闭定位SDK。调用start之后只需要等待定位结果自动回调即可。
开发者定位场景如果是单次定位的场景,在收到定位结果之后直接调用stop函数即可。
如果stop之后仍然想进行定位,可以再次start等待定位结果回调即可。


我们基本8个小时才执行一次,所以记得用完后关闭定位SDK。节省系统资源。


initLocation为初始化定位所需要的参数,代码如下:


private void initLocation(){
    LocationClientOption option = new LocationClientOption();
    option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy
    );//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
    option.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系
    int span=1000;
    option.setScanSpan(span);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的
    option.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要
    option.setOpenGps(true);//可选,默认false,设置是否使用gps
    option.setLocationNotify(true);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
    option.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近”
    option.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到
    option.setIgnoreKillProcess(false);//可选,默认false,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认杀死
    option.SetIgnoreCacheException(false);//可选,默认false,设置是否收集CRASH信息,默认收集
    option.setEnableSimulateGps(false);//可选,默认false,设置是否需要过滤gps仿真结果,默认需要
    mLocationClient.setLocOption(option);
}


根据你的需要可以适当的更改数据,或删除数据。



为了达到我们的定时功能,我们需要每隔8个小时启动广播,而且当执行完onCreate()函数后,接着Service会执行onStartCommand方法。代码如下:


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    updateWeather();
    AlarmManager manager=(AlarmManager)getSystemService(ALARM_SERVICE);
    int anHour=8*60*60*1000;
    long triggerAtTime= SystemClock.elapsedRealtime()+anHour;
    Intent i=new Intent(this, AutoUpdateReceiver.class);
    PendingIntent pi=PendingIntent.getBroadcast(this,0,i,0);
    manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);
    return super.onStartCommand(intent,flags,startId);
}

/**
 * 开始定位
 */
private void updateWeather(){
    mLocationClient.start();
}



PendingIntent就是延迟的Intent这个不必多做解释了,相信大家都懂,我们这里使用AlarmManager实现定时功能。


SystemClock.elapsedRealtime():从开机到现在的毫秒数。


AlarmManager.set方法参数:


set(int type,long startTime,PendingIntent pi);
该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。


type:AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;

startTime: 闹钟的第一次执行时间。也就是8个小时之后。

pi:绑定了闹钟的执行动作,比如发送一个广播、给出提示等等


我们在MainActivity.class的onCreate()方法中启动了该服务:


Intent i=new Intent(this,WeatherService.class);
startService(i);


那么八个小时之后,我们会在广播AutoUpdateReceiver.class中再次启动服务:


public class AutoUpdateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i=new Intent(context, WeatherService.class);
        context.startService(i);
    }
}


这样WeatherService就会一直在后台运行,并保证每8个小时更新一次天气。


⑤配置广播与服务



最后在AndroidManifest.xml中配置广播与服务:


<service android:name="com.example.liyuanjing.service.WeatherService"/>
<receiver android:name="com.example.liyuanjing.receiver.AutoUpdateReceiver"/>



3.获取天气信息



其实百度不仅提供了定位的功能,也提供了天气的功能,刚才在本文第一节的创建应用中的启动服务中,增加一个功能,天气就在这个功能中,叫车联网API。把勾勾加上。我们就可以使用百度提供的天气服务了。


那么怎么获取天气呢?


只需要请求该URL就可以获取天气的JSON数据了,下面是URL格式:


" http://api.map.baidu.com/telematics/v3/weather?location ="+ URLEncoder.encode(location.getCity().substring(0, location.getCity().length() - 1), "UTF-8")+"&output=json&ak=" +"你创建应用时候获取的AK"+"&mcode="+"你创建应用的时候输入的安全码";


如上所示,location.getCity()就是刚才定位里面的市级信息。中间转换成UTF-8编码,而且截取了字符串,比如你获取的市级信息为宜昌市,那么我们网址要的只是宜昌两个字的编码,必须把市删掉,所以截取了字符串,把市去掉了。


下面我们开始完善我们的WeatherService.class,让它不仅可以定位,而且可以获取天气信息。


在上个步骤定位回调函数MyLocationListener.onReceiveLocation的末尾加入如下代码:


try {
    String url="http://api.map.baidu.com/telematics/v3/weather?location="+ URLEncoder.encode(location.getCity().substring(0, location.getCity().length() - 1), "UTF-8")+"&output=json&ak=" +
            "qxpnHHLKBgCrTEUNPAwDF7Df"+"&mcode="+"9E:8B:66:E6:5E:73:FD:3A:AE:56:E4:22:68:DE:8E:6C:FC:16:E0:22;com.example.liyuanjing.demowy";
    new CoolAsyncTask().execute(url);
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}


并在WeatherService.class中创建CoolAsyncTask的异步加载类,如下:


private class CoolAsyncTask extends AsyncTask<String,Integer,String> {
    @Override
    protected String doInBackground(String... params) {
        String str=null;
        try {
            str=new String(ConnectNetwork.getImageDat(params[0]));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    @Override
    protected void onPostExecute(String s) {
        WeatherItem item =WeatherJson.readWeatherFromInputStream(s);
        SharedPreferences.Editor editor= PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit();
        editor.putString("pm",item.getPm());
        editor.putString("current_city", item.getCurrentCity());
        editor.putString("current_date",item.getDate());
        editor.putString("weather",item.getWeather());
        editor.putString("wind",item.getWind());
        editor.putString("temperature",item.getTemperature());
        editor.commit();
        
    }
}



这样我们请求天气信息,并用Json解析后存入SharedPreferences文件中。


这样,我们的天气请求Service就完成了。


4.Json解析百度天气



在上一个步骤中,有一个Json解析类不曾讲解到,就是WeatherJson。下面我们来看看它的代码:


public class WeatherJson {
    public static WeatherItem readWeatherFromInputStream(String str){
        WeatherItem weatherItem=null;
        try {
            weatherItem=new WeatherItem();
            //遍历第一层数据
            JSONObject rootJson = new JSONObject(str);
            JSONArray jsonArray=rootJson.getJSONArray("results");
            //遍历第二层数据
            JSONObject resultsJsonObject= (JSONObject) jsonArray.get(0);
            //Log.i("liyuanjinglyj",resultsJsonObject.getString("pm25"));
            //Log.i("liyuanjinglyj",resultsJsonObject.getString("currentCity"));
            weatherItem.setPm(resultsJsonObject.getString("pm25"));
            weatherItem.setCurrentCity(resultsJsonObject.getString("currentCity"));
            JSONArray weatherDataJsonArray=resultsJsonObject.getJSONArray("weather_data");
            //遍历第三层数据
            JSONObject nowJsonObject= (JSONObject) weatherDataJsonArray.get(0);
            //Log.i("liyuanjinglyj",nowJsonObject.getString("wind"));
            //Log.i("liyuanjinglyj",nowJsonObject.getString("weather"));
            //Log.i("liyuanjinglyj",nowJsonObject.getString("date"));
            //Log.i("liyuanjinglyj",nowJsonObject.getString("temperature"));
            weatherItem.setWind(nowJsonObject.getString("wind"));
            weatherItem.setWeather(nowJsonObject.getString("weather"));
            weatherItem.setDate(nowJsonObject.getString("date"));
            weatherItem.setTemperature(nowJsonObject.getString("temperature"));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return weatherItem;
    }
}


下面我们来看看从百度返回的Json格式,如下:


{
error: 0,
status: "success",
date: "2013-07-17",
results:
[
{
currentCity: "北京市",
pm25: "166",
index: [
{
title: "穿衣",
zs: "舒适",
tipt: "穿衣指数",
des: "建议着长袖T恤、衬衫加单裤等服装。年老体弱者宜着针织长袖衬衫、马甲和长裤。"
},
{
title: "洗车",
zs: "不宜",
tipt: "洗车指数",
des: "不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。"
},
{
title: "感冒",
zs: "较易发",
tipt: "感冒指数",
des: "相对今天出现了较大幅度降温,较易发生感冒,体质较弱的朋友请注意适当防护。"
},
{
title: "运动",
zs: "较不宜",
tipt: "运动指数",
des: "有降水,推荐您在室内进行健身休闲运动;若坚持户外运动,须注意携带雨具并注意避雨防滑。"
},
{
title: "紫外线强度",
zs: "弱",
tipt: "紫外线强度指数",
des: "紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。"
}
],
weather_data:
[
{
date: "周三(今天, 实时:24℃)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/duoyun.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "多云",
wind: "微风",
temperature: "23℃~ 15℃"
},
{
date: "明天(周四)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/leizhenyu.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/zhongyu.png",
weather: "雷阵雨转中雨",
wind: "微风",
temperature: "29~22℃"
},
{
date: "后天(周五)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/yin.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "阴转多云",
wind: "微风",
temperature: "31~23℃"
},
{
date: "大后天(周六)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/duoyun.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "多云",
wind: "微风",
temperature: "31~24℃"
}
]
},
{
currentCity: "合肥市",
weather_data:
[
{
date: "今天(周三)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/duoyun.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "多云",
wind: "东风3-4级",
temperature: "27℃"
},
{
date: "明天(周四)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/duoyun.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "多云",
wind: "东北风3-4级",
temperature: "35~27℃"
},
{
date: "后天(周五)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/duoyun.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "多云",
wind: "南风",
temperature: "35~27℃"
},
{
date: "大后天(周六)",
dayPictureUrl: " http://api.map.baidu.com/images/weather/day/duoyun.png",
nightPictureUrl: " http://api.map.baidu.com/images/weather/night/duoyun.png",
weather: "多云",
wind: "东风",
temperature: "34~27℃"
}
]
}
]
}

我们可以看到,我们所需要的Json数据大体上看仅有一个JsonObject。


所以我们获取的时候,首先直接用的JsonObject而不是JsonArray。但是我们要的数据都在results中。


我们不仅要获取results中的pm与currentCity。而且还要获取results中weather_data信息。前面属于第二层数据,weather_data中又是一个JsonArray。而我们要的只是今天的数据所以得到weather_data的第一个数据就可以了。


这样看起来上面的数据就简单多了把。



5.天气布局文件



下面就是布局文件weather_layout的代码:



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/weather_layout_main"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/lyj_title_bar_layout_main"
    android:layout_marginTop="-6dp"
    android:visibility="gone">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00000000">

        <ImageView
            android:layout_width="12dp"
            android:layout_height="6dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="18dp"

            android:background="@drawable/biz_main_more_menu_indication"/>
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="40dp"
        android:background="#88ffffff">

        <LinearLayout
            android:id="@+id/dushu"
            android:layout_width="match_parent"
            android:layout_height="83dp"
            android:orientation="horizontal">

            <ImageView
                android:id="@+id/weather_layout_current_one_image"
                android:layout_width="50dp"
                android:layout_height="83dp"
                android:background="@drawable/biz_more_menu_t2"/>

            <ImageView
                android:id="@+id/weather_layout_current_two_image"
                android:layout_width="50dp"
                android:layout_height="83dp"
                android:background="@drawable/biz_more_menu_t1"/>

            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="83dp">

                <ImageView
                    android:layout_width="30dp"
                    android:layout_height="24dp"
                    android:background="@drawable/biz_more_menu_weather_symbol"/>

                <TextView
                    android:id="@+id/weather_layout_temperature"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentBottom="true"
                    android:text="@string/weather_layout_section"
                    android:textSize="18sp"/>
            </RelativeLayout>
        </LinearLayout>

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:gravity="right"
            android:layout_marginRight="20dp"
            android:orientation="vertical">

            <ImageView
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:background="@drawable/biz_pc_plugin_weather_duoyun"/>

            <TextView
                android:id="@+id/weather_layout_weatherwind"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/weather_layout_desc"
                android:textSize="18sp"/>

            <TextView
                android:id="@+id/weather_layout_location"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/weather_layout_location"
                android:textSize="18sp"/>
        </LinearLayout>

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

            <TextView
                android:id="@+id/weather_layout_date"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/weather_layout_time"
                android:textSize="18sp"/>

            <TextView
                android:id="@+id/weather_layout_pm"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/weather_layout_pm"
                android:textSize="18sp"/>

        </LinearLayout>

        <LinearLayout
            android:id="@+id/weather_layout_one_images"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/weather_layout_desc"
            android:layout_marginTop="50dp"
            android:layout_marginLeft="-10dp"
            android:orientation="horizontal">

            <LinearLayout
                android:id="@+id/weather_layout_search"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:orientation="vertical"
                android:layout_weight="1">

                <ImageButton
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:scaleType="fitXY"
                    android:background="@drawable/ic_main_more_menu_search_icon"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:text="@string/weather_layout_search_txt"/>
            </LinearLayout>


            <LinearLayout
                android:id="@+id/weather_layout_headline"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:orientation="vertical"
                android:layout_weight="1">

                <ImageButton
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:scaleType="fitXY"
                    android:background="@drawable/ic_main_more_menu_headline_icon"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:text="@string/weather_layout_headline_txt"/>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/weather_layout_offline"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:gravity="center"
                android:layout_weight="1">

                <ImageButton
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:scaleType="fitXY"
                    android:background="@drawable/ic_main_more_menu_offline_icon"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:text="@string/weather_layout_offline_txt"/>
            </LinearLayout>


        </LinearLayout>

        <LinearLayout
            android:layout_below="@+id/weather_layout_one_images"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:layout_marginLeft="-10dp"
            android:orientation="horizontal">

            <LinearLayout
                android:id="@+id/weather_layout_theme"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:orientation="vertical"
                android:layout_weight="1">

                <ImageButton
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:scaleType="fitXY"
                    android:background="@drawable/ic_main_more_menu_theme_icon"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:text="@string/weather_layout_theme_txt"/>
            </LinearLayout>


            <LinearLayout
                android:id="@+id/weather_layout_scan"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:orientation="vertical"
                android:layout_weight="1">

                <ImageButton
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:scaleType="fitXY"
                    android:background="@drawable/ic_main_more_menu_scan_icon"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:text="@string/weather_layout_scan_txt"/>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/weather_layout_invitation"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:gravity="center"
                android:layout_weight="1">

                <ImageButton
                    android:layout_width="70dp"
                    android:layout_height="70dp"
                    android:scaleType="fitXY"
                    android:background="@drawable/ic_main_more_menu_invitation_icon"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    android:text="@string/weather_layout_invitation_txt"/>
            </LinearLayout>

        </LinearLayout>
    </RelativeLayout>
</LinearLayout>



颜色编码格式:#6位十六进制数据比如(#243232)纯粹的RGB(255,255,255)。

如果颜色编码为:#8位十六进制数据,比如(#00232323)后面六位依然是RGB方法。前面的两位就是透明度。00为完全透明,ff为完全不透明。


这个布局其他的就不需要多解释了。下面我们来分析一下,界面打开的过程。



6.渐变动画和缩放动画



我们打开网易新闻APP,我们发现它是渐变出现的,并且,当整个布局出现后,布局中的六个按钮才开始缩放。


下面我们看看渐变动画的代码:


这个是渐变显示weather_layout_alpha_show_anim.xml的代码:


<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="500"
        android:fromAlpha="0.0"
        android:toAlpha="1.0"/>
</set>


这个是渐变隐藏weather_layout_alpha_hide_anim.xml的代码:


<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="500"
        android:fromAlpha="1.0"
        android:toAlpha="0.0"/>
</set>


一个是从开始没有到有,一个是从有到没有。


按钮的缩放动画weather_layout_scale_anim.xml如下:


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="500"
        android:startOffset="500"
        android:fillAfter="false"
        android:fromXScale="0.0"
        android:toXScale="1.1"
        android:fromYScale="0.0"
        android:toYScale="1.1"
        android:pivotX="50%"
        android:pivotY="50%"/>
</set>


解释:


①因为按钮缩放动画是布局渐变动画后才开始动画的所以添加了android:startOffset="500"在渐变动画半秒执行完成后,缩放动画立即执行。


②因为按钮缩放动画都缩放为原来大小的1.1倍,所以当动画执行完成后,要恢复按钮原来的大小也就是1.0,所以android:fillAfter="false"。


③pivotX="50%",pivotY="50%",从自己的中间部分开始缩放,默认是从控件左上角(0,0)开始缩放的。如果不设置这个属性,就和网易客户端的动画不同了。



下面是执行动画了,这里有上篇文章讲解的一个动画漏洞,还请认真阅读一下。


NewsFragment.class添加天气的成员变量:


//天气代码片段
private LinearLayout weatherLayout;//天气界面整体布局
//六个按钮
private LinearLayout weatherSearchLayout;
private LinearLayout weatherHeadlineLayout;
private LinearLayout weatherOfflineLayout;
private LinearLayout weatherThemeLayout;
private LinearLayout weatherScanLayout;
private LinearLayout weatherInvitationLayout;
//显示和隐藏天气界面的按钮,也就是标题栏上面的三个点
private ImageButton weatherBut;
//标记界面是显示还是隐藏,使按钮点击显示和关闭
private boolean weatherDialogFlag = false;


接着在onCreateView()方法中添加如下代码:


//天气代码片段
this.weatherLayout = (LinearLayout) view.findViewById(R.id.weather_layout_main);
this.weatherSearchLayout = (LinearLayout) view.findViewById(R.id.weather_layout_search);
this.weatherHeadlineLayout = (LinearLayout) view.findViewById(R.id.weather_layout_headline);
this.weatherOfflineLayout = (LinearLayout) view.findViewById(R.id.weather_layout_offline);
this.weatherThemeLayout = (LinearLayout) view.findViewById(R.id.weather_layout_theme);
this.weatherScanLayout = (LinearLayout) view.findViewById(R.id.weather_layout_scan);
this.weatherInvitationLayout = (LinearLayout) view.findViewById(R.id.weather_layout_invitation);
this.weatherBut = (ImageButton) view.findViewById(R.id.weather);
final Animation alphaShow = AnimationUtils.loadAnimation(getActivity(), R.anim.weather_layout_alpha_show_anim);
final Animation alphaHide = AnimationUtils.loadAnimation(getActivity(), R.anim.weather_layout_alpha_hide_anim);
final Animation scaleAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.weather_layout_scale_anim);
this.weatherBut.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (weatherDialogFlag == false) {
            weatherLayout.setAnimation(alphaShow);
            alphaShow.start();//显示天气界面
            weatherLayout.setVisibility(View.VISIBLE);
            weatherDialogFlag = true;//标记界面已经显示
            //立即执行按钮缩放动画
            weatherSearchLayout.startAnimation(scaleAnim);
            weatherHeadlineLayout.setAnimation(scaleAnim);
            weatherOfflineLayout.startAnimation(scaleAnim);
            weatherThemeLayout.startAnimation(scaleAnim);
            weatherScanLayout.startAnimation(scaleAnim);
            weatherInvitationLayout.startAnimation(scaleAnim);
            showWeather(view);
        } else {
            weatherLayout.setAnimation(alphaHide);
            alphaHide.start();//执行渐变隐藏动画
            weatherLayout.setVisibility(View.GONE);
            weatherDialogFlag = false;//标记界面没有显示
        }
    }
});


这里界面动画使用的是setAnimation与Animation.start()而按钮使用的是startAnimation();你可以把按钮的换成前面那种方法,你会发现,打开关闭界面后,按钮渐变动画只执行一次后就不会在执行了。而上面那个渐变动画却可以执行,这是为什么呢?


下面我们按住Ctrl点击setAnimation进去该方法中,得到如下图所示的信息:




这个执行是有条件的,当屏幕打开时,会导致动画开始。也就是说start()出来的动画执行是有前提条件的。


所以当我们需要反复执行动画,或者说,你想立即动画就有效果的话,就优先使用startAnimation()方法吧。



7.设置天气界面



我们查看日志,看看我们获取的天气格式:




在看看网易的天气界面:






大21是当前温度,也就是日志里面的实时。怎么获取实时温度,字符串截取算法就可以完成,而且时间是截取该实时括号前面的部分。


在就是PM2.5后面的轻度污染。


这个判断一下大小也就可以了。


得到如下代码:


private void showWeather(View view) {
    TextView temperatureTxt = (TextView) view.findViewById(R.id.weather_layout_temperature);
    TextView weatherWindTxt = (TextView) view.findViewById(R.id.weather_layout_weatherwind);
    TextView dateTxt = (TextView) view.findViewById(R.id.weather_layout_date);
    TextView pmTxt = (TextView) view.findViewById(R.id.weather_layout_pm);
    TextView locationTxt = (TextView) view.findViewById(R.id.weather_layout_location);
    ImageView oneImages = (ImageView) view.findViewById(R.id.weather_layout_current_one_image);
    ImageView twoImages = (ImageView) view.findViewById(R.id.weather_layout_current_two_image);
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
    temperatureTxt.setText(prefs.getString("temperature", ""));
    weatherWindTxt.setText(prefs.getString("weather", "") + " " + prefs.getString("wind", ""));
    String desc=null;
    if(prefs.getString("pm","")!=""){
        int number=Integer.valueOf(prefs.getString("pm",""));
        if(number<=35){
            desc="优质空气";
        }else if(number<=75){
            desc="无污染";
        }else if(number<=115){
            desc="轻度污染";
        }else if(number<=150){
            desc="中度污染";
        }else if(number<=250){
            desc="重度污染";
        }else if(number<=115){
            desc="严重污染";
        }
        pmTxt.setText(prefs.getString("pm", "")+"    "+desc);
    }
    locationTxt.setText(prefs.getString("current_city", ""));
    if (prefs.getString("current_date", "") != "") {
        String str = prefs.getString("current_date", "");
        int index = str.indexOf(")");
        String currentTemp = str.substring(index - 3, index - 1);//截取实时温度
        index = str.indexOf("(");
        String currentDate = str.substring(0, index - 1);//截取实时温度前面的时间
        dateTxt.setText(currentDate);
        char c1 = currentTemp.charAt(0);//截取两位数温度的第一位数
        char c2 = currentTemp.charAt(1);//截取两位数温度的第二位数
        for (int i = 0; i < 10; i++) {
            if (String.valueOf(i).equals(String.valueOf(c1))) {//比较温度等于哪个数字
                oneImages.setBackgroundResource(imageRes[i]);
            }
            if(String.valueOf(i).equals(String.valueOf(c2))){//与上解释同理
                twoImages.setBackgroundResource(imageRes[i]);
            }
        }
    }
}


其中imageRes是下面运行截图中0-9的数字图片。这个方法将在打开天气界面的时候调用。


这里为什么用局部变量,其原因很简单,java回收机制,局部变量是回收最快且最迅速的,当方法执行完成之后,局部变量就会被回收,避免资源浪费。



从SharedPreference文件中获取天气信息。根据实时温度设置大的图片温度。那么天气界面部分代码就在这里完成了。


因为要离开两个星期,所以天气图片并没有设置,有兴趣的同学自己拷贝网易反编译后的天气图片进行设置,尽量不要获取百度的天气图片,能从本地获取的图片,尽量从本地获取,节约系统资源。


下一篇博文将在两到三个星期后的某个时间更新。



本文的源代码在:




查看运行的结果:





发布了94 篇原创文章 · 获赞 115 · 访问量 75万+

猜你喜欢

转载自blog.csdn.net/liyuanjinglyj/article/details/49509827