项目中的下拉加载情况和更新

# 页签详情缓存数据


TabDetailPager


在访问网络成功后,把json数据存到sp中,因为有十几个TabDetailPager对象,每个TabDetailPager对象都要缓存自己的数据,且数据要跟每个TabDetailPager对应上,所以用每个TabDetailPager的url作为缓存的key。


代码:
public void onSuccess(ResponseInfo<String> responseInfo) {
System.out.println(mData.title + "访问网络成功: " + responseInfo.result);
// 缓存页签详情数据,需要与每一个页签对应起来,把每个页签的url作为key
CacheUtil.putString(mContext, mData.url, responseInfo.result);
parseJson(responseInfo.result);
}


访问网络之前取缓存数据


代码:
public void initData() {
// 访问网络前,先获取缓存
String cacheJson = CacheUtil.getString(mContext, mData.url, "");
if(!TextUtils.isEmpty(cacheJson)){
// 如果有缓存,就展示界面
parseJson(cacheJson);
}
// 访问页签详情数据
getDataFromServer();
}



# 下拉刷新


步骤:
1、添加下拉刷新头,addHeaderView
2、隐藏下拉刷新头,setPadding
3、处理触摸事件,让下拉刷新头随手指移动,onTouchEvent
4、设置下拉刷新头的状态,下拉刷新、松开刷新、正在刷新
5、对外暴露接口,让外界处理刷新业务
6、提供刷新完成方法,恢复下拉刷新头的状态


代码:
1、添加下拉刷新头
public class RefreshListView extends ListView {
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeader();
}
private void initHeader() {
// 添加下拉刷新头
header = View.inflate(getContext(), R.layout.refresh_header, null);
this.addHeaderView(header);
}
}


2、隐藏下拉刷新头
private void initHeader() {
// 添加下拉刷新头
header = View.inflate(getContext(), R.layout.refresh_header, null);
ViewUtils.inject(this,header);

// 隐藏头布局
// 获取测量高度
header.measure(0, 0);
headerMeasureHeight = header.getMeasuredHeight();
header.setPadding(0, -headerMeasureHeight, 0, 0);
this.addHeaderView(header);
}


3、处理触摸事件
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
// 当按到轮播图控件时,获取不到down事件,在move事件给他赋值
if(downY==-1){
downY = (int) ev.getY();
}
int moveY = (int) ev.getY();
int diffY = moveY - downY;
// 当Listview中轮播图完全展示时,才处理下拉刷新
if(getFirstVisiblePosition()!=0){
break;
}
// 只处理手指从上往下滑动
if(diffY>0){
// 计算头布局的toppadding值
// topadding = 手指移动的距离 - 头布局的高度
int topadding = diffY - headerMeasureHeight;
// 设置头布局的padding,达到移动的效果
header.setPadding(0, topadding, 0, 0);
return true;// 自己消费掉事件
}
break;
case MotionEvent.ACTION_UP:
// 为了不影响下一次down,downY初始化为-1
downY = -1;
break;
default:
break;
}
return super.onTouchEvent(ev);
}


4、设置下拉刷新头的状态

// 只处理手指从上往下滑动
if(diffY>0){
// 计算头布局的toppadding值
// topadding = 手指移动的距离 - 头布局的高度
int topadding = diffY - headerMeasureHeight;
// 根据topadding值是否大于0判断状态切换
if(topadding<0&&CURREN_STATE!=PULLDOWN_STATE){// 头布局没有完全显示,切换到下拉刷新状态
System.out.println("切换到下拉刷新状态");
CURREN_STATE = PULLDOWN_STATE;
switchState(CURREN_STATE);
}
else if(topadding>0&&CURREN_STATE!=RELEASE_STATE){// 头布局已经完全显示,切换到松开刷新状态
System.out.println("切换到松开刷新状态");
CURREN_STATE = RELEASE_STATE;
switchState(CURREN_STATE);
}

// 设置头布局的padding,达到移动的效果
header.setPadding(0, topadding, 0, 0);
return true;// 自己消费掉事件
}

