10.下拉刷新、加载更多、标记已读、轮播条、缓存
下拉刷新、加载更多、标记已读、轮播条、缓存
下拉刷新-------
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
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,然后置为TRUESystem.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.头布局
<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里自定义了一个圆圈
<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.脚布局
<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的方法
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一样,只不过是是否更新更新时间的textviewlvList.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);//这个方法
将数据追加给原来的listmNewsAdapter.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);
}
}