Android 编程之天气预报下来刷新数据及城市容器配置--3

前面已经把活动和服务讲了讲,要注意的是服务的用法,我们在这里是 extends IntentService implements LocationListener ,下面看下 IntentService

IntentService是一个通过Context.startService(Intent)启动可以处理异步请求的Service,使用时你只需要继承IntentService和重写其中的onHandleIntent(Intent)方法接收一个Intent对象,在适当的时候会停止自己(一般在工作完成的时候). 所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求,消息队列模式

    这是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添加的附带客户Intent的Message对象,当Looper发现有Message的时候接着得到Intent对象通过在onHandleIntent((Intent)msg.obj)中调用你的处理程序.处理完后即会停止自己的服务.意思是Intent的生命周期跟你的处理的任务是一致的.所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出.IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程,这里就给我们提供了一个思路,如果有耗时的操作与其在Service里面开启新线程还不如使用IntentService来处理耗时操作

今天说说下来刷新和城市容器管理,很多关于 ListView 的内容通常会用到下来刷新和上拉加载更多操作,就比如QQ手机端的消息列表,下拉可以刷新消息列表一样,城市容器呢,我们这里暂时只是写了一个ArrayList自己定义了一组已经存在的城市数据,后期有需要可以考虑用 xml 存取数据,包括一些天气信息可以存起来,就是在网络状况不好的情况下,让我们的APP可以用本地数据,界面才不会显得那么空洞,XML存取,SharedPreferences适合一些账户信息或其他小数据存取
城市容器、添加城市Dialog:
package com.newer.myweather;
/**
 *城市容器,城市添加Dialog
 *@author Engineer-Jsp
 *@date 2014.10.27
 * */
import java.util.ArrayList;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class LocationActivity extends Activity implements
		MultiChoiceModeListener {

	private ListView locationListView;
	private ArrayAdapter<String> adapter;
	private EditText editText;
	private long mposition;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.location);
		MainActivity.locations.clear();
		MainActivity.locations.add("长  沙");
		MainActivity.locations.add("深  圳");
		MainActivity.locations.add("岳  阳");
		MainActivity.locations.add("常  德");
		locationListView = (ListView) findViewById(R.id.locations);
		adapter = new ArrayAdapter<String>(this,
				R.layout.city_item,
				R.id.city_item_content, MainActivity.locations);
		locationListView.setAdapter(adapter);
		adapter.notifyDataSetChanged();

		locationListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
		locationListView.setMultiChoiceModeListener(this);
	}

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

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (item.getItemId() == R.id.ic_action_add) {
			showAddDialog();
		}
		return super.onOptionsItemSelected(item);
	}

	private void showAddDialog() {
		editText = new EditText(this);
		editText.setHint("请输入地址");
		editText.setSingleLine(true);
		AlertDialog.Builder builder = new Builder(this).setTitle("添加地址")
				.setView(editText).setNegativeButton("取消", null)
				.setPositiveButton("确定", new OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						String input = editText.getText().toString().trim();
						MainActivity.locations.add(input);
					}
				});
		builder.show();

	}

	@Override
	public boolean onCreateActionMode(ActionMode mode, Menu menu) {

		getMenuInflater().inflate(R.menu.location, menu);
		mode.setTitle("选择");
		return true;
	}

	@Override
	public boolean onPrepareActionMode(ActionMode mode, Menu menu) {

		return false;
	}

	@Override
	public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
		if (item.getItemId() == R.id.ic_action_delete) {
//			/MainActivity.locations.remove(mposition);
		}

		return false;
	}

	@Override
	public void onDestroyActionMode(ActionMode mode) {

	}

	@Override
	public void onItemCheckedStateChanged(ActionMode mode, int position,
			long id, boolean checked) {
		mposition = id;
		int count = locationListView.getCheckedItemCount();
		mode.setSubtitle(count + "项");
	}
}

下拉刷新:

package com.newer.myweather.weight;
/**
 * 下拉刷新数据
 * @author Engineer-Jsp
 * @date 2014.10.27
 * */
