Android 程式設計之天氣預報下來重新整理資料及城市容器配置--3
阿新 • • 發佈:2019-02-07
前面已經把活動和服務講了講,要注意的是服務的用法,我們在這裡是 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()); } }
效果圖:
下拉重新整理:
鬆開重新整理中:
重新整理完成,更新資料:
重新整理資料以官方提供的資料為準,若官方無更新,列表內容跟沒重新整理之前一樣,因為資料來自官方提供