break;
case MotionEvent.ACTION_UP:
// 为了不影响下一次down,downY初始化为-1
downY = -1;

// 手指抬起时,根据当前状态判断是否切换到正在刷新状态
if(CURREN_STATE==PULLDOWN_STATE){
// 当前处于下拉状态,不切到正在刷新,需要把头布局隐藏
header.setPadding(0, -headerMeasureHeight, 0, 0);
}
else if(CURREN_STATE==RELEASE_STATE){
// 当前处于松开状态,需要切到正在刷新,把头布局设置成刚好完全展示
CURREN_STATE = REFRESHING_STATE;
switchState(CURREN_STATE);
header.setPadding(0, 0, 0, 0);
System.out.println("切到正在刷新");
// 当处于下拉刷新状态时,调用外界传进来的监听器的真正的业务
if(mListener!=null){
mListener.onRefreshing();
}
}
break;
* 根据状态更新下拉刷新头界面
private void switchState(int state){
switch (state) {
case PULLDOWN_STATE:
tv_refresh_state.setText("下拉刷新");
iv_refresh_arrow.setVisibility(View.VISIBLE);
pb_refresh_progress.setVisibility(View.INVISIBLE);// 不能用gone
iv_refresh_arrow.startAnimation(down);
break;
case RELEASE_STATE:
tv_refresh_state.setText("松开刷新");
iv_refresh_arrow.startAnimation(up);
break;
case REFRESHING_STATE:
// 清除箭头的动画
iv_refresh_arrow.clearAnimation();
tv_refresh_state.setText("正在刷新");
iv_refresh_arrow.setVisibility(View.INVISIBLE);
pb_refresh_progress.setVisibility(View.VISIBLE);
break;

default:
break;
}
}


5、对外暴露接口
// 对外暴露接口
public interface MyrefreshLinstener{
// 下拉刷新方法
void onRefreshing();
}

// 让外界传递监听器
public void setMyrefreshLinstener(MyrefreshLinstener linstener){
this.mListener = linstener;
}
* 当手指抬起时,切换到正在刷新状态时,监听器的回调方法
else if(CURREN_STATE==RELEASE_STATE){
// 当前处于松开状态,需要切到正在刷新,把头布局设置成刚好完全展示
CURREN_STATE = REFRESHING_STATE;
switchState(CURREN_STATE);
header.setPadding(0, 0, 0, 0);
System.out.println("切到正在刷新");
// 当处于下拉刷新状态时,调用外界传进来的监听器的真正的业务
if(mListener!=null){
mListener.onRefreshing();
}
}


6、提供刷新完成方法
public void refreshFinished(boolean success){
tv_refresh_state.setText("下拉刷新");
iv_refresh_arrow.setVisibility(View.VISIBLE);
pb_refresh_progress.setVisibility(View.INVISIBLE);// 不能用gone
CURREN_STATE = PULLDOWN_STATE;
header.setPadding(0, -headerMeasureHeight, 0, 0);
if(success){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tv_refresh_time.setText("最后刷新时间:" + time);
}else{
Toast.makeText(getContext(), "亲,网络出问题了", 0).show();
}
}


* 页签详情界面中实现下拉刷新业务
1、给新闻列表设置下拉刷新监听
代码:
protected View initView() {
View view = View.inflate(mContext, R.layout.tabdetail, null);
ViewUtils.inject(this,view);

// 加载顶部轮播图布局
View topnews = View.inflate(mContext, R.layout.topnews, null);
ViewUtils.inject(this,topnews);

// 把顶部轮播图添加到Listview的头上 
lv_tabdetail_news.addHeaderView(topnews);

// 给自己的下拉刷新Listview设置监听
lv_tabdetail_news.setMyrefreshLinstener(new RefreshLinstener());
return view;
}


