首页 > 代码库 > Android 编程之天气预报下来刷新数据及城市容器配置--3

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

效果图:
下拉刷新:
松开刷新中:
刷新完成,更新数据:
刷新数据以官方提供的数据为准,若官方无更新,列表内容跟没刷新之前一样,因为数据来自官方提供

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