前言
一直以來公司項目都沒有下拉刷新的功能法严,最近換了新產(chǎn)品瓤鼻,有了這部分的需求設計诽里。我看了下涉及到的頁面所用的控件即有ListView也有RecycleView辜膝,鑒于刷新效果是自己設計的无牵,想從網(wǎng)上直接找造好的輪子,不太現(xiàn)實厂抖,況且牽涉到兩個控件茎毁,以后要是再改到用了GridView的頁面呢。自己重寫這三個控件忱辅,牽扯到的頁面勢必要重新翻一遍七蜘,怎么看工作量都很大谭溉。后來想了下,干脆重寫一個可以下拉刷新的LinearLayout橡卤,包在外面夜只,這要是之前的所有代碼邏輯都不用動,也不用重新測試蒜魄,重要的是可以給任何你想加的控價添加下拉刷新扔亥,工作量都省了下來,說干就干谈为。
首先旅挤,會遇到以下幾個問題
1、下拉刷新觸發(fā)的時機伞鲫,也就是第一個item完全漏出的時候
2粘茄、事件分發(fā),也就是滑動事件什么時候交給ListView什么時候交給刷新LinearLayout
以上兩個問題秕脓,想詳細了解的柒瓣,直接看代碼注釋很清晰,在這里就不細說了吠架。
廢話不多說芙贫,直接上代碼。
一傍药、源碼
1磺平、頭部布局文件 header_refresh.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="100dip"
android:background="#EFEFF4">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dip"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_refresh"
android:layout_width="25dip"
android:layout_height="25dip"
android:layout_toLeftOf="@+id/ll_refresh_right"
android:layout_marginRight="10dip"
android:layout_centerVertical="true"
android:src="@drawable/refresh1"/>
<LinearLayout
android:id="@+id/ll_refresh_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#D7D6D8"
android:textSize="14sp"
android:text="下拉刷新"/>
<TextView
android:id="@+id/tv_last_refresh_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:textColor="#D7D6D8"
android:textSize="13sp"
android:text="最近更新:今天14:20"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
2、刷新動畫refreshing_anim.xml寫在drawable里
<?xml version="1.0" encoding="utf-8"?>
<set android:shareInterpolator="false" xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="+360"
android:duration="300"
android:startOffset="-1"
android:repeatMode="restart"
android:repeatCount="-1"/>
</set>
3拐辽、重寫的PullToRefreshLinearLayout .java
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import com.xuetian.netschool.R;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 下拉刷新控件拣挪,可以配合 RecyclerView剧蚣,Scrollview集绰,ListView等所有view使用
*
*/
public class PullToRefreshLinearLayout extends LinearLayout {
private View targetRefreshView;
int downY = 0, moveY = 0;
int downX = 0, moveX = 0;
boolean isRefreshStart = false;
int hearderViewHeight = 100;//刷新頭部高度dip,與布局文件中保持一致
int instance;
int paddingTop;
View headerView;
final String SHARE_KEY = "refreshTime";
final SimpleDateFormat HHmm = new SimpleDateFormat("HH:mm");
OnRefreshListener onRefreshListener;
private SharedPreferences sharedPreferences;
boolean isRefreshing = false;
TextView textView, tvLastRefreshTime;
ImageView ivRefresh;
public PullToRefreshLinearLayout(Context context) {
this(context, null);
}
public PullToRefreshLinearLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
Context context;
public PullToRefreshLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
setOrientation(VERTICAL);
sharedPreferences = context.getSharedPreferences(SHARE_KEY, Context.MODE_PRIVATE);
initHeaderView();
}
private void initHeaderView(){
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
headerView = inflater.inflate(R.layout.header_refresh, null);
textView = headerView.findViewById(R.id.tv_refresh);
ivRefresh = headerView.findViewById(R.id.iv_refresh);
tvLastRefreshTime = headerView.findViewById(R.id.tv_last_refresh_time);
tvLastRefreshTime.setText("最后更新:今天" + getHHmm(getRefreshTime()));
headerView.setPadding(0, -dip2px(context, hearderViewHeight), 0, 0);
addView(headerView,0);
}
@Override
public void addView(View child) {
if (getChildCount() > 1) {
throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
}
super.addView(child);
}
@Override
public void addView(View child, int index) {
if (getChildCount() > 1) {
throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
}
super.addView(child, index);
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
if (getChildCount() > 1) {
throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
}
super.addView(child, params);
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (getChildCount() > 1) {
throw new IllegalStateException("RefreshLinearLayout can host only one direct child");
}
targetRefreshView = child;
super.addView(child, index, params);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(targetRefreshView == null) return super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
isRefreshStart = isStartRefresh(targetRefreshView);
downY = (int) event.getY();
downX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int) event.getY();
instance = moveY - downY;
if (instance > 0 && isRefreshStart) {
paddingTop = -dip2px(context, hearderViewHeight) + instance;
if(paddingTop >=dip2px(context, hearderViewHeight)) paddingTop = dip2px(context, 100);
headerView.setPadding(0, paddingTop, 0, 0);
if(instance >= dip2px(context, hearderViewHeight)) textView.setText("松開刷新");
else textView.setText("下拉刷新");
return true;
}
break;
case MotionEvent.ACTION_UP:
if(instance >= dip2px(context, hearderViewHeight)){
isRefreshing = true;
textView.setText("正在刷新...");
// 加載動畫
ivRefresh.setImageResource(R.drawable.refresh1);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(
context, R.anim.refreshing_anim);
// 使用ImageView顯示動畫
ivRefresh.startAnimation(hyperspaceJumpAnimation);
new Thread(new Runnable() {
int i = 0;
@Override
public void run() {
for ( i = 0; i <= 50; i++){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
headerView.setPadding(0, paddingTop - i * paddingTop/50 , 0, 0);
}
});
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
if(onRefreshListener != null)onRefreshListener.onRefresh();
}
});
}
}).start();
}else {
new Thread(new Runnable() {
int i = 0;
@Override
public void run() {
for ( i = 0; i <= 100; i++){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
headerView.setPadding(0,
paddingTop + i *(-dip2px(context, hearderViewHeight) - paddingTop)/100 , 0, 0);
}
});
}
}
}).start();
}
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if(targetRefreshView == null) return super.onInterceptTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
isRefreshStart = isStartRefresh(targetRefreshView);
downY = (int) event.getY();
downX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
moveY = (int) event.getY();
moveX = (int) event.getX();
instance = moveY - downY;
if (!isRefreshing && instance > 0 && isRefreshStart) {
if(!isRefreshing){
tvLastRefreshTime.setText("最后更新:今天" + getHHmm(getRefreshTime()));
}
if(Math.abs(downX - moveX) > Math.abs(downY - moveY)){
return false;
}
return true;
}else if(isRefreshing && instance < 0){
refreshComplete();
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(event);
}
private boolean isStartRefresh(View targetRefreshView){
if(targetRefreshView != null && (targetRefreshView instanceof ListView ||
targetRefreshView instanceof RecyclerView ||
targetRefreshView instanceof GridView || targetRefreshView instanceof ScrollView)){
if((targetRefreshView instanceof ListView) && ((ListView)targetRefreshView).getFirstVisiblePosition() == 0
&& ((ListView)targetRefreshView).getChildAt(0).getTop() >= targetRefreshView.getPaddingTop())
return true;
else if ((targetRefreshView instanceof RecyclerView) && ((LinearLayoutManager)((RecyclerView)targetRefreshView)
.getLayoutManager()).findFirstVisibleItemPosition() == 0
&& ((RecyclerView)targetRefreshView).getChildAt(0).getTop() >= targetRefreshView.getPaddingTop())
return true;
else if ((targetRefreshView instanceof GridView) && ((GridView)targetRefreshView).getFirstVisiblePosition() == 0
&& ((GridView)targetRefreshView).getChildAt(0).getTop() >= targetRefreshView.getPaddingTop())
return true;
else if((targetRefreshView instanceof ScrollView) && ((ScrollView)targetRefreshView).getScrollY() == 0)
return true;
else return false;
}
else if(targetRefreshView != null)
return true;
else return false;
}
/**
* 將刷新頭部復位
* */
private void headerRestoration(){
setRefreshTime();
if (!isRefreshing) return;
isRefreshing = false;
ivRefresh.clearAnimation();
ivRefresh.setImageResource(R.drawable.refresh2);
textView.setText("刷新成功");
new Thread(new Runnable() {
int i = 0;
@Override
public void run() {
for ( i = 1; i <= 100; i++){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
headerView.setPadding(0,
i * (-dip2px(context, hearderViewHeight))/100 , 0, 0);
}
});
}
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("下拉刷新");
ivRefresh.setImageResource(R.drawable.refresh1);
if(onRefreshListener != null)onRefreshListener.refreshData();
}
});
}
}).start();
}
/**
* 設置最近刷新時間
*
*/
private void setRefreshTime() {
sharedPreferences.edit().putLong(context.getPackageName() + context.getClass().getName(), System.currentTimeMillis()).commit();
}
/**
* 獲取最近刷新時間
*/
private Long getRefreshTime() {
return sharedPreferences.getLong(context.getPackageName() + context.getClass().getName(), System.currentTimeMillis());
}
/**
* 根據(jù)手機的分辨率從 dp 的單位 轉成為 px(像素)
*/
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* HH:mm類型的日期
* @param milliseconds
* @return
*/
private String getHHmm(long milliseconds){
if(milliseconds == 0){
return "";
}
String string;
Date date = new Date(milliseconds);
string = HHmm.format(date);
return string;
}
//=======================================以上代碼都不用看,獲取該控件實例后直接調(diào)用setOnRefreshListener即可淤刃, 會回調(diào)相應方法================================
public void setOnRefreshListener(OnRefreshListener onRefreshListener){
this.onRefreshListener = onRefreshListener;
}
//數(shù)據(jù)請求完畢睁搭,調(diào)用該方法
public void refreshComplete(){
headerRestoration();
}
/**
* 刷新回掉接口
* */
public interface OnRefreshListener{
//刷新開始赶诊,回調(diào)該方法;可在該方法中請求最新數(shù)據(jù)
void onRefresh();
//刷新頭部復位后介袜,會回調(diào)該方法甫何,可在該方法里將請求到的新數(shù)據(jù)渲染到頁面上
void refreshData();
}
}
注:刷新的頭部有用到兩個圖片出吹,在此就不上傳了遇伞,可以根據(jù)需要自己選擇
二、使用方式
獲取到下拉刷新控件設置回調(diào)監(jiān)聽接口即可
pullToRefreshLinearLayout.setOnRefreshListener(new PullToRefreshLinearLayout().OnRefreshListener() {
@Override
public void onRefresh() {
}
@Override
public void refreshData() {
}
});
總結:只要講該下拉刷新控件包在任何一個View或者Viewroup的外面捶牢,都可以給出下拉刷新效果
著作:一點愁
原創(chuàng)博客轉載請注明出處……