2、在RefreshLinstener的onRefreshing方法中访问网络重新获取数据
代码:
public void onRefreshing() {
isRefreshing = true;
// 真正的刷新业务
getDataFromServer();
}
* 访问网络成功或失败都需要调用RefreshListview的刷新完成方法
public void onSuccess(ResponseInfo<String> responseInfo) {
System.out.println(mData.title + "访问网络成功: " + responseInfo.result);
// 缓存页签详情数据,需要与每一个页签对应起来,把每个页签的url作为key
CacheUtil.putString(mContext, mData.url, responseInfo.result);
parseJson(responseInfo.result);
if(isRefreshing){
lv_tabdetail_news.refreshFinished(true);
isRefreshing = false;
}
}


@Override
public void onFailure(HttpException error, String msg) {
System.out.println(mData.title + "访问网络失败");
if(isRefreshing){
lv_tabdetail_news.refreshFinished(false);
isRefreshing = false;
}
}


# 加载更多


步骤:
1、添加加载更多头,addFooterView
2、隐藏加载更多头,setPadding
3、监听ListView滚动状态,当处于停止或惯性停止时,显示加载更多头
4、对外暴露接口,让外界处理加载更多业务
5、提供加载更多完成方法,恢复加载更多头的状态


代码:
1、添加加载更多头
private void initFooter() {
footer = View.inflate(getContext(), R.layout.refresh_footer, null);
this.addFooterView(footer);
}
* 构造方法中调用initFooter
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeader();
iniAnimation();
initFooter();
}


2、隐藏加载更多头
private void initFooter() {
footer = View.inflate(getContext(), R.layout.refresh_footer, null);
// 隐藏脚布局
footer.measure(0, 0);
footerMeasuredHeight = footer.getMeasuredHeight();
footer.setPadding(0, -footerMeasuredHeight, 0, 0);
this.addFooterView(footer);
}


3、监听ListView滚动状态
private void initFooter() {
footer = View.inflate(getContext(), R.layout.refresh_footer, null);
// 隐藏脚布局
footer.measure(0, 0);
footerMeasuredHeight = footer.getMeasuredHeight();
footer.setPadding(0, -footerMeasuredHeight, 0, 0);
this.addFooterView(footer);

// 监听Listview滚动状态
this.setOnScrollListener(new MyOnScrollListener());
}
* MyOnScrollListener的onScrollStateChanged方法
public void onScrollStateChanged(AbsListView view, int scrollState) {
// 当处于停止或惯性停止状态时,且Listview展示的条目最后一条数据,显示加载更多布局
if (OnScrollListener.SCROLL_STATE_IDLE == scrollState
|| OnScrollListener.SCROLL_STATE_FLING == scrollState) {
if(getLastVisiblePosition()==getCount()-1&&!isLoadMore){
isLoadMore = true;
System.out.println("加载更多");
footer.setPadding(0, 0, 0, 0);
// 让加载更多脚布局自动显示出来
setSelection(getCount());
}
}
}


4、对外暴露接口
* 在之前的OnRefreshingListener中添加加载更多的回调方法
public interface MyrefreshLinstener {
// 下拉刷新方法
void onRefreshing();
// 加载更多方法
void onLoadMore();
}
* 当加载更多头显示时,调用MyrefreshLinstener的onLoadMore方法
if (OnScrollListener.SCROLL_STATE_IDLE == scrollState
|| OnScrollListener.SCROLL_STATE_FLING == scrollState) {
if(getLastVisiblePosition()==getCount()-1&&!isLoadMore){
isLoadMore = true;
System.out.println("加载更多");
footer.setPadding(0, 0, 0, 0);
// 让加载更多脚布局自动显示出来
setSelection(getCount());
// 当处于加载更多时,调用外界的监听器的业务
if(mListener!=null){
mListener.onLoadMore();
}
}
}


