下拉菜单的实现:基本原理是把一些东西隐藏起来,下来时才显示,要设置一个padding属性,负值表示往里面收,也就是隐藏,然后在初始化数据时需要记录位置信息
只有dy小于0,并且listview显示的是第一个item时,才刷新数据
底部自动加载的实现:不能再new adapter,这样之前的数据就没有了,而是应该通知adapter
主要原理为监听触摸和滑动操作,在listview头部加载一个视图。那要做的其实很简单:
1.写好加载到listview头部的view
2.重写listview,实现onTouchEvent方法和onScroll方法,监听滑动状态。
3.计算headview全部显示出来即可实行加载动作,加载完成即刷新列表。
4.重新隐藏headview
第一步:下拉刷新和自动加载,本质上都是对listview的重加载,所以可以写到一个类里面RefreshListView,来实现重加载时对listview的初始化,之前TabDetailPager里的listview的声明什么的都要改成RefreshListView了
public class RefreshListView extends ListView {
public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public RefreshListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
}
出了一个bug,忘了改布局里的类型,不是listview了
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.mynewsapp.view.RefreshListView
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:cacheColorHint="#FFF"
android:id="@+id/lv_tab_detail_news"/>
</LinearLayout>
下拉刷新的时候,等于是把隐藏的view给显示出来,他有3个状态,没要拉到指定位置时显示下拉刷新,拉到底显示释放刷新,释放后,显示正在刷新,整个过程中都要显示上次刷新时间,如图
他应该有一个单独布局,这其中有一个不显示进度条的圆形进度条,需要在drawable里利用动画,rotate的shape节点定义其属性
<?xml version="1.0" encoding="utf-8"?>
<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"
android:endColor="#f00"
android:startColor="#fff" />
</shape>
</rotate>
<?xml version="1.0" encoding="utf-8"?>
<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_refreshhead_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_refreshhead_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_refreshhead_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#f00"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_refreshhead_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>
第二步:需要一个callback,当发生下拉或者到底时,能够调用两个方法刷新 关于callback,这里讲的很透彻
http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html
这个callback此处就应该是监听了
注意一个语法问题,接口即默认是抽象,无需再添上abstract了(普通的方法加abstract表明这个必须要被实现,接口里的方法默认必须全部实现)
//注册
public void setOnRefreshListner(onRefreshListener listener){
mlistener= listener;
}
//callback
public interface onRefreshListener{
public void onRefesh();
public void onLoadmore();
}
这两个方法的具体该怎么操作在tab里完成,回头再说
关于下拉刷新
关于自动刷新
public class RefreshListView extends ListView {
private int mHeaderViewHeight;
int rawstartY ;
int rawendY ;
private View mHeaderView;
private ImageView iv_refreshhead_arr;
private ProgressBar pb_refreshhead_progress;
private TextView tv_refreshhead_title;
private TextView tv_refreshhead_time;
private static final int STATE_PULL_REFRESH = 0;// 下拉刷新
private static final int STATE_RELEASE_REFRESH = 1;// 松开刷新
private static final int STATE_REFRESHING = 2;// 正在刷新
private int mCurrrentState = STATE_PULL_REFRESH;// 当前状态
private View mFooterView;
private int mFooterViewHeight;
onRefreshListener mlistener;
public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
initHeaderView();
initFooterView();
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
initHeaderView();
initFooterView();
}
public RefreshListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
initHeaderView();
initFooterView();
}
private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.refresh_header, null);
//下拉时,实际上是往list的顶部加一个view
this.addHeaderView(mHeaderView);
mHeaderView.measure(0, 0);//测量控件高度,0,0为默认参数
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏头布局
iv_refreshhead_arr = (ImageView)mHeaderView.findViewById(R.id.iv_refreshhead_arr);
pb_refreshhead_progress = (ProgressBar) mHeaderView.findViewById(R.id.pb_refreshhead_progress);
tv_refreshhead_title = (TextView) mHeaderView.findViewById(R.id.tv_refreshhead_title);
tv_refreshhead_time = (TextView) mHeaderView.findViewById(R.id.tv_refreshhead_time);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
rawstartY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (rawstartY == -1) {// 确保startY有效
rawstartY = (int) ev.getRawY();
}
rawendY = (int) ev.getRawY();
int dy = rawendY-rawstartY;
System.out.println("RefreshListView.onTouchEvent()"+dy+":"+getFirstVisiblePosition());
if (dy>0 && getFirstVisiblePosition()==0) {
int padding = dy-mHeaderViewHeight ; //-(mHeaderViewHeight-dy);
System.out.println("RefreshListView.onTouchEvent() setPadding"+padding);
if (mCurrrentState==STATE_REFRESHING) {//如果发现正在刷新的时候,就不做判断
break;
}
mHeaderView.setPadding(0, padding, 0, 0);
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;//设置完之后需要return,已消费。避免其他事件处理
}
break;
case MotionEvent.ACTION_UP:
rawstartY = -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:
tv_refreshhead_title.setText("下拉刷新");
iv_refreshhead_arr.setVisibility(View.VISIBLE);
pb_refreshhead_progress.setVisibility(View.INVISIBLE);
break;
case STATE_RELEASE_REFRESH:
tv_refreshhead_title.setText("释放刷新");
iv_refreshhead_arr.setVisibility(View.VISIBLE);
pb_refreshhead_progress.setVisibility(View.INVISIBLE);
break;
case STATE_REFRESHING:
tv_refreshhead_title.setText("正在刷新");
iv_refreshhead_arr.setVisibility(View.INVISIBLE);
pb_refreshhead_progress.setVisibility(View.VISIBLE);
if (mlistener!=null) {
mlistener.onRefesh();
}
break;
default:
break;
}
}
public String getCurrentTime() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
return time ;
}
private void initFooterView() {
mFooterView = View.inflate(getContext(), R.layout.refresh_listview_footer, null);
this.addFooterView(mFooterView); //加到listview的最后面
mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getMeasuredHeight();
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏脚布局
this.setOnScrollListener(new MyOnScrollListener());
}
private boolean isLoadingMore = false;
class MyOnScrollListener implements OnScrollListener{
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if((scrollState== SCROLL_STATE_IDLE ||scrollState== SCROLL_STATE_FLING )&& !isLoadingMore){
if (getLastVisiblePosition()==getCount()-1) {//滑动到最后的位置
System.out
.println("RefreshListView.MyOnScrollListener.onScrollStateChanged() 到底部了");
mFooterView.setPadding(0, 0, 0, 0);// 显示
setSelection(getCount()-1);
isLoadingMore=true;
if (mlistener!=null) {
mlistener.onLoadmore();
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
}
public void onRefreshComplete() {
if (isLoadingMore) {
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏
isLoadingMore=false;
}else {
mCurrrentState = STATE_PULL_REFRESH;
tv_refreshhead_title.setText("下拉刷新");
iv_refreshhead_arr.setVisibility(View.VISIBLE);
pb_refreshhead_progress.setVisibility(View.INVISIBLE);
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏
}
}
//注册
public void setOnRefreshListner(onRefreshListener listener){
mlistener= listener;
}
//callback
public interface onRefreshListener{
public void onRefesh();
public void onLoadmore();
}
}
tab里的调用
lv_tab_detail_news.setOnRefreshListner(new onRefreshListener() {
@Override
public void onRefesh() {
// TODO Auto-generated method stub
getDataFromServer();
}
@Override
public void onLoadmore() {
if (mMoreUrl!=null) {
getMoreDataFromServer();
}
else {
Toast.makeText(mActivity, "没有更多新闻了,休息一下", 0).show();
lv_tab_detail_news.onRefreshComplete();
}
}
});
//返回整个listview
return v;
}
protected 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;
System.out.println("获取到的json数据" + result);
parseData(result, true);
lv_tab_detail_news.onRefreshComplete();
}
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();
error.printStackTrace();
lv_tab_detail_news.onRefreshComplete();
}
});
}
注意获取数据时要调用complete
lv_tab_detail_news.setOnRefreshListner(new onRefreshListener() {
@Override
public void onRefesh() {
// TODO Auto-generated method stub
getDataFromServer();
}
@Override
public void onLoadmore() {
if (mMoreUrl!=null) {
getMoreDataFromServer();
}
else {
Toast.makeText(mActivity, "没有更多新闻了,休息一下", 0).show();
lv_tab_detail_news.onRefreshComplete();
}
}
});
//返回整个listview
return v;
}
protected 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;
System.out.println("获取到的json数据" + result);
parseData(result, true);
lv_tab_detail_news.onRefreshComplete();
}
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();
error.printStackTrace();
lv_tab_detail_news.onRefreshComplete();
}
});
}
@Override
public void initData() {
//初始化数据获取服务器的数据
super.initData();
getDataFromServer();
}
private void getDataFromServer() {
// TODO Auto-generated method stub
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("获取到的json数据" + result);
parseData(result, false);
//下拉刷新要complete
lv_tab_detail_news.onRefreshComplete();
}
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();
error.printStackTrace();
lv_tab_detail_news.onRefreshComplete();
}
});
}
private void parseData(String result, boolean isLoadMoredata) {
// 拿到数据后,开始解析
Gson gson = new Gson();
mTabDetailData = gson.fromJson(result, TabData.class);
//下拉需要新的URL
mMoreUrl = mTabDetailData.data.more;
if (TextUtils.isEmpty(mMoreUrl) ) {
mMoreUrl=null;
}else {
mMoreUrl = GlobalContants.SERVER_URL+mTabDetailData.data.more;
}
System.out.println(" 解析到的数据为:" + mTabDetailData);
if (!isLoadMoredata) {
mTopNewsList = mTabDetailData.data.topnews;
vp_tab_detail_topnews.setAdapter(new TopNewsAdapter());
vp_tab_detail_topnews.setOnPageChangeListener(new MyOnPageChangeListener());
//indicator的显示
topnews_indicator.setViewPager(vp_tab_detail_topnews);
topnews_indicator.setOnPageChangeListener(new MyOnPageChangeListener());
topnews_indicator.setSnap(true);
//拿到数据之后,现将第一个topnews的title设置到控件上。即默认显示第一条topnews
String tilte0 = mTopNewsList.get(0).title;
tv_tabdetial_topnewstitle.setText(tilte0);
//下面的listview也要设置adapter去填充数据
listnewsData =mTabDetailData.data.news;
MyListViewAdapter listViewAdapter = new MyListViewAdapter();
lv_tab_detail_news.setAdapter(listViewAdapter);
}else {//如果是loadmore,需要先拿到数据,然后将数据追加给之前的list
ArrayList<TabNewsData> morenews =mTabDetailData.data.news;
listnewsData.addAll(morenews);
listViewAdapter.notifyDataSetChanged();
}
}
一个小bug,空指针,在上面声明了adapter,下面又声明了,这就相当于new了一个新的,去掉下面的类型声明
//下面的listview也要设置adapter去填充数据
listnewsData =mTabDetailData.data.news;
listViewAdapter = new MyListViewAdapter();
lv_tab_detail_news.setAdapter(listViewAdapter);
OK~