前面已经把活动和服务讲了讲,要注意的是服务的用法,我们在这里是 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()); } }
效果图:
下拉刷新:
松开刷新中:
刷新完成,更新数据:
刷新数据以官方提供的数据为准,若官方无更新,列表内容跟没刷新之前一样,因为数据来自官方提供