5、提供加载更多完成方法
public void loadMoreFinished(){
footer.setPadding(0, -footerMeasuredHeight, 0, 0);
isLoadMore = false;
}


* TabDetailPager中加载更多业务实现


加载更多业务是通过看TabDetailBean中的more字段是否有值来判断是否有更多数据,如果有更多数据,需要访问网络把更多的数据拿到,添加到当前ListView中。


1、在RefreshLinstener的onLoadMore方法中访问网络获取更多数据
当没有更多数据时,需要调用Listview的加载更多完成方法
public void onLoadMore() {
// 判断moreUrl是否为空,如果为空是没有更多数据
if (!TextUtils.isEmpty(moreUrl)) {
// 为了访问网络后解析json时,调用加载跟得的代码
isLoadMore = true;
// 加载更多业务
getMoreDataFromServer();
} else {
// 恢复加载更多界面状态
lv_tabdetail_news.loadMoreFinished();
Toast.makeText(mContext, "没有更多数据了,亲", 0).show();
}
}


2、加载更多时访问网络
* 每次解析完json数据后,先获取其中的more字段
protected void parseJson(String result) {
Gson gson = new Gson();
TabDetailBean tabDetailBean = gson
.fromJson(result, TabDetailBean.class);
// 获取加载更多对应url
moreUrl = tabDetailBean.data.more;
//......之前代码省略

* 加载更多方法,加载成功或失败时都需要调用Listview的加载更多完成方法
private void getMoreDataFromServer() {
HttpUtils httpUtils = new HttpUtils();
httpUtils.configDefaultHttpCacheExpiry(0);
httpUtils.send(HttpMethod.GET, ConstantUtil.SERVER_URL + moreUrl,
new RequestCallBack<String>() {


@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
System.out.println(mData.title + "加载更多成功: "
+ responseInfo.result);
parseJson(responseInfo.result);
// 加载更多完成后,需要把isLoadMore再置为false
isLoadMore = false;
lv_tabdetail_news.loadMoreFinished();
}


@Override
public void onFailure(HttpException error, String msg) {
System.out.println(mData.title + "加载更多失败");
// 加载更多完成后,需要把isLoadMore再置为false
isLoadMore = false;
lv_tabdetail_news.loadMoreFinished();
}
});
}


3、解析json数据时,需要根据isLoadMore判断是刷新界面还是加载更多数据
protected void parseJson(String result) {
Gson gson = new Gson();
TabDetailBean tabDetailBean = gson
.fromJson(result, TabDetailBean.class);
// 获取加载更多对应url
moreUrl = tabDetailBean.data.more;
if (!isLoadMore) {
//......之前代码省略
}else {// 是加载更多,把更多的数据,更多的新闻列表,添加到之前新闻列表集合中
newsData.addAll(tabDetailBean.data.news);
adapter.notifyDataSetChanged();
}

}


# 缓存已读新闻


当用户点击某一天新闻后,需要把当前新闻条目变颜色,用来提示用户此新闻已经看过,需要把新闻对应的id保存起来,方便下次展示时改变条目颜色。因为要实现程序关闭后再打开依旧有已读新闻效果,所以把点击过的新闻id保存到sp中。


代码:
1、点击新闻列表条目时,保存当前新闻的id到sp中,然后刷新Listview
* 监听条目点击事件
protected View initView() {
// ......代码省略
// 给新闻列表设置条目点击事件
lv_tabdetail_news.setOnItemClickListener(new MyOnItemClickListener());
return view;
}
* MyOnItemClickListener的onItemClick方法
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// 由于添加了两个头,把position减2
int realPosition = position - 2;
// 点击新闻后,保存当前新闻的id
News news = newsData.get(realPosition);
// 保存新闻id
// 先取之前保存的新闻id
String cacheIds = CacheUtil.getString(mContext, CACHE_READ_NEWSID,
"");
String newsid = String.valueOf(news.id);
String tempId = "";
if (!cacheIds.contains(newsid)) {
tempId = cacheIds + "," + newsid;
CacheUtil.putString(mContext, CACHE_READ_NEWSID,
tempId);
// 通知Listview刷新
adapter.notifyDataSetChanged();
}
}


