public class SlideRefreshListView extends ListView implements AbsListView.OnScrollListener{
private View header;
private ImageView arrow;
private ProgressBar progressBar;
private TextView content;
private TextView time;
private float downY;
private float moveY;
private int mHeaderHeight;
//箭頭上滑動畫
private RotateAnimation rotateUpAnimation;
//箭頭下滑動畫
private RotateAnimation rotateDowmAnimation;
//下拉刷新
public static final int PULL_TO_REFRESH = 0;
//釋放刷新
public static final int RELEASE_REFRESH = 1;
//正在刷新中
public static final int REFRESHING = 2;
//當前刷新模式
private int currentState = PULL_TO_REFRESH;
//刷新監聽用於接收具體實現
private OnRefreshListener onRefreshListener;
//是否在加載更多 默認沒有加載更多
boolean isLoadMore = false;
private View footer;
private int mFooterHeight;
//刷新完成狀態
public static final int REFRESH = 0;
//加載更多完成狀態
public static final int LOAD_MORE = 1;
public SlideRefreshListView(Context context) {
super(context);
init();
}
public SlideRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SlideRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
//初始化
private void init() {
initHeader();
initFooter();
initAnim();
setOnScrollListener(this);
}
//初始化頭佈局
private void initHeader() {
header = View.inflate(getContext(), R.layout.layout_header, null);
arrow = (ImageView) header.findViewById(R.id.arrow);
progressBar = (ProgressBar) header.findViewById(R.id.progressBar);
content = (TextView) header.findViewById(R.id.tv_content);
time = (TextView) header.findViewById(R.id.tv_time);
header.measure(0, 0);
mHeaderHeight = header.getMeasuredHeight();
//初始化隱藏頭佈局
header.setPadding(0, -mHeaderHeight, 0, 0);
addHeaderView(header);
}
//初始化腳佈局
private void initFooter() {
footer = View.inflate(getContext(), R.layout.layout_footer, null);
//先測量再拿高度
footer.measure(0, 0);
mFooterHeight = footer.getMeasuredHeight();
//初始化隱藏腳佈局
footer.setPadding(0, -mFooterHeight, 0, 0);
addFooterView(footer);
}
//初始化動畫 箭頭旋轉
private void initAnim() {
//逆時針 0->-180
rotateUpAnimation = new RotateAnimation(0, -180f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
//持續時間
rotateUpAnimation.setDuration(200);
//動畫結束後保持位置
rotateUpAnimation.setFillAfter(true);
rotateDowmAnimation = new RotateAnimation(-180, -360f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateDowmAnimation.setDuration(200);
rotateDowmAnimation.setFillAfter(true);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
moveY = ev.getY();
//如果當前已經在刷新 則將事件交由父親去處理
if (currentState == REFRESHING) {
return super.onTouchEvent(ev);
}
float dy = moveY - downY; // 移動的偏移量
// 當dy>0 且頭佈局可見時才改變頭佈局的padding
if (dy > 0 && getFirstVisiblePosition() == 0) {
int paddingTop = (int) (-mHeaderHeight + 0.4 * dy);
header.setPadding(0, paddingTop, 0, 0);
//佈局完全顯示
if (paddingTop >= 0 && currentState != RELEASE_REFRESH) {
currentState = RELEASE_REFRESH;
updateHeader();
} else if (paddingTop < 0 && currentState != PULL_TO_REFRESH) {
//佈局不完全顯示
currentState = PULL_TO_REFRESH;
updateHeader();
}
return true;
}
break;
case MotionEvent.ACTION_UP:
//還沒滑到位 鬆開隱藏頭佈局
if (currentState == PULL_TO_REFRESH) {
header.setPadding(0, -mHeaderHeight, 0, 0);
} else if (currentState == RELEASE_REFRESH) {
//設置當前狀態爲REFRESHING 避免重複進入
currentState = REFRESHING;
//滑到位了 全部顯示頭佈局
header.setPadding(0, 0, 0, 0);
updateHeader();
}
break;
}
return super.onTouchEvent(ev);
}
//根據currentState更新頭佈局
private void updateHeader() {
switch (currentState) {
case PULL_TO_REFRESH:
arrow.startAnimation(rotateUpAnimation);
content.setText("下拉刷新...");
break;
case RELEASE_REFRESH:
arrow.startAnimation(rotateDowmAnimation);
content.setText("釋放刷新...");
break;
case REFRESHING:
//只有清除綁定的動畫 才能設置不可見
arrow.clearAnimation();
arrow.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.VISIBLE);
content.setText("正在刷新...");
if(onRefreshListener != null) {
onRefreshListener.onRefresh();
}
break;
default:
break;
}
}
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
if(isLoadMore){
return;
}
// 最新狀態是空閒狀態, 並且當前界面顯示了所有數據的最後一條. 加載更多
if(scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() >= (getCount() - 1)){
isLoadMore = true;
footer.setPadding(0, 0, 0, 0);
setSelection(getCount()); // 跳轉到最後一條, 使其顯示出加載更多.
if(onRefreshListener != null){
onRefreshListener.onLoadMore();
}
}
}
@Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
}
// 獲取當前時間
private String getRefreshTime(){
long current = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
return sdf.format(current);
}
public void RefreshComplete(int state){
switch (state){
case REFRESH:
currentState = PULL_TO_REFRESH;
content.setText("下拉刷新...");
header.setPadding(0,-mHeaderHeight,0,0);
arrow.setVisibility(VISIBLE);
progressBar.setVisibility(View.VISIBLE);
String ctime = getRefreshTime();
time.setText("上次刷新時間: " + time);
break;
case LOAD_MORE:
footer.setPadding(0, -mFooterHeight, 0, 0);
isLoadMore = false;
break;
}
}
/**
* 刷新回調接口
*/
public interface OnRefreshListener{
void onRefresh();
void onLoadMore();
}
/**
* 設置監聽
* @param orl
*/
public void setOnRefreshListener(OnRefreshListener orl){
this.onRefreshListener = orl;
}
}
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SlideRefreshListView listView = (SlideRefreshListView) findViewById(R.id.srListView);
listView.setOnRefreshListener(new SlideRefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
// TODO: 2017/3/15 刷新邏輯
}
@Override
public void onLoadMore() {
// TODO: 2017/3/15 加載更多
new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("加載更多出來的數據****");
runOnUiThread(new Runnable() {
@Override
public void run() {
listView.RefreshComplete(SlideRefreshListView.LOAD_MORE);
lvAdapter.notifyDataSetChanged();
}
});
}
}.start();
//這裏我開始愚蠢的用成了.run() 然後手機卡到爆由於時間沒到5s沒有報ANR
}
});
list = new ArrayList<>();
for(int i=0;i<30;i++)
list.add(i,"這是假數據"+i);
lvAdapter = new LVAdapter();
listView.setAdapter(lvAdapter);
}
class LVAdapter extends BaseAdapter{
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
TextView textView = new TextView(parent.getContext());
textView.setTextSize(18f);
textView.setText(list.get(position));
return textView;
}
}