1. 程式人生 > >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());
	}
}

效果圖:
下拉重新整理:
鬆開重新整理中:
重新整理完成,更新資料:
重新整理資料以官方提供的資料為準,若官方無更新,列表內容跟沒重新整理之前一樣,因為資料來自官方提供