import java.util.Date;
import com.newer.myweather.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MyListView extends ListView implements OnScrollListener {
	private View headView;
	private ImageView arror;
	private ProgressBar progressBar;
	private TextView title;
	private TextView last_update;
	private int headContentWidth;
	private int headContentHeight;
	private int firstVisableIndex;//在页面中 第一个能够看见item(listView)的位置
	private Animation animation;
	private Animation animation2;
	private float startY;//用来记录headeVIew将要显示时位置   在整个滑动中 只记录一次
	private boolean isRecord = false; //用来记录startY 是否已经记录
	private float tempY;//动态Y轴坐标

	private final static int PULL_TO_REFRESH = 0;//下拉刷新
	private final static int RELEASE_TO_REFRESH = 1;//松开刷新
	private final static int REFRESHING = 2;//正在刷新
	private final static int DONE = 3;//刷新完成
	private int state ;//当前下拉刷新控件的状态
	private boolean isBack= false;//记录是否从松开刷新回到的下拉刷新
	private OnRefreshListener refreshListener;//刷新监听器

	private final static int RATIO = 3;//实际拉动的距离 和  headview距离页面顶端距离的比例
										// 300px					100px;

	public MyListView(Context context) {
		super(context);
		init(context);
	}

	public MyListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	private void init(Context context) {
		//1:将header 与listView 合成
//		LayoutInflater inflater = LayoutInflater.from(context);
//		inflater.inflate(resource, root, attachToRoot);
		headView = View.inflate(context, R.layout.header, null);

		arror = (ImageView) headView.findViewById(R.id.arror);
		progressBar = (ProgressBar) headView.findViewById(R.id.progressBar);
		title = (TextView) headView.findViewById(R.id.title);
		last_update = (TextView) headView.findViewById(R.id.last_update);

		arror.setMinimumWidth(70);
		arror.setMinimumHeight(50);

		//测量出header控件的尺寸
		measureView(headView);

		//得到haderView测量后的尺寸
		headContentWidth = headView.getMeasuredWidth();
		headContentHeight = headView.getMeasuredHeight();

		//指定headView 位置
		headView.setPadding(0, -1 * headContentHeight, 0, 0);

		//绑定
		addHeaderView(headView);

		//listView 添加OnScrollListener
		setOnScrollListener(this);

		//创建箭头使用的动画

		//右→左
		animation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		animation.setDuration(250);
		animation.setFillAfter(true);
		animation.setInterpolator(new LinearInterpolator());//修改动画 运行效果
		/**
		 * Interpolator 定义了动画的变化速度,可以实现匀速、正加速、负加速、无规则变加速等;

			AccelerateDecelerateInterpolator,延迟减速,在动作执行到中间的时候才执行该特效。
			AccelerateInterpolator, 会使慢慢以(float)的参数降低速度。
			LinearInterpolator,平稳不变的
			DecelerateInterpolator,在中间加速,两头慢
			CycleInterpolator,曲线运动特效,要传递float型的参数。
		 * */

		//左→右
		animation2 = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		animation2.setDuration(200);
		animation2.setFillAfter(true);
		animation2.setInterpolator(new LinearInterpolator());//修改动画 运行效果
	}

	//测量出header控件的尺寸
	private void measureView(View child) {//child <==> headView
		ViewGroup.LayoutParams lp = child.getLayoutParams();

		//初始化操作
		if(lp == null){
			lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
		}

		//设置控件尺寸
		int childWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
		int childHeight;

		if(lp.height > 0){
			//headView有自己的高度
			childHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
		} else {
			//headView没有高度  指定为0
			childHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}

		//记录测量后 控件的尺寸
		child.measure(childWidth, childHeight);

	}

	@Override
	public void onScroll(AbsListView arg0, int firstVisableItem, int arg2, int arg3) {
		// TODO Auto-generated method stub
		//firstVisableItem  在页面中 第一个能够看见item(listView)的位置
		firstVisableIndex = firstVisableItem;
	}

	@Override
	public void onScrollStateChanged(AbsListView arg0, int arg1) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN://按下

			//如果当前第一个看见的item为0的位置 同时  startY没有被记录过, 这个时候记录StartY
			if(firstVisableIndex == 0 && !isRecord){
				startY = event.getY();
				isRecord = true;//修正记录StartY 点的状态
			}
			break;
		case MotionEvent.ACTION_MOVE://移动
			tempY = event.getY();

			if(firstVisableIndex == 0 && !isRecord){
				startY = tempY;
				isRecord = true;
			}

			//在非正在滑动状态时, listView的滑动效果
			if(state != REFRESHING){

				//下拉刷新状态
				if(state == PULL_TO_REFRESH){
					setSelection(0);

					if((tempY - startY) <= 0){
						//下拉刷新状态 向上推了 把整个headView全部隐藏  回到了 刷新完成
						state = DONE;
						//headView控件状态变化
						changeHeadViewOfState();
					} else if((tempY - startY) /RATIO > headContentHeight){
						//下拉刷新状态 向下拉了   把整个headView全部显示  来到了 松开刷新
						state = RELEASE_TO_REFRESH;
						//headView控件状态变化
						changeHeadViewOfState();
					}
				}

				//松开刷新状态
				if(state == RELEASE_TO_REFRESH){
					setSelection(0);

					if((tempY - startY) <= 0){
						//下拉刷新状态 向上推了 把整个headView全部隐藏  回到了 刷新完成
						state = DONE;
						//headView控件状态变化
						changeHeadViewOfState();
					} else if((tempY - startY) /RATIO < headContentHeight && (tempY - startY) > 0){
						//松开刷新状态 向上推了   把headView隐藏一部分 显示一部分   来到了 下拉刷新
						state = PULL_TO_REFRESH;

						isBack = true;//松开刷新 -->下拉刷新

						//headView控件状态变化
						changeHeadViewOfState();
					}
				}

				//刷新完成状态
				if(state == DONE){
					if((tempY - startY) > 0){
						//刷新完成 下拉  进入了  下拉刷新状态
						state = PULL_TO_REFRESH;
						//headView控件状态变化
						changeHeadViewOfState();
					}
				}
			}

			//setPadding(int left, int top, int right, int bottom)
			headView.setPadding(0, (int) ((tempY-startY) /RATIO - headContentHeight), 0, 0);

			break;
		case MotionEvent.ACTION_UP://松手

			if(state == PULL_TO_REFRESH){
				//回到刷新完成状态
				state = DONE;

				changeHeadViewOfState();
			}

			if(state == RELEASE_TO_REFRESH){
				//进入正在刷新状态

				state = REFRESHING;
				changeHeadViewOfState();

				//数据刷新
				onRefresh();

			}
			break;

		}

		invalidate();//listView重绘

		return true;
