10.下拉刷新、加载更多、标记已读、轮播条、缓存

2022-01-12 13:31:46 浏览数 (1)

下拉刷新、加载更多、标记已读、轮播条、缓存

下拉刷新-------

1.addHeaderView必须在setAdapter之前调用

2.将paddingTop设置一个headerView高度的负值去隐藏它

getHeight()和getMeasuredHeight()的区别:

getMeasuredHeight():获取测量完的高度,只要在onMeasure方法执行完,就可以用

它获取到宽高,在自定义控件内部多使用这个

使用view.measure(0,0)方法可以主动通知系统去测量,然后就

可以直接使用它获取宽高

getHeight():必须在onLayout方法执行完后,才能获得宽高

view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

@Override

public void onGlobalLayout() {

headerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);

int headerViewHeight = headerView.getHeight();

//直接可以获取宽高

}

});

3.setSelection(position);将对应位置的item放置到屏幕顶端

下拉刷新的原理:

1.自定义带下拉刷新,加载更多的listview

代码语言:javascript复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* public class RefreshListView extends ListView implements OnScrollListener,		android.widget.AdapterView.OnItemClickListener {	private static final int STATE_PULL_REFRESH = 0;// 下拉刷新	private static final int STATE_RELEASE_REFRESH = 1;// 松开刷新	private static final int STATE_REFRESHING = 2;// 正在刷新	private View mHeaderView;	private int startY = -1;// 滑动起点的y坐标	private int mHeaderViewHeight;	private int mCurrrentState = STATE_PULL_REFRESH;// 当前状态	private TextView tvTitle;	private TextView tvTime;	private ImageView ivArrow;	private ProgressBar pbProgress;	private RotateAnimation animUp;	private RotateAnimation animDown;	public RefreshListView(Context context, AttributeSet attrs, int defStyle) {		super(context, attrs, defStyle);		initHeaderView();		initFooterView();	}	public RefreshListView(Context context, AttributeSet attrs) {		super(context, attrs);		initHeaderView();		initFooterView();	}	public RefreshListView(Context context) {		super(context);		initHeaderView();		initFooterView();	}	/**	 * 初始化头布局	 */	private void initHeaderView() {		mHeaderView = View.inflate(getContext(), R.layout.refresh_header, null);		this.addHeaderView(mHeaderView);		tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title);		tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time);		ivArrow = (ImageView) mHeaderView.findViewById(R.id.iv_arr);		pbProgress = (ProgressBar) mHeaderView.findViewById(R.id.pb_progress);		mHeaderView.measure(0, 0);//测量之后该view的getMeasuredHeight()就会返回刚才测量所得的高,getMeasuredWidth返回测量所得宽		mHeaderViewHeight = mHeaderView.getMeasuredHeight();		mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏头布局		initArrowAnim();		tvTime.setText("最后刷新时间:"   getCurrentTime());	}	/*	 * 初始化脚布局	 */	private void initFooterView() {		mFooterView = View.inflate(getContext(),				R.layout.refresh_listview_footer, null);		this.addFooterView(mFooterView);		mFooterView.measure(0, 0);		mFooterViewHeight = mFooterView.getMeasuredHeight();		mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏		this.setOnScrollListener(this);//加载更多需要实现OnScrollListener接口	}	@Override	public boolean onTouchEvent(MotionEvent ev) {//下拉刷新需要重写这个方法:触摸事件		switch (ev.getAction()) {		case MotionEvent.ACTION_DOWN:			startY = (int) ev.getRawY();			break;		case MotionEvent.ACTION_MOVE:			if (startY == -1) {// 确保startY有效,有时候ACTION_DOWN事件不会调用,需要重新掉一次				startY = (int) ev.getRawY();			}			if (mCurrrentState == STATE_REFRESHING) {// 正在刷新时不做处理				break;			}			int endY = (int) ev.getRawY();			int dy = endY - startY;// 移动便宜量			if (dy > 0 && getFirstVisiblePosition() == 0) {// 只有下拉并且当前是第一个item,才允许下拉				int padding = dy - mHeaderViewHeight;// 计算padding				mHeaderView.setPadding(0, padding, 0, 0);// 设置当前padding				if (padding > 0 && mCurrrentState != STATE_RELEASE_REFRESH) {// 状态改为松开刷新,头布局全部出来了					mCurrrentState = STATE_RELEASE_REFRESH;					refreshState();				} else if (padding < 0 && mCurrrentState != STATE_PULL_REFRESH) {// 改为下拉刷新状态					mCurrrentState = STATE_PULL_REFRESH;					refreshState();				}				return true;			}			break;		case MotionEvent.ACTION_UP:			startY = -1;// 重置			if (mCurrrentState == STATE_RELEASE_REFRESH) {				mCurrrentState = STATE_REFRESHING;// 正在刷新				mHeaderView.setPadding(0, 0, 0, 0);// 显示				refreshState();			} else if (mCurrrentState == STATE_PULL_REFRESH) {				mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏			}			break;		default:			break;		}		return super.onTouchEvent(ev);	}	/**	 * 刷新下拉控件的布局	 */	private void refreshState() {		switch (mCurrrentState) {		case STATE_PULL_REFRESH:			tvTitle.setText("下拉刷新");			ivArrow.setVisibility(View.VISIBLE);			pbProgress.setVisibility(View.INVISIBLE);			ivArrow.startAnimation(animDown);//箭头向下			break;		case STATE_RELEASE_REFRESH:			tvTitle.setText("松开刷新");			ivArrow.setVisibility(View.VISIBLE);			pbProgress.setVisibility(View.INVISIBLE);			ivArrow.startAnimation(animUp);			break;		case STATE_REFRESHING:			tvTitle.setText("正在刷新...");			ivArrow.clearAnimation();// 必须先清除动画,才能隐藏			ivArrow.setVisibility(View.INVISIBLE);			pbProgress.setVisibility(View.VISIBLE);			if (mListener != null) {				mListener.onRefresh();//不等于空时才刷新数据			}			break;		default:			break;		}	}	/**	 * 初始化箭头动画	 */	private void initArrowAnim() {		// 箭头向上动画		animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,				Animation.RELATIVE_TO_SELF, 0.5f);		animUp.setDuration(200);		animUp.setFillAfter(true);		// 箭头向下动画		animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,				0.5f, Animation.RELATIVE_TO_SELF, 0.5f);		animDown.setDuration(200);		animDown.setFillAfter(true);	}	OnRefreshListener mListener;	private View mFooterView;	private int mFooterViewHeight;	public void setOnRefreshListener(OnRefreshListener listener) {		mListener = listener;	}	public interface OnRefreshListener {//定义个接口		public void onRefresh();		public void onLoadMore();// 加载下一页数据	}	/*	 * 收起下拉刷新的控件	 */	public void onRefreshComplete(boolean success) {//加了个参数,成功才更新时间		if (isLoadingMore) {// 正在加载更多...如果是加载下一页			mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏脚布局,			isLoadingMore = false;		} else {			mCurrrentState = STATE_PULL_REFRESH;			tvTitle.setText("下拉刷新");			ivArrow.setVisibility(View.VISIBLE);			pbProgress.setVisibility(View.INVISIBLE);			mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏			if (success) {				tvTime.setText("最后刷新时间:"   getCurrentTime());			}		}	}	/**	 * 获取当前时间	 */	public String getCurrentTime() {		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");		return format.format(new Date());	}//定义个标记,要不然一直掉这个到底了,判断现在是否加载了	private boolean isLoadingMore;	@Override	public void onScrollStateChanged(AbsListView view, int scrollState) {		if (scrollState == SCROLL_STATE_IDLE				|| scrollState == SCROLL_STATE_FLING) {//空闲和快速滑状态			if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {// 滑动到最后,默认是FALSE,然后置为TRUE				System.out.println("到底了.....");				mFooterView.setPadding(0, 0, 0, 0);// 显示				setSelection(getCount() - 1);// 改变listview显示位置				isLoadingMore = true;//致为TRUE,下次进来不调用了,可是如果还有数据,什么时候致为FALSE了?在onRefreshComplete方法里,加载结束后				if (mListener != null) {					mListener.onLoadMore();// 在这里调用这个方法:加载下一页数据				}			}		}	}	@Override	public void onScroll(AbsListView view, int firstVisibleItem,			int visibleItemCount, int totalItemCount) {	}	OnItemClickListener mItemClickListener;	@Override	public void setOnItemClickListener(//重写listview的setOnItemClickListener方法			android.widget.AdapterView.OnItemClickListener listener) {		super.setOnItemClickListener(this);//改成this,并且实现OnItemClickListener 接口		mItemClickListener = listener;	}	@Override	public void onItemClick(AdapterView<?> parent, View view, int position,			long id) {		if (mItemClickListener != null) {//这样就调用地方的点击条目就变成了减去头view后重新开始的position了			mItemClickListener.onItemClick(parent, view, position					- getHeaderViewsCount(), id);		}	}}
*/

2.头布局

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="horizontal" >    <FrameLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:padding="10dp" >        <ImageView            android:id="@ id/iv_arr"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center"            android:src="@drawable/common_listview_headview_red_arrow" />        <ProgressBar            android:id="@ id/pb_progress"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center"            android:indeterminateDrawable="@drawable/custom_progress"            android:visibility="invisible" />    </FrameLayout>    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_gravity="center"        android:gravity="center"        android:orientation="vertical" >        <TextView            android:id="@ id/tv_title"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="下拉刷新"            android:textColor="#f00"            android:textSize="16sp" />        <TextView            android:id="@ id/tv_time"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_marginTop="5dp"            android:text="最后刷新时间:2015-03-10 17:07:07"            android:textColor="@android:color/darker_gray"            android:textSize="14sp" />    </LinearLayout></LinearLayout>

custom_progress:改变ProgressBar的样子,这里在drawable里自定义了一个圆圈

代码语言:javascript复制
<rotate xmlns:android="http://schemas.android.com/apk/res/android"    android:fromDegrees="0"    android:pivotX="50%"    android:pivotY="50%"    android:toDegrees="360" >    <shape        android:innerRadius="12dp"//圈内半径        android:shape="ring"//圆环        android:thickness="3dp"//圆圈厚度        android:useLevel="false" >        <gradient            android:centerColor="#3f00"//开始,粉色,3是透明度            android:endColor="#f00"//中间            android:startColor="#fff" />//结束,循环转圈    </shape></rotate>

3.脚布局

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:gravity="center"    android:orientation="horizontal" >    <ProgressBar        android:id="@ id/pb_pull_list_header"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout_margin="5dp"        android:indeterminateDrawable="@drawable/custom_progress" />    <TextView        android:id="@ id/tv_pull_list_header_title"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="加载中..."        android:textColor="#ff0000"        android:textSize="18sp" /></LinearLayout>

4.在页签详情页中调用自定义listview的方法

代码语言:javascript复制
public class TabDetailPager extends BaseMenuDetailPager implements		OnPageChangeListener {	。。。	private String mMoreUrl;// 更多页面的地址,数据里有的话才加载下一页
	@Override	public View initViews() {		View view = View.inflate(mActivity, R.layout.tab_detail_pager, null);		// 加载头布局		View headerView = View.inflate(mActivity, R.layout.list_header_topnews,				null);		ViewUtils.inject(this, view);		ViewUtils.inject(this, headerView);		// 将头条新闻以头布局的形式加给listview		lvList.addHeaderView(headerView);		// 设置下拉刷新监听		lvList.setOnRefreshListener(new OnRefreshListener() {			@Override			public void onRefresh() {				getDataFromServer();			}			@Override			public void onLoadMore() {				if (mMoreUrl != null) {					getMoreDataFromServer();//调用more接口				} else {					Toast.makeText(mActivity, "最后一页了", Toast.LENGTH_SHORT)							.show();// 收起加载更多的布局,传false和TRUE一样,只不过是是否更新更新时间的textview
					lvList.onRefreshComplete(false);				}			}		});		lvList.setOnItemClickListener(new OnItemClickListener() {			@Override			public void onItemClick(AdapterView<?> parent, View view,					int position, long id) {				System.out.println("被点击:"   position);				// 35311,34221,34234,34342				// 在本地记录已读状态				String ids = PrefUtils.getString(mActivity, "read_ids", "");				String readId = mNewsList.get(position).id;				if (!ids.contains(readId)) {					ids = ids   readId   ",";					PrefUtils.setString(mActivity, "read_ids", ids);				}				// mNewsAdapter.notifyDataSetChanged();//这样写也可以,不过重新调用getview方法// 实现局部界面刷新, 这个view就是被点击的item布局对象,注意这是个好方法
				changeReadState(view);				// 跳转新闻详情页				Intent intent = new Intent();				intent.setClass(mActivity, NewsDetailActivity.class);				intent.putExtra("url", mNewsList.get(position).url);				mActivity.startActivity(intent);			}		});		return view;	}	/**	 * 改变已读新闻的颜色	 */	private void changeReadState(View view) {		TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);		tvTitle.setTextColor(Color.GRAY);	}	@Override	public void initData() {		String cache = CacheUtils.getCache(mUrl, mActivity);//读取缓存		if (!TextUtils.isEmpty(cache)) {			parseData(cache, false);		}		getDataFromServer();//不管用没有缓存都请求下服务器,看有没有最新的	}	private void getDataFromServer() {		HttpUtils utils = new HttpUtils();		utils.send(HttpMethod.GET, mUrl, new RequestCallBack<String>() {			@Override			public void onSuccess(ResponseInfo<String> responseInfo) {				String result = (String) responseInfo.result;				System.out.println("页签详情页返回结果:"   result);				parseData(result, false);				lvList.onRefreshComplete(true);				// 设置缓存				CacheUtils.setCache(mUrl, result, mActivity);			}			@Override			public void onFailure(HttpException error, String msg) {				Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();				error.printStackTrace();				lvList.onRefreshComplete(false)			}		});	}	/**	 * 加载下一页数据	 */	private void getMoreDataFromServer() {		HttpUtils utils = new HttpUtils();		utils.send(HttpMethod.GET, mMoreUrl, new RequestCallBack<String>() {			@Override			public void onSuccess(ResponseInfo<String> responseInfo) {				String result = (String) responseInfo.result;				parseData(result, true);				lvList.onRefreshComplete(true);			}			@Override			public void onFailure(HttpException error, String msg) {				Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();				error.printStackTrace();				lvList.onRefreshComplete(false);			}		});	}	protected void parseData(String result, boolean isMore) {		Gson gson = new Gson();		mTabDetailData = gson.fromJson(result, TabData.class);		System.out.println("页签详情解析:"   mTabDetailData);		// 处理下一页链接		String more = mTabDetailData.data.more;		if (!TextUtils.isEmpty(more)) {			mMoreUrl = GlobalContants.SERVER_URL   more;		} else {			mMoreUrl = null;		}		if (!isMore) {			mTopNewsList = mTabDetailData.data.topnews;			mNewsList = mTabDetailData.data.news;			if (mTopNewsList != null) {				mViewPager.setAdapter(new TopNewsAdapter());				mIndicator.setViewPager(mViewPager);				mIndicator.setSnap(true);// 支持快照显示				mIndicator.setOnPageChangeListener(this);				mIndicator.onPageSelected(0);// 让指示器重新定位到第一个点				tvTitle.setText(mTopNewsList.get(0).title);			}			if (mNewsList != null) {				mNewsAdapter = new NewsAdapter();				lvList.setAdapter(mNewsAdapter);			}			// 自动轮播条显示			if (mHandler == null) {				mHandler = new Handler() {					public void handleMessage(android.os.Message msg) {						int currentItem = mViewPager.getCurrentItem();						if (currentItem < mTopNewsList.size() - 1) {							currentItem  ;						} else {							currentItem = 0;						}						mViewPager.setCurrentItem(currentItem);// 切换到下一个页面// 继续延时3秒发消息,形成循环,可以handleMessage方法里发送消息的						mHandler.sendEmptyMessageDelayed(0, 3000);					};				};				mHandler.sendEmptyMessageDelayed(0, 3000);// 延时3秒后发消息			}		} else {// 如果是加载下一页,需要将数据追加给原来的集合			ArrayList<TabNewsData> news = mTabDetailData.data.news;			mNewsList.addAll(news);//这个方法将数据追加给原来的list			mNewsAdapter.notifyDataSetChanged();		}	}	/**	 * 头条新闻适配器	 * 	 * @author Kevin	 * 	 */	class TopNewsAdapter extends PagerAdapter {		private BitmapUtils utils;		public TopNewsAdapter() {			utils = new BitmapUtils(mActivity);			utils.configDefaultLoadingImage(R.drawable.topnews_item_default);// 设置默认图片		}		@Override		public int getCount() {			return mTabDetailData.data.topnews.size();		}		@Override		public boolean isViewFromObject(View arg0, Object arg1) {			return arg0 == arg1;		}		@Override		public Object instantiateItem(ViewGroup container, int position) {			ImageView image = new ImageView(mActivity);			image.setScaleType(ScaleType.FIT_XY);// 基于控件大小填充图片			TopNewsData topNewsData = mTopNewsList.get(position);			utils.display(image, topNewsData.topimage);// 传递imagView对象和图片地址			container.addView(image);			image.setOnTouchListener(new TopNewsTouchListener());//设置触摸监听			return image;		}		@Override		public void destroyItem(ViewGroup container, int position, Object object) {			container.removeView((View) object);		}	}	/**	 * 头条新闻的触摸监听	 * 	 * @author Kevin	 * 	 */	class TopNewsTouchListener implements OnTouchListener {		@Override		public boolean onTouch(View v, MotionEvent event) {			switch (event.getAction()) {			case MotionEvent.ACTION_DOWN:				System.out.println("按下");// 删除Handler中的所有消息,Callbacks是说有个postDelayed,传的是Runnable,这么这个方法每几秒执行一次				mHandler.removeCallbacksAndMessages(null);				// mHandler.postDelayed(new Runnable() {				//				// @Override				// public void run() {				//				// }				// }, 3000);				break;			case MotionEvent.ACTION_CANCEL://因为当按下没抬起,而是滑了一下,那么事件就取消了,需要重新发送一下
				System.out.println("事件取消");				mHandler.sendEmptyMessageDelayed(0, 3000);				break;			case MotionEvent.ACTION_UP:				System.out.println("抬起");				mHandler.sendEmptyMessageDelayed(0, 3000);				break;			default:				break;			}			return true;		}	}	/**	 * 新闻列表的适配器	 * 	 * @author Kevin	 * 	 */	class NewsAdapter extends BaseAdapter {		private BitmapUtils utils;		public NewsAdapter() {			utils = new BitmapUtils(mActivity);			utils.configDefaultLoadingImage(R.drawable.pic_item_list_default);		}		@Override		public int getCount() {			return mNewsList.size();		}		@Override		public TabNewsData getItem(int position) {			return mNewsList.get(position);		}		@Override		public long getItemId(int position) {			return position;		}		@Override		public View getView(int position, View convertView, ViewGroup parent) {			ViewHolder holder;			if (convertView == null) {				convertView = View.inflate(mActivity, R.layout.list_news_item,						null);				holder = new ViewHolder();				holder.ivPic = (ImageView) convertView						.findViewById(R.id.iv_pic);				holder.tvTitle = (TextView) convertView						.findViewById(R.id.tv_title);				holder.tvDate = (TextView) convertView						.findViewById(R.id.tv_date);				convertView.setTag(holder);			} else {				holder = (ViewHolder) convertView.getTag();			}			TabNewsData item = getItem(position);			holder.tvTitle.setText(item.title);			holder.tvDate.setText(item.pubdate);			utils.display(holder.ivPic, item.listimage);			String ids = PrefUtils.getString(mActivity, "read_ids", "");//保存缓存			if (ids.contains(getItem(position).id)) {				holder.tvTitle.setTextColor(Color.GRAY);			} else {				holder.tvTitle.setTextColor(Color.BLACK);			}			return convertView;		}	}	static class ViewHolder {		public TextView tvTitle;		public TextView tvDate;		public ImageView ivPic;	}	@Override	public void onPageScrollStateChanged(int arg0) {	}	@Override	public void onPageScrolled(int arg0, float arg1, int arg2) {	}	@Override	public void onPageSelected(int arg0) {		TopNewsData topNewsData = mTopNewsList.get(arg0);		tvTitle.setText(topNewsData.title);	}}

来自为知笔记(Wiz)

0 人点赞