2、刷新Listview时,在Adapter的getView方法中根据保存的新闻id判断是否改变颜色
public View getView(int position, View convertView, ViewGroup parent) {
// ......代码省略
News news = newsData.get(position);
// 获取保存的已读新闻id,根据当前展示的新闻id判断是否变颜色
String cacheIds = CacheUtil.getString(mContext, CACHE_READ_NEWSID, "");
if(cacheIds.contains(String.valueOf(news.id))){
holder.title.setTextColor(Color.RED);
}else{
holder.title.setTextColor(Color.BLACK);
}
bitmapUtils.display(holder.img, news.listimage);
holder.title.setText(news.title);
holder.time.setText(news.pubdate);
return convertView;
}




# 新闻详情界面


因为新闻详情界面的标题跟主界面的标题类似,把标题抽取出来,用到的地方再include的进来


1、抽取titlebar.xml


2、basepager.xml中引用titlebar
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >
   
   <include layout="@layout/titlebar"/>

   <FrameLayout
       android:id="@+id/fl_basepager_container"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</LinearLayout>


3、新闻详情界面布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical" >

   <include layout="@layout/titlebar" />

   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent" >

       <WebView
           android:id="@+id/wv_newsdetail_web"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

       <ProgressBar
           android:id="@+id/pb_newsdetail_progress"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="center"
           android:indeterminateDrawable="@drawable/custom_progressbar" />
   </FrameLayout>

</LinearLayout>


4、Webview使用
private void initWebView() {
String url = getIntent().getStringExtra("url");
// 配置Webview

settings = wv_newsdetail_web.getSettings();
settings.setBuiltInZoomControls(true);// 支持缩放按钮
settings.setUseWideViewPort(true);// 支持双击缩放
settings.setJavaScriptEnabled(true);// 支持JavaScript

// 监听Webview
wv_newsdetail_web.setWebViewClient(new WebViewClient(){
@Override
public void onPageFinished(WebView view, String url) {
// 网页加载完成后回调
pb_newsdetail_progress.setVisibility(View.GONE);
super.onPageFinished(view, url);
}
});
wv_newsdetail_web.loadUrl(url);
}


5、Xutils注解处理按钮点击事件
@OnClick({R.id.ib_titlebar_back,R.id.ib_titlebar_share,R.id.ib_titlebar_textsize})
public void btnClick(View v){
switch (v.getId()) {
case R.id.ib_titlebar_back:
finish();
break;
case R.id.ib_titlebar_share:

break;
case R.id.ib_titlebar_textsize:
showTextsize();
break;


default:
break;
}
}


6、更改Webview字体大小
private int currentIndex = 2;
private WebSettings settings;
private int tempIndex;
private void showTextsize() {
AlertDialog.Builder builder = new Builder(this);
String[] items = new String[]{"超大号字体","大号字体","正常字体","小号字体","超小号字体"};
builder.setSingleChoiceItems(items, currentIndex, new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
tempIndex = which;
}
});
builder.setPositiveButton("确定", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
currentIndex = tempIndex;
// 根据位置改变字体
switchTextSize(currentIndex);
}
});
builder.setNegativeButton("取消",null);
builder.show();
}
// 根据点击的位置更改文字字体
protected void switchTextSize(int currentIndex) {
switch (currentIndex) {
case 0:
settings.setTextSize(TextSize.LARGEST);
break;
case 1:
settings.setTextSize(TextSize.LARGER);
break;
case 2:
settings.setTextSize(TextSize.NORMAL);
break;
case 3:
settings.setTextSize(TextSize.SMALLER);
break;
case 4:
settings.setTextSize(TextSize.SMALLEST);
break;


default:
break;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值