文章目录
- 1.UI框架梳理
- 2.滑动显示下拉刷新
- 3.通过下拉位置切换刷新状态
- 4.根据当前状态刷新界面
- 5.设置下拉刷新回调监听 & 刷新数据
- 6.更新下拉刷新时间
- 7.添加脚布局并隐藏
- 8.监听滑动到底事件显示加载中布局
- 9.分页加载更多数据
- 10.本地记录已读未读状态
- 11.根据已读未读状态刷新页面
- 12.新闻详情页布局开发
- 13.加载新闻网页 & 显示进度条
1.UI框架梳理
项目开发到现在,基本上已经完成了大体的功能。不过由于开发流程过于繁琐,这里就按照执行顺序专门列出一个小节来整理一下当前UI框架:
- SplashActivity,闪屏页面
- GuideActivity,引导页面,第一次打开时会弹出,后续弹出后会读取PreferencedShared中的数据,便不会再弹出
- MainActivity,主页面,后续的所有页面均在此处开发
- BaseFragment,主碎片,后续碎片均继承这个类
- LeftMenuFragment,侧边栏碎片
- ContentFragment,内容详情碎片
- BasePaper,详情页的基类
- HomePaper,“首页”详情页
- NewsCenterPaper,“新闻中心”详情页
- BaseMenuDetailPaper,侧边栏详情页的基类
- NewsMenuDetailPaper,“新闻”侧边栏详情页
- TabDetailPaper,显示新闻具体内容的页面
- TopNewsViewPaper,显示头条数据具体内容的页面
- RefreshListView,显示下拉刷新效果的页面
- TopicMenuDetailPaper,“专题”侧边栏详情页
- PhotosMenuDetailPaper,“组图”侧边栏详情页
- InteractMenuDetailPaper,“互动”侧边栏详情页
- SmartServicePaper,“智慧服务”详情页
- GovAffairsPaper,“政务”详情页
- SettingPaper,“设置”详情页
- NoScrollViewPaper,禁用侧边栏的详情页
每个单体页面的实现不算很难,难点在于三层ViewPager的嵌套,以及View事件分发时需要让父类不要拦截子类的滑动事件,否则会出现滑动冲突的问题
2.滑动显示下拉刷新
在上一篇博客中我们实现了刷新效果的显示,现在就需要完善下拉刷新的逻辑,即刷新的同时也更新页面的数据
修改RefreshListView,重写onTouchEvent()方法,用于手势判断,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
}
3.通过下拉位置切换刷新状态
我们已经完成了下拉刷新的动画,而一般应用在刷新时会改变文字,我们现在需要实现这个效果
- 修改RefreshListView,重写onTouchEvent()方法,用于判断三种不同的下拉刷新状态,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
}
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
}
- 修改RefreshListView,添加refreshState()方法,用于刷新当前状态,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
}
}
4.根据当前状态刷新界面
当状态可以根据手势判断来进行切换后,现在就需要根据当前状态来刷新界面。
- 修改RefreshListView,完善refreshState()方法,用于刷新当前状态,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.setVisibility(View.INVISIBLE);
break;
default:
break;
}
}
}
- 修改RefreshListView,添加initArrowAnim()方法,用于完善箭头的动画,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.setVisibility(View.INVISIBLE);
break;
default:
break;
}
}
}
- 修改RefreshListView,进一步完善refreshState()方法,用于让箭头改变状态时出现动画,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
break;
default:
break;
}
}
}
5.设置下拉刷新回调监听 & 刷新数据
我们实现了刷新的效果,现在就需要实现在下拉刷新的时候刷新数据。
- 修改RefreshListView,添加OnRefreshListener接口,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
/**
* 监听器回调接口
*/
private OnRefreshListener mListener;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
// 回调下拉刷新
if (mListener != null){
mListener.onRefresh();
}
break;
default:
break;
}
}
public void setOnRefreshListener(OnRefreshListener listener){
mListener = listener;
}
// 回调接口,通知刷新状态
public interface OnRefreshListener{
// 下拉刷新新的回调
public void onRefresh();
}
}
- 修改TabDetailPaper,修改initViews()方法,设置下拉刷新的监听,代码如下:
package com.example.zhbj.base.impl;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.zhbj.R;
import com.example.zhbj.base.BaseMenuDetailPaper;
import com.example.zhbj.domain.NewsMenu;
import com.example.zhbj.domain.NewsTab;
import com.example.zhbj.global.GlobalConstans;
import com.example.zhbj.util.CacheUtil;
import com.example.zhbj.view.RefreshListView;
import com.example.zhbj.view.TopNewsViewPaper;
import com.google.gson.Gson;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.CirclePageIndicator;
import java.util.ArrayList;
/**
* 页签详情页,包含北京、中国、国际等页签
* ViewPagerIndicator
* 1.引入ViewPagerIndicator库
* 2.解决v4冲突,用大的版本覆盖小的版本
* 3.仿照sample中的程序进行拷贝SampleTabsDefault
*/
public class TabDetailPaper extends BaseMenuDetailPaper {
/**
* 当前页签数据
*/
private NewsMenu.NewsTabData newsTabData;
/**
* 文本框对象
*/
// private TextView view;
/**
* ViewPager对象
*/
@ViewInject(R.id.vp_tab_detail)
private TopNewsViewPaper mViewPaper;
/**
* 请求的URL地址
*/
private String mUrl;
/**
* 保存头条新闻图片的集合
*/
private ArrayList<NewsTab.TopNews> mTopNewsList;
/**
* TextView控件实例
*/
@ViewInject(R.id.tv_title2)
private TextView tvTitle;
/**
* Indicator控件实例
*/
@ViewInject(R.id.indicator2)
private CirclePageIndicator mIndicator;
/**
* ListView控件实例
*/
@ViewInject(R.id.lv_list)
private RefreshListView lvList;
/**
* 存储新闻列表的集合
*/
private ArrayList<NewsTab.News> mNewsList;
public TabDetailPaper(Activity activity, NewsMenu.NewsTabData newsTabData) {
super(activity);
this.newsTabData = newsTabData;
mUrl = GlobalConstans.SERVER_URL + newsTabData.url;
}
@Override
public View initViews() {
/*
view = new TextView(mActivity);
view.setTextSize(22);
view.setTextColor(Color.RED);
view.setGravity(Gravity.CENTER); // 居中显示
view.setText("页签");
*/
View view = View.inflate(mActivity,R.layout.paper_tab_detail,null);
// 加载头条新闻的头布局
View headerView = View.inflate(mActivity,R.layout.list_item_header,null);
ViewUtils.inject(this,view);
ViewUtils.inject(this,headerView);
lvList.addHeaderView(headerView); // 给ListView添加头布局
// 设置下拉刷新的监听
lvList.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// 刷新数据
getDataFromServer();
}
});
return view;
}
@Override
public void initData() {
// view.setText(newsTabData.title); // 修改当前布局的数据
String cache = CacheUtil.getCache(mActivity, mUrl);
if (!TextUtils.isEmpty(cache)){
// 有缓存
processData(cache);
}
getDataFromServer();
}
/**
* 从服务器中获取数据
*/
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result);
CacheUtil.setCache(mActivity,mUrl,result);
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
}
});
}
private void processData(String result) {
Gson gson = new Gson();
NewsTab newsTab = gson.fromJson(result, NewsTab.class);
// 初始化头条新闻数据
mTopNewsList = newsTab.data.topnews;
if (mTopNewsList != null){
mViewPaper.setAdapter(new TopNewsAdapter());
mIndicator.setViewPager(mViewPaper); // 将圆形指示器和viewpager绑定
mIndicator.setSnap(true); // 快照展示方式
mIndicator.onPageSelected(0); // 将圆点位置归零,保证圆点和页面同步
mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
// 更新头条新闻的标题
tvTitle.setText(mTopNewsList.get(i).title);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 初始化首页标题
tvTitle.setText(mTopNewsList.get(0).title);
}
/**
* 初始化新闻列表数据
*/
mNewsList = newsTab.data.news;
if (mNewsList != null){
lvList.setAdapter(new NewsAdapter());
}
}
class TopNewsAdapter extends PagerAdapter{
/**
* xUtils中的BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public TopNewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
// 设置加载中的默认图片
mBitmapUtils.configDefaultLoadingImage(R.drawable.pic_item_list_default);
}
@Override
public int getCount() {
return mTopNewsList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = new ImageView(mActivity);
NewsTab.TopNews topNews = mTopNewsList.get(position);
String topimage = topNews.topimage; // 图片的下载链接
view.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放模式:宽高匹配窗体
/**
* 1,根据url下载图片
* 2.将图片设置给ImageView
* 3.将图片作成缓存
* 4.避免内存溢出
* 由于工程量巨大,这里使用xUtils中的BitmapUtils里的api来完成这四个逻辑
*/
mBitmapUtils.display(view,topimage);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
/**
* 新闻列表适配器
*/
class NewsAdapter extends BaseAdapter{
/**
* BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public NewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);
}
@Override
public int getCount() {
return mNewsList.size();
}
@Override
public NewsTab.News 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_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
return convertView;
}
}
static class ViewHolder{
public ImageView ivIcon;
public TextView tvTitle;
public TextView tvTime;
}
}
- 修改RefreshListView,添加onRefreshComplete()方法,表示下拉刷新结束后隐藏组件的逻辑,代码如下:
/**
* 刷新结束,隐藏控件
*/
public void onRefreshComplete(){
// 隐藏控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
// 所有状态初始化
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
mCurrentState = STATE_PULL_TO_REFRESH;
}
- 修改TabDetailPaper,修改getDataFromServer()方法,调用刚才编写的方法,代码如下:
package com.example.zhbj.base.impl;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.zhbj.R;
import com.example.zhbj.base.BaseMenuDetailPaper;
import com.example.zhbj.domain.NewsMenu;
import com.example.zhbj.domain.NewsTab;
import com.example.zhbj.global.GlobalConstans;
import com.example.zhbj.util.CacheUtil;
import com.example.zhbj.view.RefreshListView;
import com.example.zhbj.view.TopNewsViewPaper;
import com.google.gson.Gson;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.CirclePageIndicator;
import java.util.ArrayList;
/**
* 页签详情页,包含北京、中国、国际等页签
* ViewPagerIndicator
* 1.引入ViewPagerIndicator库
* 2.解决v4冲突,用大的版本覆盖小的版本
* 3.仿照sample中的程序进行拷贝SampleTabsDefault
*/
public class TabDetailPaper extends BaseMenuDetailPaper {
/**
* 当前页签数据
*/
private NewsMenu.NewsTabData newsTabData;
/**
* 文本框对象
*/
// private TextView view;
/**
* ViewPager对象
*/
@ViewInject(R.id.vp_tab_detail)
private TopNewsViewPaper mViewPaper;
/**
* 请求的URL地址
*/
private String mUrl;
/**
* 保存头条新闻图片的集合
*/
private ArrayList<NewsTab.TopNews> mTopNewsList;
/**
* TextView控件实例
*/
@ViewInject(R.id.tv_title2)
private TextView tvTitle;
/**
* Indicator控件实例
*/
@ViewInject(R.id.indicator2)
private CirclePageIndicator mIndicator;
/**
* ListView控件实例
*/
@ViewInject(R.id.lv_list)
private RefreshListView lvList;
/**
* 存储新闻列表的集合
*/
private ArrayList<NewsTab.News> mNewsList;
public TabDetailPaper(Activity activity, NewsMenu.NewsTabData newsTabData) {
super(activity);
this.newsTabData = newsTabData;
mUrl = GlobalConstans.SERVER_URL + newsTabData.url;
}
@Override
public View initViews() {
/*
view = new TextView(mActivity);
view.setTextSize(22);
view.setTextColor(Color.RED);
view.setGravity(Gravity.CENTER); // 居中显示
view.setText("页签");
*/
View view = View.inflate(mActivity,R.layout.paper_tab_detail,null);
// 加载头条新闻的头布局
View headerView = View.inflate(mActivity,R.layout.list_item_header,null);
ViewUtils.inject(this,view);
ViewUtils.inject(this,headerView);
lvList.addHeaderView(headerView); // 给ListView添加头布局
// 设置下拉刷新的监听
lvList.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// 刷新数据
getDataFromServer();
}
});
return view;
}
@Override
public void initData() {
// view.setText(newsTabData.title); // 修改当前布局的数据
String cache = CacheUtil.getCache(mActivity, mUrl);
if (!TextUtils.isEmpty(cache)){
// 有缓存
processData(cache);
}
getDataFromServer();
}
/**
* 从服务器中获取数据
*/
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result);
CacheUtil.setCache(mActivity,mUrl,result);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
private void processData(String result) {
Gson gson = new Gson();
NewsTab newsTab = gson.fromJson(result, NewsTab.class);
// 初始化头条新闻数据
mTopNewsList = newsTab.data.topnews;
if (mTopNewsList != null){
mViewPaper.setAdapter(new TopNewsAdapter());
mIndicator.setViewPager(mViewPaper); // 将圆形指示器和viewpager绑定
mIndicator.setSnap(true); // 快照展示方式
mIndicator.onPageSelected(0); // 将圆点位置归零,保证圆点和页面同步
mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
// 更新头条新闻的标题
tvTitle.setText(mTopNewsList.get(i).title);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 初始化首页标题
tvTitle.setText(mTopNewsList.get(0).title);
}
/**
* 初始化新闻列表数据
*/
mNewsList = newsTab.data.news;
if (mNewsList != null){
lvList.setAdapter(new NewsAdapter());
}
}
class TopNewsAdapter extends PagerAdapter{
/**
* xUtils中的BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public TopNewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
// 设置加载中的默认图片
mBitmapUtils.configDefaultLoadingImage(R.drawable.pic_item_list_default);
}
@Override
public int getCount() {
return mTopNewsList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = new ImageView(mActivity);
NewsTab.TopNews topNews = mTopNewsList.get(position);
String topimage = topNews.topimage; // 图片的下载链接
view.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放模式:宽高匹配窗体
/**
* 1,根据url下载图片
* 2.将图片设置给ImageView
* 3.将图片作成缓存
* 4.避免内存溢出
* 由于工程量巨大,这里使用xUtils中的BitmapUtils里的api来完成这四个逻辑
*/
mBitmapUtils.display(view,topimage);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
/**
* 新闻列表适配器
*/
class NewsAdapter extends BaseAdapter{
/**
* BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public NewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);
}
@Override
public int getCount() {
return mNewsList.size();
}
@Override
public NewsTab.News 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_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
return convertView;
}
}
static class ViewHolder{
public ImageView ivIcon;
public TextView tvTitle;
public TextView tvTime;
}
}
6.更新下拉刷新时间
现在数据已经能够刷新了,现在就需要刷新时间。
- 修改RefreshListView,添加setRefreshTime()方法,代码如下:
// 设置刷新时间
private void setRefreshTime(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tvTime.setText(time);
}
- 修改RefreshListView,修改onRefreshComplete()方法,调用上面的方法,并且在初始化时也刷新一次时间,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* HeadView对象
*/
private View mHeadView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
/**
* 监听器回调接口
*/
private OnRefreshListener mListener;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
setRefreshTime();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
// 回调下拉刷新
if (mListener != null){
mListener.onRefresh();
}
break;
default:
break;
}
}
/**
* 刷新结束,隐藏控件
*/
public void onRefreshComplete(){
// 隐藏控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
// 所有状态初始化
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
mCurrentState = STATE_PULL_TO_REFRESH;
// 更新刷新时间
setRefreshTime();
}
public void setOnRefreshListener(OnRefreshListener listener){
mListener = listener;
}
// 回调接口,通知刷新状态
public interface OnRefreshListener{
// 下拉刷新新的回调
public void onRefresh();
}
// 设置刷新时间
private void setRefreshTime(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tvTime.setText(time);
}
}
7.添加脚布局并隐藏
现在添加完刷新逻辑后,就需要添加另一种效果:在下拉列表时会出现类似于刷新分页的效果,借助ListView的api,我们现在开始实现。
- 新建一个布局文件pull_to_refresh_foot.xml,作为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="wrap_content"
android:padding="5dp"
android:gravity="center"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/pb_loading2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/shape_custom_progress"
android:layout_gravity="center"/>
<TextView
android:id="@+id/tv_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载中..."
android:layout_marginLeft="5dp"
android:textColor="#f00"
android:textSize="18sp"/>
</LinearLayout>
- 修改RefreshListView,添加initFooterView()方法,表示初始化脚布局,并且在初始化方法中添加这个api,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView {
/**
* 当前布局的高度
*/
private int mMeasuredHeight;
/**
* 头布局对象
*/
private View mHeadView;
/**
* 脚布局对象
*/
private View mFootView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
/**
* 监听器回调接口
*/
private OnRefreshListener mListener;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
setRefreshTime();
}
/**
* 初始化脚布局
*/
private void initFooterView(){
mFootView = View.inflate(getContext(),R.layout.pull_to_refresh_foot,null);
addFooterView(mFootView);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
bLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
// 回调下拉刷新
if (mListener != null){
mListener.onRefresh();
}
break;
default:
break;
}
}
/**
* 刷新结束,隐藏控件
*/
public void onRefreshComplete(){
// 隐藏控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
// 所有状态初始化
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
mCurrentState = STATE_PULL_TO_REFRESH;
// 更新刷新时间
setRefreshTime();
}
public void setOnRefreshListener(OnRefreshListener listener){
mListener = listener;
}
// 回调接口,通知刷新状态
public interface OnRefreshListener{
// 下拉刷新新的回调
public void onRefresh();
}
// 设置刷新时间
private void setRefreshTime(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tvTime.setText(time);
}
}
8.监听滑动到底事件显示加载中布局
之前添加了脚布局刷新的效果,现在需要实现滑动到底加载数据的逻辑
修改RefreshListView,实现OnScrollListener接口,并且重写onScrollStateChanged()方法中的相应逻辑,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
/**
* 头布局的高度
*/
private int mMeasuredHeight;
/**
* 头布局对象
*/
private View mHeadView;
/**
* 脚布局的高度
*/
private int mMeasuredFootHeight;
/**
* 脚布局对象
*/
private View mFootView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
/**
* 监听器回调接口
*/
private OnRefreshListener mListener;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
setRefreshTime();
}
/**
* 初始化脚布局
*/
private void initFooterView(){
mFootView = View.inflate(getContext(),R.layout.pull_to_refresh_foot,null);
addFooterView(mFootView);
mFootView.measure(0,0);
mMeasuredFootHeight = mFootView.getMeasuredHeight();
// 隐藏脚布局
mFootView.setPadding(0,-mMeasuredFootHeight,0,0);
// 设置滑动监听
setOnScrollListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
// 回调下拉刷新
if (mListener != null){
mListener.onRefresh();
}
break;
default:
break;
}
}
/**
* 刷新结束,隐藏控件
*/
public void onRefreshComplete(){
// 隐藏控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
// 所有状态初始化
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
mCurrentState = STATE_PULL_TO_REFRESH;
// 更新刷新时间
setRefreshTime();
}
public void setOnRefreshListener(OnRefreshListener listener){
mListener = listener;
}
/**
* 滑动状态发生变化
* @param view
* @param scrollState
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE){
// 空闲状态
int lastPosition = getLastVisiblePosition(); // 当前显示item的最后一个位置
if (lastPosition == getCount() - 1){
// 显示加载中布局
mFootView.setPadding(0,0,0,0);
setSelection(getCount() - 1); // 显示在最后一个item的位置(展示加载中布局)
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
// 回调接口,通知刷新状态
public interface OnRefreshListener{
// 下拉刷新新的回调
public void onRefresh();
}
// 设置刷新时间
private void setRefreshTime(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tvTime.setText(time);
}
}
9.分页加载更多数据
之前我们实现了将刷新效果显示到脚布局中,现在就需要通过分页来加载更多数据
- 修改RefreshListView,在OnRefreshListener接口中添加一个方法onLoadMore(),并且完善相应逻辑,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
/**
* 头布局的高度
*/
private int mMeasuredHeight;
/**
* 头布局对象
*/
private View mHeadView;
/**
* 脚布局的高度
*/
private int mMeasuredFootHeight;
/**
* 脚布局对象
*/
private View mFootView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
/**
* 监听器回调接口
*/
private OnRefreshListener mListener;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
setRefreshTime();
}
/**
* 初始化脚布局
*/
private void initFooterView(){
mFootView = View.inflate(getContext(),R.layout.pull_to_refresh_foot,null);
addFooterView(mFootView);
mFootView.measure(0,0);
mMeasuredFootHeight = mFootView.getMeasuredHeight();
// 隐藏脚布局
mFootView.setPadding(0,-mMeasuredFootHeight,0,0);
// 设置滑动监听
setOnScrollListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
// 回调下拉刷新
if (mListener != null){
mListener.onRefresh();
}
break;
default:
break;
}
}
/**
* 刷新结束,隐藏控件
*/
public void onRefreshComplete(){
// 隐藏控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
// 所有状态初始化
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
mCurrentState = STATE_PULL_TO_REFRESH;
// 更新刷新时间
setRefreshTime();
}
public void setOnRefreshListener(OnRefreshListener listener){
mListener = listener;
}
/**
* 滑动状态发生变化
* @param view
* @param scrollState
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE){
// 空闲状态
int lastPosition = getLastVisiblePosition(); // 当前显示item的最后一个位置
if (lastPosition == getCount() - 1){
// 显示加载中布局
mFootView.setPadding(0,0,0,0);
setSelection(getCount() - 1); // 显示在最后一个item的位置(展示加载中布局)
// 加载更多数据
if (mListener != null){
mListener.onLoadMore();
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
// 回调接口,通知刷新状态
public interface OnRefreshListener{
// 下拉刷新新的回调
public void onRefresh();
// 加载更多数据的回调
public void onLoadMore();
}
// 设置刷新时间
private void setRefreshTime(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tvTime.setText(time);
}
}
- 修改TabDetailPaper,在监听器中实现刚刚定义好的方法,代码如下:
package com.example.zhbj.base.impl;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.zhbj.R;
import com.example.zhbj.base.BaseMenuDetailPaper;
import com.example.zhbj.domain.NewsMenu;
import com.example.zhbj.domain.NewsTab;
import com.example.zhbj.global.GlobalConstans;
import com.example.zhbj.util.CacheUtil;
import com.example.zhbj.view.RefreshListView;
import com.example.zhbj.view.TopNewsViewPaper;
import com.google.gson.Gson;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.CirclePageIndicator;
import java.util.ArrayList;
/**
* 页签详情页,包含北京、中国、国际等页签
* ViewPagerIndicator
* 1.引入ViewPagerIndicator库
* 2.解决v4冲突,用大的版本覆盖小的版本
* 3.仿照sample中的程序进行拷贝SampleTabsDefault
*/
public class TabDetailPaper extends BaseMenuDetailPaper {
/**
* 当前页签数据
*/
private NewsMenu.NewsTabData newsTabData;
/**
* 文本框对象
*/
// private TextView view;
/**
* ViewPager对象
*/
@ViewInject(R.id.vp_tab_detail)
private TopNewsViewPaper mViewPaper;
/**
* 请求的URL地址
*/
private String mUrl;
/**
* 保存头条新闻图片的集合
*/
private ArrayList<NewsTab.TopNews> mTopNewsList;
/**
* TextView控件实例
*/
@ViewInject(R.id.tv_title2)
private TextView tvTitle;
/**
* Indicator控件实例
*/
@ViewInject(R.id.indicator2)
private CirclePageIndicator mIndicator;
/**
* ListView控件实例
*/
@ViewInject(R.id.lv_list)
private RefreshListView lvList;
/**
* 存储新闻列表的集合
*/
private ArrayList<NewsTab.News> mNewsList;
public TabDetailPaper(Activity activity, NewsMenu.NewsTabData newsTabData) {
super(activity);
this.newsTabData = newsTabData;
mUrl = GlobalConstans.SERVER_URL + newsTabData.url;
}
@Override
public View initViews() {
/*
view = new TextView(mActivity);
view.setTextSize(22);
view.setTextColor(Color.RED);
view.setGravity(Gravity.CENTER); // 居中显示
view.setText("页签");
*/
View view = View.inflate(mActivity,R.layout.paper_tab_detail,null);
// 加载头条新闻的头布局
View headerView = View.inflate(mActivity,R.layout.list_item_header,null);
ViewUtils.inject(this,view);
ViewUtils.inject(this,headerView);
lvList.addHeaderView(headerView); // 给ListView添加头布局
// 设置下拉刷新的监听
lvList.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// 刷新数据
getDataFromServer();
}
@Override
public void onLoadMore() {
}
});
return view;
}
@Override
public void initData() {
// view.setText(newsTabData.title); // 修改当前布局的数据
String cache = CacheUtil.getCache(mActivity, mUrl);
if (!TextUtils.isEmpty(cache)){
// 有缓存
processData(cache);
}
getDataFromServer();
}
/**
* 从服务器中获取数据
*/
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result);
CacheUtil.setCache(mActivity,mUrl,result);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
private void processData(String result) {
Gson gson = new Gson();
NewsTab newsTab = gson.fromJson(result, NewsTab.class);
// 初始化头条新闻数据
mTopNewsList = newsTab.data.topnews;
if (mTopNewsList != null){
mViewPaper.setAdapter(new TopNewsAdapter());
mIndicator.setViewPager(mViewPaper); // 将圆形指示器和viewpager绑定
mIndicator.setSnap(true); // 快照展示方式
mIndicator.onPageSelected(0); // 将圆点位置归零,保证圆点和页面同步
mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
// 更新头条新闻的标题
tvTitle.setText(mTopNewsList.get(i).title);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 初始化首页标题
tvTitle.setText(mTopNewsList.get(0).title);
}
/**
* 初始化新闻列表数据
*/
mNewsList = newsTab.data.news;
if (mNewsList != null){
lvList.setAdapter(new NewsAdapter());
}
}
class TopNewsAdapter extends PagerAdapter{
/**
* xUtils中的BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public TopNewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
// 设置加载中的默认图片
mBitmapUtils.configDefaultLoadingImage(R.drawable.pic_item_list_default);
}
@Override
public int getCount() {
return mTopNewsList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = new ImageView(mActivity);
NewsTab.TopNews topNews = mTopNewsList.get(position);
String topimage = topNews.topimage; // 图片的下载链接
view.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放模式:宽高匹配窗体
/**
* 1,根据url下载图片
* 2.将图片设置给ImageView
* 3.将图片作成缓存
* 4.避免内存溢出
* 由于工程量巨大,这里使用xUtils中的BitmapUtils里的api来完成这四个逻辑
*/
mBitmapUtils.display(view,topimage);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
/**
* 新闻列表适配器
*/
class NewsAdapter extends BaseAdapter{
/**
* BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public NewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);
}
@Override
public int getCount() {
return mNewsList.size();
}
@Override
public NewsTab.News 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_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
return convertView;
}
}
static class ViewHolder{
public ImageView ivIcon;
public TextView tvTitle;
public TextView tvTime;
}
}
- 修改RefreshListView,添加一个标志位,保证只装载一次数据,并且完善相应逻辑,代码如下:
package com.example.zhbj.view;
import android.content.Context;
import android.media.Image;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.zhbj.R;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 下拉刷新ListView
*/
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
/**
* 头布局的高度
*/
private int mMeasuredHeight;
/**
* 头布局对象
*/
private View mHeadView;
/**
* 脚布局的高度
*/
private int mMeasuredFootHeight;
/**
* 脚布局对象
*/
private View mFootView;
/**
* 起始的Y坐标
*/
private int startY = -1;
/**
* 结束的Y坐标
*/
private int endY;
/**
* Y坐标轴的偏移量
*/
private int dY;
/**
* 下拉刷新状态标志位
*/
private static final int STATE_PULL_TO_REFRESH = 0;
/**
* 松开刷新状态标志位
*/
private static final int STATE_RELEASE_TO_REFRESH = 1;
/**
* 正在刷新状态标志位
*/
private static final int STATE_REFRESHING = 2;
/**
* 当前状态的标志位,默认为松开刷新
*/
private int mCurrentState = STATE_PULL_TO_REFRESH;
/**
* 状态文本组件
*/
private TextView tvState;
/**
* 时间文本组件
*/
private TextView tvTime;
/**
* 箭头图标
*/
private ImageView ivArrow;
/**
* 加载条组件
*/
private ProgressBar pbLoading;
/**
* 箭头向上的动画对象
*/
private RotateAnimation animUp;
/**
* 箭头向下的动画对象
*/
private RotateAnimation animDown;
/**
* 监听器回调接口
*/
private OnRefreshListener mListener;
/**
* 标记是否正在加载更多
*/
private boolean isLoadMore = false;
public RefreshListView(Context context) {
this(context, null);
}
public RefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeaderView();
initFooterView();
}
/**
* 初始化头布局
*/
private void initHeaderView(){
mHeadView = View.inflate(getContext(),R.layout.pull_to_refresh_header,null);
addHeaderView(mHeadView); // 给ListView添加头布局
tvState = (TextView) mHeadView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeadView.findViewById(R.id.tv_time2);
ivArrow = (ImageView) mHeadView.findViewById(R.id.iv_arrow);
pbLoading = (ProgressBar) mHeadView.findViewById(R.id.pb_loading);
/*
隐藏头布局
1.获取当前头布局高度
2.设置负paddingTop,布局就会向上走
*/
// int height = mHeadView.getHeight(); 不能这样获取宽高,因为没有绘制完毕
mHeadView.measure(0,0); // 手动测量,宽高传0表示不参与具体宽高的设定,全由系统底层决定
mMeasuredHeight = mHeadView.getMeasuredHeight(); // 获取测量后的高度
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
initArrowAnim();
setRefreshTime();
}
/**
* 初始化脚布局
*/
private void initFooterView(){
mFootView = View.inflate(getContext(),R.layout.pull_to_refresh_foot,null);
addFooterView(mFootView);
mFootView.measure(0,0);
mMeasuredFootHeight = mFootView.getMeasuredHeight();
// 隐藏脚布局
mFootView.setPadding(0,-mMeasuredFootHeight,0,0);
// 设置滑动监听
setOnScrollListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (startY == -1){
// 没有获取到按下的事件(按住头条新闻滑动时,按下事件被ViewPager消费了)
startY = (int) ev.getY(); // 重新获取起点的位置
}
endY = (int) ev.getY();
dY = endY - startY;
// 如果正在刷新,就什么都不做
if (mCurrentState == STATE_REFRESHING){
break;
}
int firstVisiblePosition = this.getFirstVisiblePosition(); // 当前显示的第一个item的位置
if (dY > 0 && firstVisiblePosition == 0){
// 下拉动作 & 当前在ListView的顶部
int padding = -mMeasuredHeight + dY;
if (padding > 0 && mCurrentState != STATE_RELEASE_TO_REFRESH){
// 切换到松开刷新状态
mCurrentState = STATE_RELEASE_TO_REFRESH;
refreshState();
}else if (padding <= 0 && mCurrentState != STATE_PULL_TO_REFRESH){
// 切换到下拉刷新状态
mCurrentState = STATE_PULL_TO_REFRESH;
refreshState();
}
// 通过修改padding来设置当前刷新控件的最新位置
mHeadView.setPadding(0,padding,0,0);
return true; // 消费此事件,处理下拉刷新控件的滑动,不需要ListView原生效果参与
}
break;
case MotionEvent.ACTION_UP:
startY = -1; // 起始坐标归0
if (mCurrentState == STATE_RELEASE_TO_REFRESH){
// 切换成正在刷新
mCurrentState = STATE_REFRESHING;
// 完整显示刷新控件
mHeadView.setPadding(0,0,0,0);
refreshState();
}else if (mCurrentState == STATE_PULL_TO_REFRESH){
// 隐藏刷新控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
}
break;
default:
break;
}
return super.onTouchEvent(ev); // 要返回super,方便ListView原生滑动处理
}
/**
* 初始化箭头动画
*/
private void initArrowAnim(){
animUp = new RotateAnimation(0,-180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animUp.setDuration(300);
animUp.setFillAfter(true); // 保持住动画结束的状态
animDown = new RotateAnimation(-180,0, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
animDown.setDuration(300);
animDown.setFillAfter(true); // 保持住动画结束的状态
}
/**
* 根据当前状态来刷新界面
*/
private void refreshState() {
switch (mCurrentState){
case STATE_PULL_TO_REFRESH:
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animDown);
break;
case STATE_RELEASE_TO_REFRESH:
tvState.setText("松开刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
ivArrow.setAnimation(animUp);
break;
case STATE_REFRESHING:
tvState.setText("正在刷新");
pbLoading.setVisibility(View.VISIBLE);
ivArrow.clearAnimation(); // 清理动画之后才能隐藏
ivArrow.setVisibility(View.INVISIBLE);
// 回调下拉刷新
if (mListener != null){
mListener.onRefresh();
}
break;
default:
break;
}
}
/**
* 刷新结束,隐藏控件
*/
public void onRefreshComplete(){
// 隐藏控件
mHeadView.setPadding(0,-mMeasuredHeight,0,0);
// 所有状态初始化
tvState.setText("下拉刷新");
pbLoading.setVisibility(View.INVISIBLE);
ivArrow.setVisibility(View.VISIBLE);
mCurrentState = STATE_PULL_TO_REFRESH;
// 更新刷新时间
setRefreshTime();
}
public void setOnRefreshListener(OnRefreshListener listener){
mListener = listener;
}
/**
* 滑动状态发生变化
* @param view
* @param scrollState
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE){
// 空闲状态
int lastPosition = getLastVisiblePosition(); // 当前显示item的最后一个位置
if (lastPosition == getCount() - 1 && !isLoadMore){
// 初始化加载数据标志位
isLoadMore = true;
// 显示加载中布局
mFootView.setPadding(0,0,0,0);
setSelection(getCount() - 1); // 显示在最后一个item的位置(展示加载中布局)
// 加载更多数据
if (mListener != null){
mListener.onLoadMore();
}
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
// 回调接口,通知刷新状态
public interface OnRefreshListener{
// 下拉刷新新的回调
public void onRefresh();
// 加载更多数据的回调
public void onLoadMore();
}
// 设置刷新时间
private void setRefreshTime(){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(new Date());
tvTime.setText(time);
}
}
- 修改TabDetailPaper,实现onLoadMore()方法,读取更多数据,再修改processData()方法,拼接进入到更多信息的URL,代码如下
package com.example.zhbj.base.impl;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.zhbj.R;
import com.example.zhbj.base.BaseMenuDetailPaper;
import com.example.zhbj.domain.NewsMenu;
import com.example.zhbj.domain.NewsTab;
import com.example.zhbj.global.GlobalConstans;
import com.example.zhbj.util.CacheUtil;
import com.example.zhbj.view.RefreshListView;
import com.example.zhbj.view.TopNewsViewPaper;
import com.google.gson.Gson;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.CirclePageIndicator;
import java.util.ArrayList;
/**
* 页签详情页,包含北京、中国、国际等页签
* ViewPagerIndicator
* 1.引入ViewPagerIndicator库
* 2.解决v4冲突,用大的版本覆盖小的版本
* 3.仿照sample中的程序进行拷贝SampleTabsDefault
*/
public class TabDetailPaper extends BaseMenuDetailPaper {
/**
* 当前页签数据
*/
private NewsMenu.NewsTabData newsTabData;
/**
* 文本框对象
*/
// private TextView view;
/**
* ViewPager对象
*/
@ViewInject(R.id.vp_tab_detail)
private TopNewsViewPaper mViewPaper;
/**
* 请求的URL地址
*/
private String mUrl;
/**
* 保存头条新闻图片的集合
*/
private ArrayList<NewsTab.TopNews> mTopNewsList;
/**
* TextView控件实例
*/
@ViewInject(R.id.tv_title2)
private TextView tvTitle;
/**
* Indicator控件实例
*/
@ViewInject(R.id.indicator2)
private CirclePageIndicator mIndicator;
/**
* ListView控件实例
*/
@ViewInject(R.id.lv_list)
private RefreshListView lvList;
/**
* 存储新闻列表的集合
*/
private ArrayList<NewsTab.News> mNewsList;
/**
* 加载更多信息的Url
*/
private String mMoreUrl;
public TabDetailPaper(Activity activity, NewsMenu.NewsTabData newsTabData) {
super(activity);
this.newsTabData = newsTabData;
mUrl = GlobalConstans.SERVER_URL + newsTabData.url;
}
@Override
public View initViews() {
/*
view = new TextView(mActivity);
view.setTextSize(22);
view.setTextColor(Color.RED);
view.setGravity(Gravity.CENTER); // 居中显示
view.setText("页签");
*/
View view = View.inflate(mActivity,R.layout.paper_tab_detail,null);
// 加载头条新闻的头布局
View headerView = View.inflate(mActivity,R.layout.list_item_header,null);
ViewUtils.inject(this,view);
ViewUtils.inject(this,headerView);
lvList.addHeaderView(headerView); // 给ListView添加头布局
// 设置下拉刷新的监听
lvList.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// 刷新数据
getDataFromServer();
}
@Override
public void onLoadMore() {
if (mMoreUrl != null){
}else {
Toast.makeText(mActivity,"没有更多数据啦",Toast.LENGTH_SHORT).show();
}
}
});
return view;
}
@Override
public void initData() {
// view.setText(newsTabData.title); // 修改当前布局的数据
String cache = CacheUtil.getCache(mActivity, mUrl);
if (!TextUtils.isEmpty(cache)){
// 有缓存
processData(cache);
}
getDataFromServer();
}
/**
* 从服务器中获取数据
*/
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result);
CacheUtil.setCache(mActivity,mUrl,result);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
private void processData(String result) {
Gson gson = new Gson();
NewsTab newsTab = gson.fromJson(result, NewsTab.class);
// 获取下一页的数据地址
String more = newsTab.data.more;
if (!TextUtils.isEmpty(more)){
mMoreUrl = GlobalConstans.SERVER_URL + more;
}
else {
mMoreUrl = null;
}
// 初始化头条新闻数据
mTopNewsList = newsTab.data.topnews;
if (mTopNewsList != null){
mViewPaper.setAdapter(new TopNewsAdapter());
mIndicator.setViewPager(mViewPaper); // 将圆形指示器和viewpager绑定
mIndicator.setSnap(true); // 快照展示方式
mIndicator.onPageSelected(0); // 将圆点位置归零,保证圆点和页面同步
mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
// 更新头条新闻的标题
tvTitle.setText(mTopNewsList.get(i).title);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 初始化首页标题
tvTitle.setText(mTopNewsList.get(0).title);
}
/**
* 初始化新闻列表数据
*/
mNewsList = newsTab.data.news;
if (mNewsList != null){
lvList.setAdapter(new NewsAdapter());
}
}
class TopNewsAdapter extends PagerAdapter{
/**
* xUtils中的BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public TopNewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
// 设置加载中的默认图片
mBitmapUtils.configDefaultLoadingImage(R.drawable.pic_item_list_default);
}
@Override
public int getCount() {
return mTopNewsList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = new ImageView(mActivity);
NewsTab.TopNews topNews = mTopNewsList.get(position);
String topimage = topNews.topimage; // 图片的下载链接
view.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放模式:宽高匹配窗体
/**
* 1,根据url下载图片
* 2.将图片设置给ImageView
* 3.将图片作成缓存
* 4.避免内存溢出
* 由于工程量巨大,这里使用xUtils中的BitmapUtils里的api来完成这四个逻辑
*/
mBitmapUtils.display(view,topimage);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
/**
* 新闻列表适配器
*/
class NewsAdapter extends BaseAdapter{
/**
* BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public NewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);
}
@Override
public int getCount() {
return mNewsList.size();
}
@Override
public NewsTab.News 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_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
return convertView;
}
}
static class ViewHolder{
public ImageView ivIcon;
public TextView tvTitle;
public TextView tvTime;
}
}
- 修改TabDetailPaper,增加getMoreDataFromServer()方法,读取更多数据,类似于getDataFromServer()方法,只是url的地址不相同,代码如下:
package com.example.zhbj.base.impl;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.zhbj.R;
import com.example.zhbj.base.BaseMenuDetailPaper;
import com.example.zhbj.domain.NewsMenu;
import com.example.zhbj.domain.NewsTab;
import com.example.zhbj.global.GlobalConstans;
import com.example.zhbj.util.CacheUtil;
import com.example.zhbj.view.RefreshListView;
import com.example.zhbj.view.TopNewsViewPaper;
import com.google.gson.Gson;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.CirclePageIndicator;
import java.util.ArrayList;
/**
* 页签详情页,包含北京、中国、国际等页签
* ViewPagerIndicator
* 1.引入ViewPagerIndicator库
* 2.解决v4冲突,用大的版本覆盖小的版本
* 3.仿照sample中的程序进行拷贝SampleTabsDefault
*/
public class TabDetailPaper extends BaseMenuDetailPaper {
/**
* 当前页签数据
*/
private NewsMenu.NewsTabData newsTabData;
/**
* 文本框对象
*/
// private TextView view;
/**
* ViewPager对象
*/
@ViewInject(R.id.vp_tab_detail)
private TopNewsViewPaper mViewPaper;
/**
* 请求的URL地址
*/
private String mUrl;
/**
* 保存头条新闻图片的集合
*/
private ArrayList<NewsTab.TopNews> mTopNewsList;
/**
* TextView控件实例
*/
@ViewInject(R.id.tv_title2)
private TextView tvTitle;
/**
* Indicator控件实例
*/
@ViewInject(R.id.indicator2)
private CirclePageIndicator mIndicator;
/**
* ListView控件实例
*/
@ViewInject(R.id.lv_list)
private RefreshListView lvList;
/**
* 存储新闻列表的集合
*/
private ArrayList<NewsTab.News> mNewsList;
/**
* 加载更多信息的Url
*/
private String mMoreUrl;
public TabDetailPaper(Activity activity, NewsMenu.NewsTabData newsTabData) {
super(activity);
this.newsTabData = newsTabData;
mUrl = GlobalConstans.SERVER_URL + newsTabData.url;
}
@Override
public View initViews() {
/*
view = new TextView(mActivity);
view.setTextSize(22);
view.setTextColor(Color.RED);
view.setGravity(Gravity.CENTER); // 居中显示
view.setText("页签");
*/
View view = View.inflate(mActivity,R.layout.paper_tab_detail,null);
// 加载头条新闻的头布局
View headerView = View.inflate(mActivity,R.layout.list_item_header,null);
ViewUtils.inject(this,view);
ViewUtils.inject(this,headerView);
lvList.addHeaderView(headerView); // 给ListView添加头布局
// 设置下拉刷新的监听
lvList.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// 刷新数据
getDataFromServer();
}
@Override
public void onLoadMore() {
if (mMoreUrl != null){
getMoreDataFromServer();
}else {
Toast.makeText(mActivity,"没有更多数据啦",Toast.LENGTH_SHORT).show();
}
}
});
return view;
}
@Override
public void initData() {
// view.setText(newsTabData.title); // 修改当前布局的数据
String cache = CacheUtil.getCache(mActivity, mUrl);
if (!TextUtils.isEmpty(cache)){
// 有缓存
processData(cache);
}
getDataFromServer();
}
/**
* 从服务器中获取数据
*/
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result);
CacheUtil.setCache(mActivity,mUrl,result);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
/**
* 从服务器中获取下一页数据
*/
private void getMoreDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mMoreUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
private void processData(String result) {
Gson gson = new Gson();
NewsTab newsTab = gson.fromJson(result, NewsTab.class);
// 获取下一页的数据地址
String more = newsTab.data.more;
if (!TextUtils.isEmpty(more)){
mMoreUrl = GlobalConstans.SERVER_URL + more;
}
else {
mMoreUrl = null;
}
// 初始化头条新闻数据
mTopNewsList = newsTab.data.topnews;
if (mTopNewsList != null){
mViewPaper.setAdapter(new TopNewsAdapter());
mIndicator.setViewPager(mViewPaper); // 将圆形指示器和viewpager绑定
mIndicator.setSnap(true); // 快照展示方式
mIndicator.onPageSelected(0); // 将圆点位置归零,保证圆点和页面同步
mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
// 更新头条新闻的标题
tvTitle.setText(mTopNewsList.get(i).title);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 初始化首页标题
tvTitle.setText(mTopNewsList.get(0).title);
}
/**
* 初始化新闻列表数据
*/
mNewsList = newsTab.data.news;
if (mNewsList != null){
lvList.setAdapter(new NewsAdapter());
}
}
class TopNewsAdapter extends PagerAdapter{
/**
* xUtils中的BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public TopNewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
// 设置加载中的默认图片
mBitmapUtils.configDefaultLoadingImage(R.drawable.pic_item_list_default);
}
@Override
public int getCount() {
return mTopNewsList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = new ImageView(mActivity);
NewsTab.TopNews topNews = mTopNewsList.get(position);
String topimage = topNews.topimage; // 图片的下载链接
view.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放模式:宽高匹配窗体
/**
* 1,根据url下载图片
* 2.将图片设置给ImageView
* 3.将图片作成缓存
* 4.避免内存溢出
* 由于工程量巨大,这里使用xUtils中的BitmapUtils里的api来完成这四个逻辑
*/
mBitmapUtils.display(view,topimage);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
/**
* 新闻列表适配器
*/
class NewsAdapter extends BaseAdapter{
/**
* BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public NewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);
}
@Override
public int getCount() {
return mNewsList.size();
}
@Override
public NewsTab.News 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_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
return convertView;
}
}
static class ViewHolder{
public ImageView ivIcon;
public TextView tvTitle;
public TextView tvTime;
}
}
- 修改TabDetailPaper,修改processData()方法,传入布尔类型的参数,表示是否加载更多数据,并进行相应修改,代码如下:
package com.example.zhbj.base.impl;
import android.app.Activity;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.zhbj.R;
import com.example.zhbj.base.BaseMenuDetailPaper;
import com.example.zhbj.domain.NewsMenu;
import com.example.zhbj.domain.NewsTab;
import com.example.zhbj.global.GlobalConstans;
import com.example.zhbj.util.CacheUtil;
import com.example.zhbj.view.RefreshListView;
import com.example.zhbj.view.TopNewsViewPaper;
import com.google.gson.Gson;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.CirclePageIndicator;
import java.util.ArrayList;
/**
* 页签详情页,包含北京、中国、国际等页签
* ViewPagerIndicator
* 1.引入ViewPagerIndicator库
* 2.解决v4冲突,用大的版本覆盖小的版本
* 3.仿照sample中的程序进行拷贝SampleTabsDefault
*/
public class TabDetailPaper extends BaseMenuDetailPaper {
/**
* 当前页签数据
*/
private NewsMenu.NewsTabData newsTabData;
/**
* 文本框对象
*/
// private TextView view;
/**
* ViewPager对象
*/
@ViewInject(R.id.vp_tab_detail)
private TopNewsViewPaper mViewPaper;
/**
* 请求的URL地址
*/
private String mUrl;
/**
* 保存头条新闻图片的集合
*/
private ArrayList<NewsTab.TopNews> mTopNewsList;
/**
* TextView控件实例
*/
@ViewInject(R.id.tv_title2)
private TextView tvTitle;
/**
* Indicator控件实例
*/
@ViewInject(R.id.indicator2)
private CirclePageIndicator mIndicator;
/**
* ListView控件实例
*/
@ViewInject(R.id.lv_list)
private RefreshListView lvList;
/**
* 存储新闻列表的集合
*/
private ArrayList<NewsTab.News> mNewsList;
/**
* 加载更多信息的Url
*/
private String mMoreUrl;
/**
* 新闻详情的适配器
*/
private NewsAdapter mNewsAdapter;
public TabDetailPaper(Activity activity, NewsMenu.NewsTabData newsTabData) {
super(activity);
this.newsTabData = newsTabData;
mUrl = GlobalConstans.SERVER_URL + newsTabData.url;
}
@Override
public View initViews() {
/*
view = new TextView(mActivity);
view.setTextSize(22);
view.setTextColor(Color.RED);
view.setGravity(Gravity.CENTER); // 居中显示
view.setText("页签");
*/
View view = View.inflate(mActivity,R.layout.paper_tab_detail,null);
// 加载头条新闻的头布局
View headerView = View.inflate(mActivity,R.layout.list_item_header,null);
ViewUtils.inject(this,view);
ViewUtils.inject(this,headerView);
lvList.addHeaderView(headerView); // 给ListView添加头布局
// 设置下拉刷新的监听
lvList.setOnRefreshListener(new RefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// 刷新数据
getDataFromServer();
}
@Override
public void onLoadMore() {
if (mMoreUrl != null){
getMoreDataFromServer();
}else {
Toast.makeText(mActivity,"没有更多数据啦",Toast.LENGTH_SHORT).show();
lvList.onRefreshComplete();
}
}
});
return view;
}
@Override
public void initData() {
// view.setText(newsTabData.title); // 修改当前布局的数据
String cache = CacheUtil.getCache(mActivity, mUrl);
if (!TextUtils.isEmpty(cache)){
// 有缓存
processData(cache,false);
}
getDataFromServer();
}
/**
* 从服务器中获取数据
*/
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result,false);
CacheUtil.setCache(mActivity,mUrl,result);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
/**
* 从服务器中获取下一页数据
*/
private void getMoreDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpRequest.HttpMethod.GET, mMoreUrl, new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = responseInfo.result;
processData(result,true);
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
@Override
public void onFailure(HttpException e, String s) {
e.printStackTrace();
Toast.makeText(mActivity,s,Toast.LENGTH_SHORT).show();
// 隐藏下拉刷新控件
lvList.onRefreshComplete();
}
});
}
private void processData(String result,boolean isMore) {
Gson gson = new Gson();
NewsTab newsTab = gson.fromJson(result, NewsTab.class);
// 获取下一页的数据地址
String more = newsTab.data.more;
if (!TextUtils.isEmpty(more)){
mMoreUrl = GlobalConstans.SERVER_URL + more;
}
else {
mMoreUrl = null;
}
if (!isMore){
// 初始化头条新闻数据
mTopNewsList = newsTab.data.topnews;
if (mTopNewsList != null){
mViewPaper.setAdapter(new TopNewsAdapter());
mIndicator.setViewPager(mViewPaper); // 将圆形指示器和viewpager绑定
mIndicator.setSnap(true); // 快照展示方式
mIndicator.onPageSelected(0); // 将圆点位置归零,保证圆点和页面同步
mIndicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
// 更新头条新闻的标题
tvTitle.setText(mTopNewsList.get(i).title);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 初始化首页标题
tvTitle.setText(mTopNewsList.get(0).title);
}
/**
* 初始化新闻列表数据
*/
mNewsList = newsTab.data.news;
if (mNewsList != null){
mNewsAdapter = new NewsAdapter();
lvList.setAdapter(new NewsAdapter());
}
}else {
// 加载更多
ArrayList<NewsTab.News> moreNews = newsTab.data.news;
mNewsList.addAll(moreNews); // 追加更多数据
// 刷新listview
mNewsAdapter.notifyDataSetChanged();
}
}
class TopNewsAdapter extends PagerAdapter{
/**
* xUtils中的BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public TopNewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
// 设置加载中的默认图片
mBitmapUtils.configDefaultLoadingImage(R.drawable.pic_item_list_default);
}
@Override
public int getCount() {
return mTopNewsList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ImageView view = new ImageView(mActivity);
NewsTab.TopNews topNews = mTopNewsList.get(position);
String topimage = topNews.topimage; // 图片的下载链接
view.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放模式:宽高匹配窗体
/**
* 1,根据url下载图片
* 2.将图片设置给ImageView
* 3.将图片作成缓存
* 4.避免内存溢出
* 由于工程量巨大,这里使用xUtils中的BitmapUtils里的api来完成这四个逻辑
*/
mBitmapUtils.display(view,topimage);
container.addView(view);
return view;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
}
/**
* 新闻列表适配器
*/
class NewsAdapter extends BaseAdapter{
/**
* BitmapUtils工具类
*/
private BitmapUtils mBitmapUtils;
public NewsAdapter() {
mBitmapUtils = new BitmapUtils(mActivity);
mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);
}
@Override
public int getCount() {
return mNewsList.size();
}
@Override
public NewsTab.News 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_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
return convertView;
}
}
static class ViewHolder{
public ImageView ivIcon;
public TextView tvTitle;
public TextView tvTime;
}
}
10.本地记录已读未读状态
下拉刷新和分页刷新的效果已经实现了,现在需要记录是否点击过任意一条新闻的状态,即点击过就变成灰色,未点击则变成黑色
修改TabDetailPaper,修改initViews()方法,为lvList注册点击事件监听器,代码如下:
// 设置点击事件
lvList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 头布局也算位置,所以使用position时要将头布局个数减掉
int headerViewsCount = lvList.getHeaderViewsCount();
position -= headerViewsCount;
NewsTab.News news = mNewsList.get(position);
// 标记已读未读:将已读新闻id保存在sp中
// key: "read_ids"
// value: 11000,11001,11002...
// 读取现有的id
String readIds = PrefUtils.getString(mActivity, "read_ids", "");
if (!readIds.contains(news.id)){
// 在现有id基础上追加新的id
readIds = readIds + news.id + ",";
// 保存最新的id集合
PrefUtils.putString(mActivity,"read_ids",readIds);
}
}
});
11.根据已读未读状态刷新页面
现在我们能获取到是否点击新闻,接下来就是要根据这个状态来刷新页面
- 修改TabDetailPaper,修改getView()方法,用于刷新页面,代码如下:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null){
convertView = View.inflate(mActivity,R.layout.list_item_news,null);
holder = new ViewHolder();
holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title3);
holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
NewsTab.News info = getItem(position);
holder.tvTitle.setText(info.title);
holder.tvTime.setText(info.pubdate);
mBitmapUtils.display(holder.ivIcon,info.listimage);
// 判断已读未读
String readIds = PrefUtils.getString(mActivity, "read_ids", "");
if (readIds.contains(info.id)){
holder.tvTitle.setTextColor(Color.GRAY);
}
else {
holder.tvTitle.setTextColor(Color.BLACK);
}
return convertView;
}
}
- 修改TabDetailPaper,修改lvList的点击事件监听器,用于刷新lvList,代码如下:
// 设置点击事件
lvList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 头布局也算位置,所以使用position时要将头布局个数减掉
int headerViewsCount = lvList.getHeaderViewsCount();
position -= headerViewsCount;
NewsTab.News news = mNewsList.get(position);
// 标记已读未读:将已读新闻id保存在sp中
// key: "read_ids"
// value: 11000,11001,11002...
// 读取现有的id
String readIds = PrefUtils.getString(mActivity, "read_ids", "");
if (!readIds.contains(news.id)){
// 在现有id基础上追加新的id
readIds = readIds + news.id + ",";
// 保存最新的id集合
PrefUtils.putString(mActivity,"read_ids",readIds);
}
// 刷新ListView,全局刷新
// mNewsAdapter.notifyDataSetChanged();
// 刷新ListView,局部刷新
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title3);
tvTitle.setTextColor(Color.GRAY);
}
});
12.新闻详情页布局开发
我们完成了列表页面的逻辑,现在需要实现点击某条新闻后进入新闻详情的页面
- 新建一个活动NewsDetailActivity,代表点击某条新闻后可以查看详情的页面
- 修改TabDetailPaper,修改lvList的点击事件,添加跳入NewsDetailActivity活动的逻辑,代码如下:
// 设置点击事件
lvList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 头布局也算位置,所以使用position时要将头布局个数减掉
int headerViewsCount = lvList.getHeaderViewsCount();
position -= headerViewsCount;
NewsTab.News news = mNewsList.get(position);
// 标记已读未读:将已读新闻id保存在sp中
// key: "read_ids"
// value: 11000,11001,11002...
// 读取现有的id
String readIds = PrefUtils.getString(mActivity, "read_ids", "");
if (!readIds.contains(news.id)){
// 在现有id基础上追加新的id
readIds = readIds + news.id + ",";
// 保存最新的id集合
PrefUtils.putString(mActivity,"read_ids",readIds);
}
// 刷新ListView,全局刷新
// mNewsAdapter.notifyDataSetChanged();
// 刷新ListView,局部刷新
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title3);
tvTitle.setTextColor(Color.GRAY);
// 跳到新闻详情页
Intent intent = new Intent(mActivity, NewsDetailActivity.class);
mActivity.startActivity(intent);
}
});
- 修改title_bar.xml,由于要重用该文件,所以需要进行相应修改,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/title_red_bg">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textColor="#fff"
android:textSize="25sp"
android:text=""/>
<ImageButton
android:id="@+id/btn_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@null"
android:layout_marginLeft="10dp"
android:src="@drawable/img_menu"/>
<ImageButton
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@null"
android:layout_marginLeft="10dp"
android:visibility="gone"
android:src="@drawable/back"/>
<LinearLayout
android:id="@+id/ll_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:visibility="gone"
android:orientation="horizontal">
<ImageButton
android:id="@+id/btn_textsize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:layout_marginRight="10dp"
android:src="@drawable/icon_textsize"/>
<ImageButton
android:id="@+id/btn_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:src="@drawable/icon_share"/>
</LinearLayout>
</RelativeLayout>
- 修改activity_newss_detail.xml,将其完善为新闻详情的布局,代码如下:
<?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">
<include layout="@layout/title_bar"/>
</LinearLayout>
- 修改NewsDetailActivity,获取控件实例,并且添加initView()方法,用于初始化控件,代码如下:
package com.example.zhbj;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;
/**
* 新闻详情页
*/
public class NewsDetailActivity extends Activity {
/**
* 返回按钮
*/
@ViewInject(R.id.btn_back)
private ImageButton btnBack;
/**
* 菜单按钮
*/
@ViewInject(R.id.btn_menu)
private ImageButton btnMenu;
/**
* 包含两个功能按钮的线性布局
*/
@ViewInject(R.id.ll_control)
private LinearLayout llControl;
/**
* 字体大小按钮
*/
@ViewInject(R.id.btn_textsize)
private ImageButton btnTextSize;
/**
* 分享按钮
*/
@ViewInject(R.id.btn_share)
private ImageButton btnShare;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_news_detail);
ViewUtils.inject(this);
initViews();
}
/**
* 初始化布局
*/
private void initViews() {
btnBack.setVisibility(View.VISIBLE);
btnMenu.setVisibility(View.GONE);
llControl.setVisibility(View.VISIBLE);
}
}
13.加载新闻网页 & 显示进度条
为了显示排版复杂的新闻内容,就需要使用一个高级的组件——WebView,以此来直接加载好网页规范的排版
- 修改activity_news_detail.xml,添加WebView组件,代码如下:
<?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">
<include layout="@layout/title_bar"/>
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
- 修改TabDetailPaper,修改lvList的点击事件,将url地址传给NewsDetailActivity,代码如下:
// 设置点击事件
lvList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 头布局也算位置,所以使用position时要将头布局个数减掉
int headerViewsCount = lvList.getHeaderViewsCount();
position -= headerViewsCount;
NewsTab.News news = mNewsList.get(position);
// 标记已读未读:将已读新闻id保存在sp中
// key: "read_ids"
// value: 11000,11001,11002...
// 读取现有的id
String readIds = PrefUtils.getString(mActivity, "read_ids", "");
if (!readIds.contains(news.id)){
// 在现有id基础上追加新的id
readIds = readIds + news.id + ",";
// 保存最新的id集合
PrefUtils.putString(mActivity,"read_ids",readIds);
}
// 刷新ListView,全局刷新
// mNewsAdapter.notifyDataSetChanged();
// 刷新ListView,局部刷新
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title3);
tvTitle.setTextColor(Color.GRAY);
// 跳到新闻详情页
Intent intent = new Intent(mActivity, NewsDetailActivity.class);
intent.putExtra("url",news.url); // 传递网页
mActivity.startActivity(intent);
}
});
- 修改NewsDetailActivity,获取WebView的实例,并且让其加载页面,代码如下:
package com.example.zhbj;
import android.app.Activity;
import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;
/**
* 新闻详情页
*/
public class NewsDetailActivity extends Activity {
/**
* 返回按钮
*/
@ViewInject(R.id.btn_back)
private ImageButton btnBack;
/**
* 菜单按钮
*/
@ViewInject(R.id.btn_menu)
private ImageButton btnMenu;
/**
* 包含两个功能按钮的线性布局
*/
@ViewInject(R.id.ll_control)
private LinearLayout llControl;
/**
* 字体大小按钮
*/
@ViewInject(R.id.btn_textsize)
private ImageButton btnTextSize;
/**
* 分享按钮
*/
@ViewInject(R.id.btn_share)
private ImageButton btnShare;
@ViewInject(R.id.webview)
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_news_detail);
ViewUtils.inject(this);
initViews();
// 获取WebView的设置对象
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true); // 启用js功能
settings.setBuiltInZoomControls(true); // 启用显示放大/缩小的按钮,不支持已经适配好移动端的页面
settings.setUseWideViewPort(true); // 启用双击缩放
// 给WebView设置一个监听
mWebView.setWebViewClient(new WebViewClient(){
/**
* 页面开始加载
* @param view
* @param url
* @param favicon
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
/**
* 跳转链接
* @param view
* @param url
* @return
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 强制所有跳转链接都在WebView执行
mWebView.loadUrl(url);
return true;
}
/**
* 加载结束
* @param view
* @param url
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
});
mWebView.setWebChromeClient(new WebChromeClient(){
/**
* 获取网页标题
* @param view
* @param title
*/
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
/**
* 进度发生变化
* @param view
* @param newProgress
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
});
String url = getIntent().getStringExtra("url");
// 开始加载网页
mWebView.loadUrl(url);
}
/**
* 初始化布局
*/
private void initViews() {
btnBack.setVisibility(View.VISIBLE);
btnMenu.setVisibility(View.GONE);
llControl.setVisibility(View.VISIBLE);
}
/**
* 拦截物理返回键
*/
@Override
public void onBackPressed() {
if (mWebView.canGoBack()){ // 判断是否可以返回
mWebView.goBack(); // 返回上一个网页
// mWebView.goForward(); 跳到下一个网页(前提是有一个历史记录)
// mWebView.canGoForward(); 判断是否可以跳转到下一个网页
}
else {
finish();
}
}
}
- 修改activity_news_detail.xml,添加进度条组件,代码如下:
<?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">
<include layout="@layout/title_bar"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ProgressBar
android:id="@+id/pb_loading3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/shape_custom_progress"
android:layout_gravity="center"/>
</FrameLayout>
</LinearLayout>
- 修改NewsDetailActivity,获取进度条组件的实例,并调用相应api,代码如下:
package com.example.zhbj;
import android.app.Activity;
import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;
/**
* 新闻详情页
*/
public class NewsDetailActivity extends Activity {
/**
* 返回按钮
*/
@ViewInject(R.id.btn_back)
private ImageButton btnBack;
/**
* 菜单按钮
*/
@ViewInject(R.id.btn_menu)
private ImageButton btnMenu;
/**
* 包含两个功能按钮的线性布局
*/
@ViewInject(R.id.ll_control)
private LinearLayout llControl;
/**
* 字体大小按钮
*/
@ViewInject(R.id.btn_textsize)
private ImageButton btnTextSize;
/**
* 分享按钮
*/
@ViewInject(R.id.btn_share)
private ImageButton btnShare;
/**
* WebView的实例
*/
@ViewInject(R.id.webview)
private WebView mWebView;
/**
* 进度条实例
*/
@ViewInject(R.id.pb_loading3)
private ProgressBar pbLoading;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_news_detail);
ViewUtils.inject(this);
initViews();
// 获取WebView的设置对象
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true); // 启用js功能
settings.setBuiltInZoomControls(true); // 启用显示放大/缩小的按钮,不支持已经适配好移动端的页面
settings.setUseWideViewPort(true); // 启用双击缩放
// 给WebView设置一个监听
mWebView.setWebViewClient(new WebViewClient(){
/**
* 页面开始加载
* @param view
* @param url
* @param favicon
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
pbLoading.setVisibility(View.VISIBLE);
}
/**
* 跳转链接
* @param view
* @param url
* @return
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 强制所有跳转链接都在WebView执行
mWebView.loadUrl(url);
return true;
}
/**
* 加载结束
* @param view
* @param url
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
pbLoading.setVisibility(View.GONE);
}
});
mWebView.setWebChromeClient(new WebChromeClient(){
/**
* 获取网页标题
* @param view
* @param title
*/
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
/**
* 进度发生变化
* @param view
* @param newProgress
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
});
String url = getIntent().getStringExtra("url");
// 开始加载网页
mWebView.loadUrl(url);
}
/**
* 初始化布局
*/
private void initViews() {
btnBack.setVisibility(View.VISIBLE);
btnMenu.setVisibility(View.GONE);
llControl.setVisibility(View.VISIBLE);
}
/**
* 拦截物理返回键
*/
@Override
public void onBackPressed() {
if (mWebView.canGoBack()){ // 判断是否可以返回
mWebView.goBack(); // 返回上一个网页
// mWebView.goForward(); 跳到下一个网页(前提是有一个历史记录)
// mWebView.canGoForward(); 判断是否可以跳转到下一个网页
}
else {
finish();
}
}
}