美文网首页
一个完整的Android应用程序(4)

一个完整的Android应用程序(4)

作者: zda123000 | 来源:发表于2018-11-05 22:02 被阅读0次

    下面手动更新天气和切换城市

    1. 手动更新天气
    由于在之前对天气进行了缓存,目前每次展示的都是之前的缓存数据,因此需要实现用户手动更新天气。

    首先修改activity_weather.xml中代码,在ScrollView的外面又嵌套了一层SwipeRefreshLayout,这样ScrollView就自动拥有了刷新功能了。

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary">
        ...
            <android.support.v4.widget.SwipeRefreshLayout
                android:id="@+id/swipe_refresh"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
                <ScrollView
                    android:id="@+id/weather_layout"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:overScrollMode="never"
                    android:scrollbars="none">
            ...
            </android.support.v4.widget.SwipeRefreshLayout>
    </FrameLayout>
    

    然后修改WeatherActivity中代码,首先在onCreate()方法中获取SwipeRefreshLayout的实例,然后调用setColorSchemeResources()方法来设置下拉刷新进度条的颜色,接着定义一个mWeatherId变量用来记录天气id,然后调用setOnRefreshListener()方法来设置一个下拉刷新的监听器,最后请求结束吼,调用setRefreshing()方法并传入false,用于表示刷新时间结束,隐藏刷新进度条。

    public class WeatherActivity extends AppCompatActivity {
        //我是新增的
        public SwipeRefreshLayout swipeRefresh;
        private String mWeatherId;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ...
            //我是新增的
            swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
            swipeRefresh.setColorSchemeResources(R.color.colorPrimary);
    
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            String weatherString = prefs.getString("weather", null);
            //我是新增的
            if (weatherString != null) {
                //有缓存直接解析天气信息
                Weather weather = Utility.handleWeatherResponse(weatherString);
                mWeatherId = weather.basic.weatherId;
                showWeatherInfo(weather);
            } else {
                // 无缓存去服务器查询天气
                mWeatherId = getIntent().getStringExtra("weather_id");
                weatherLayout.setVisibility(View.INVISIBLE);
                requestWeather(mWeatherId);
            }
            swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    requestWeather(mWeatherId);
                }
            });
            ...
        }
    
       public void requestWeather(final String weatherId) {
            String weatherUrl = "http://guolin.tech/api/weather?cityid=" +
                    weatherId + "&key=9618c9b7080b4638a16fca8687bf9a60";
    
            HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(WeatherActivity.this, "获取天气信息失败1",
                                    Toast.LENGTH_SHORT).show();
                            //我是新增的
                            swipeRefresh.setRefreshing(false);
                        }
                    });
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    final String responseText = response.body().string();
                    final Weather weather = Utility.handleWeatherResponse(responseText);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (weather != null && "ok".equals(weather.status)) {
                                SharedPreferences.Editor editor = PreferenceManager.
                                        getDefaultSharedPreferences(WeatherActivity.this).edit();
                                editor.putString("weather", responseText);
                                editor.apply();
                                //我是新增的
                                mWeatherId=weather.basic.weatherId;
                                showWeatherInfo(weather);
                            } else {
                                Toast.makeText(WeatherActivity.this, "获取天气信息失败2",
                                        Toast.LENGTH_SHORT).show();
                            }
                            //我是新增的
                            swipeRefresh.setRefreshing(false);
                        }
                    });
    
                }
            });
            loadBingPic();
        }
    }
    

    2. 切换城市
    首先按照Material Design 的建议,我们需要在头部布局中加一个切换城市的按钮,不然用户不知道左侧边缘是可以拖动的。修改title.xml中的代码,添加一个BUtton作为切换城市的按钮。

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize">
        <Button
            android:id="@+id/nav_button"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_marginLeft="10dp"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:background="@drawable/ic_home"></Button>
          ...
    </RelativeLayout>
    

    然后修改activity_weather.xml来加入滑动菜单功能,在SwipeRefreshLayout的外面嵌套了一层DrawerLayout。DrawerLayout中第一个子控件用于作为屏幕中显示的内容,第二个子控件用于作为滑动菜单中显示的内容,因此在第二个子控件的位置添加了用于遍历省市县的数据碎片。

    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary">
        ...
        <android.support.v4.widget.DrawerLayout
            android:id="@+id/drawer_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <android.support.v4.widget.SwipeRefreshLayout
                android:id="@+id/swipe_refresh"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
               ...
            </android.support.v4.widget.SwipeRefreshLayout>
    
            <fragment
                android:id="@+id/choose_area_fragment"
                android:name="com.example.dean.deanweather.ChooseAreaFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="start"></fragment>
        </android.support.v4.widget.DrawerLayout>
    </FrameLayout>
    

    然后需要在WeatherActivity中加入滑动菜单的逻辑处理,修改WeatherActivity中的代码,首先在onCreate()方法中获取新增的DrawerLayout和Button 的实例,然后在button点击事件中调用DrawerLayout的openDrawer()方法来打开滑动菜单。

    public class WeatherActivity extends AppCompatActivity {
        public DrawerLayout drawerLayout;
        private Button navButton;
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ...
            drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
            navButton = (Button) findViewById(R.id.nav_button);
            ...
            navButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    drawerLayout.openDrawer(GravityCompat.START);
                }
            });
        }
    }
    

    上面仅仅是打开了滑动菜单而已,还需要处理切换城市后的逻辑。之前选中了某个城市后是跳转到WeatherActivity的,而现在由于外面本来是在WeatherActivity当中的,因此不需要跳转,只是去请求新选择城市的天气信息就行了。因此需要根据ChooseAreaFragment的不同状态来进行不同的逻辑处理,修改ChooseAreaFragment中的代码,instanceof 关键字可以用来判断一个对象是否属于某个类的实例。我们在碎片中调用个体Activity()方法,然后配合instanceof关键字,就能轻松的判断该碎片是MainActivity还是WeatherActivity当中。如果是在MainActivity,那么逻辑不变。如果是在WeatherActivity当中,那么关掉滑动菜单,显示下拉刷新进度条,然后请求新的城市的天气信息。

    public class ChooseAreaFragment extends Fragment {
            @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    if (currentLevel == LEVEL_PROVINCE) {
                        selectedProvince = provinceList.get(position);
                        queryCities();
                    } else if (currentLevel == LEVEL_CITY) {
                        selectedCity = cityList.get(position);
                        queryCounties();
                    } else if (currentLevel == LEVEL_COUNTY) {
                        String weatherId = countyList.get(position).getWeatherId();
                        //我是新增的
                        if(getActivity() instanceof  MainActivity) {
                            Intent intent = new Intent(getActivity(), WeatherActivity.class);
                            intent.putExtra("weather_id", weatherId);
                            startActivity(intent);
                            getActivity().finish();
                        }else if(getActivity() instanceof WeatherActivity) {
                            WeatherActivity activity = (WeatherActivity) getActivity();
                            activity.drawerLayout.closeDrawers();
                            activity.swipeRefresh.setRefreshing(true);
                            activity.requestWeather(weatherId);
                        }
                    }
                }
            });
            ...
        }
    }
    
    效果1
    效果2

    3. 后台自动更新天气
    想要实现上述功能,需要创建一个长期在后台运行的定时任务。
    首先在service包下新建一个服务,创建AutoUpdateService,在onStartCommand()方法中先调用了updateWeather() 方法来更新天气,然后调用了updateBingPic()方法来更新背景图片。这里直接存储到SharedPreferences文件中就可以了,因为打开WeatherActivity的时候会优先从SharedPreferences缓存中读取数据。将时间间隔设置为8小时,8小时后onStartCommand()方法重新执行。

    public class AutoUpdateService extends Service {
    
        public AutoUpdateService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            //throw new UnsupportedOperationException("Not yet implemented");
            return null;
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            updateWeather();
            updateBingPic();
    
            AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
            int anhour = 8 * 60 * 60 * 1000;//8小时
            long triggerAtTime = SystemClock.elapsedRealtime() + anhour;
            Intent i = new Intent(this, AutoUpdateService.class);
            PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
            manager.cancel(pi);
            manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
            return super.onStartCommand(intent, flags, startId);
        }
    
        private void updateWeather() {
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            String weatherString = prefs.getString("weather", null);
            if (weatherString != null) {
                Weather weather = Utility.handleWeatherResponse(weatherString);
                final String weatherId = weather.basic.weatherId;
    
                String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId +
                        "&key=9618c9b7080b4638a16fca8687bf9a60";
                HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        e.printStackTrace();
                    }
    
                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        String responseText = response.body().string();
                        Weather weather = Utility.handleWeatherResponse(responseText);
                        if (weather != null && "ok".equals(weather.status)) {
                            SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(
                                    AutoUpdateService.this).edit();
                            editor.putString("weather", responseText);
                            editor.apply();
                        }
                    }
    
                });
            }
        }
    
        /**
         * 更新每日一图
         */
        private void updateBingPic() {
            String requestBingPic = "http://guolin.tech/api/bing_pic";
            HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    e.printStackTrace();
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    String bingPic = response.body().string();
                    SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(
                            AutoUpdateService.this).edit();
                    editor.putString("bing_pic", bingPic);
                    editor.apply();
                }
            });
        }
    }
    

    最后去激活AutoUpdateService 这个服务就行

    private void showWeatherInfo(Weather weather) {
                ...
                Intent intent = new Intent(this,AutoUpdateService.class);
                startService(intent);
           
        }
    

    基本功能就已经完成了,后续会继续添加其他功能

    源码地址

    相关文章

      网友评论

          本文标题:一个完整的Android应用程序(4)

          本文链接:https://www.haomeiwen.com/subject/grjexqtx.html