//		return super.onTouchEvent(event);
	}

	//headView控件状态变化
	private void changeHeadViewOfState(){
		switch (state) {
		case PULL_TO_REFRESH://下拉刷新
			arror.setVisibility(View.VISIBLE);
			progressBar.setVisibility(View.GONE);
			title.setVisibility(View.VISIBLE);
			last_update.setVisibility(View.VISIBLE);

			title.setText("下拉刷新");

			//指定动画
			arror.clearAnimation();
			if(isBack){
				//松开刷新 --> 下拉刷新
				//左-->右  顺指针
				arror.startAnimation(animation2);

				isBack = false;
			}

			break;

		case RELEASE_TO_REFRESH://松开刷新
			arror.setVisibility(View.VISIBLE);
			progressBar.setVisibility(View.GONE);
			title.setVisibility(View.VISIBLE);
			last_update.setVisibility(View.VISIBLE);

			title.setText("松开刷新");

			arror.clearAnimation();
			//右-->左  逆时针
			arror.startAnimation(animation);

			break;

		case REFRESHING://正在刷新
			arror.setVisibility(View.GONE);
			progressBar.setVisibility(View.VISIBLE);
			title.setVisibility(View.VISIBLE);
			last_update.setVisibility(View.VISIBLE);

			title.setText("正在刷新中...");

			//setPadding(int left, int top, int right, int bottom)
			headView.setPadding(0, 0, 0, 0);

			break;

		case DONE://刷新完成
			arror.setVisibility(View.VISIBLE);
			progressBar.setVisibility(View.GONE);
			title.setVisibility(View.VISIBLE);
			last_update.setVisibility(View.VISIBLE);

			title.setText("下拉刷新");

			headView.setPadding(0, -1 * headContentHeight, 0, 0);
			break;
		}
	}

	//刷新数据
	private void onRefresh() {
		refreshListener.onRefresh();
	}

	//提供访问接口
	public interface OnRefreshListener{
		abstract void onRefresh();
	}

	public void setOnRefreshListener(OnRefreshListener listener){
		refreshListener = listener;
	}

	//刷新后 执行的操作    更新时间、更新headView的状态
	public void onRefreshComplete() {

		//更新headView的状态
		state = DONE;
		changeHeadViewOfState();

		//更新时间
		last_update.setText("更新于: " + new Date().toLocaleString());
	}

	@Override
	public void setAdapter(ListAdapter adapter) {
		super.setAdapter(adapter);

		//更新时间
		last_update.setText("更新于: " + new Date().toLocaleString());
	}
}

效果图:

下拉刷新:

松开刷新中:

刷新完成,更新数据:

刷新数据以官方提供的数据为准,若官方无更新,列表内容跟没刷新之前一样,因为数据来自官方提供
				
时间: 2024-10-19 09:42:00

Android 编程之天气预报下来刷新数据及城市容器配置--3的相关文章

Android 编程之天气预报闹钟启动服务设置界面加载--4

真个application讲的已经差不多了,在说完这一篇之后,我会上传源码到资源,喜欢的可以下载和学习,今天主讲闹钟启动设置,设置页面的加载 在Android系统中,闹钟和唤醒功能都是由Alarm Manager Service控制并管理的.我们所熟悉的RTC闹钟以及定时器都和它有莫大的关系.为了便于称呼,我常常也把这个service简称为ALMS.         另外,ALMS还提供了一个AlarmManager辅助类.在实际的代码中,应用程序一般都是通过这个辅助类来和ALMS打交道的.就代

Android 编程下如何调整 SwipeRefreshLayout 的下拉刷新距离

SwipeRefreshLayout 的下拉刷新距离比较短,并且也没有提供设置下拉距离的 API,但是看 SwipeRefreshLayout 的源码,会发现有一个内部变量 mDistanceToTriggerSync,这个变量决定了触发刷新的下拉距离.下面的代码展示了源码中是如何给这个变量赋值的: final DisplayMetrics metrics = getResources().getDisplayMetrics(); mDistanceToTriggerSync = (int) M

Android简易版天气预报app的实现(改进版)

最近总是有人来和我说我以前写的一个小app无法正常获取数据~Android简易版天气预报app 今天就又运行了下来查找问题,发现或许是接口有限制吧,不能在多台手机使用同个apikey 然后,发现了我写的代码实在乱七八糟,界面也实在不好看,就又重写了一遍,小小地修改了一遍,开发环境改为了Android Studio 最终效果图如下 工程图如下 一.获取地区信息 做这么一个天气预报app,首先就要获取到国内地区列表 (在我的另一篇博客有介绍:向任意网址发起数据请求) 中国天气网开放有天气预报接口,访

《Android编程权威指南》PhotoGallery应用梳理

PhotoGalley是<Android编程权威指南>书中另外一个重要的应用.        github:https://github.com/xurui1995/PhotoGallery   (欢迎和谢谢您的star) 在第一个应用CriminalIntent中,主要学习了Intent的使用,Fragment的使用,Sqlite等等. 详情:http://www.cnblogs.com/xurui1995/p/5829458.html 在PhotoGalley的应用中,主要学习Androi

《Android编程权威指南》CriminalIntent项目梳理

相信很多新手或者初级开发人员都已经买了第2版的<Android编程权威指南>, 这本书基于Android Studio开发,对入门人员来说是很好的选择,但是很可惜的是, 在完成一个项目后,缺少对项目的一个大体上的梳理,本书在7-17章花了大量篇幅介 绍CriminalIntent应用,所以这里在完成CriminalIntent之后,我在这里自己对项目进行梳理. 介绍:CriminalIntent应用能记录陋习的标题,日期以及照片,也支持在联系人当中查找当事人,通过E-mail, Twitter

《Android编程权威指南》-读书笔记(四)-GeoQuiz功能扩展

<Android编程权威指南>-读书笔记 -GeoQuiz功能扩展 从现在开始,这本书开始扩展应用.在这次扩展中我们将会学习以下知识点: 创建一个新类 更新视图层 更新控制层 Git代码的修改和提交 Android Studio 在设备中运行该应用 给按钮添加图片资源 功能:下图是GeoQuiz应用对象图解.应用的对象按模型.控制器和视图的类别被分为三部分.Android应用是给予模型-控制器-视图(Model-View-Controller,简称MVC)的架构模式进行设计的. 创建一个类 T

菜鸟学Android编程——简单计算器《一》

菜鸟瞎搞,高手莫进 本人菜鸟一枚,最近在学Android编程,网上看了一些视频教程,于是想着平时手机上的计算器应该很简单,自己何不尝试着做一个呢? 于是就冒冒失失的开撸了. 简单计算器嘛,功能当然很少,加减乘除就可以. 第一步:设计布局文件 界面如下图: 由于刚开始学Android,对布局文件也不是很了解,边查边找,最后凑合着写好了布局文件. 注意事项:此布局文件用到了GridLayout布局,是在Android4.0以上才出现的(不知道谷歌有没有开发相应的包来适配4.0以下版本). 有关Gri

Android编程实现WebView全屏播放的方法

这篇文章主要介绍了Android编程实现WebView全屏播放的方法,结合实例形式较为详细的分析了Android实现WebView全屏播放的布局与功能相关技巧,需要的朋友可以参考下! 本文实例讲述了Android编程实现WebView全屏播放的方法.分享给大家供大家参考,具体如下: 最近因为项目要用webview加载html5的视频,开始不能全屏播,做了很久才做出来!那按我的理解说下怎么实现全屏吧. 首先写布局文件activity_main.xml: <LinearLayout xmlns:an

Android listview的下拉刷新

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">好久没有写博客了,今天我就写一个listview的下拉刷新分享给大家,希望对大家学习或者工作上有所帮助,其实呢,大家都知道,在现在的app当中,下拉刷新是比较常用的功能,用到的地方太多了,所以,今天我也就结合自己的思想给家真理一下,那么好,接下来,让我们一起学习,让你分分钟写出自己想要的