android 之下拉刷新
一.概述
Android 下拉刷新几乎是每个应用都必带的功能, 并且现在下拉刷新第三方库也越来越多了,很方便就能实现该功能, 下面我介绍一下 自己常用的几个方法.
二.例子
第一种方式:就是集成ListView实现自定义控件完成上下拉刷新
public class PullToRefreshListView extends ListView implements OnScrollListener, OnClickListener { /** * 下拉状态 */ private static final int PULL_TO_REFRESH = 1; //下拉-默认为初始状态 准备下拉刷新 private static final int RELEASE_TO_REFRESH = 2; //释放刷新 private static final int REFRESHING = 3; //正在刷新 private static final String TAG = "PullRefreshListView"; private OnRefreshListener mOnRefreshListener; /** * 组件滑动监听器 scroll 当view在进行下拉滑动的时候,判断滑动的距离, * 如果达到可以进行刷新的临界点时候,回调当前接口中的方法 * Listener that will receive notifications every time the list scrolls. */ private OnScrollListener mOnScrollListener; //下拉刷新的的头部view private LinearLayout mRefreshView; private ImageView mRefreshViewImage; private ProgressBar mRefreshViewProgress; private TextView mRefreshViewText; private TextView mRefreshViewLastUpdated; private int mRefreshState; private int mCurrentScrollState; private RotateAnimation mFlipAnimation; private RotateAnimation mReverseFlipAnimation; private int mRefreshViewHeight; private int mRefreshOriginalTopPadding; private int mLastMotionY; public PullToRefreshListView(Context context) { super(context); init(context); } public PullToRefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mFlipAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mFlipAnimation.setInterpolator(new LinearInterpolator()); mFlipAnimation.setDuration(250); mFlipAnimation.setFillAfter(true); mReverseFlipAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); mReverseFlipAnimation.setInterpolator(new LinearInterpolator()); mReverseFlipAnimation.setDuration(250); mReverseFlipAnimation.setFillAfter(true); mRefreshView = (LinearLayout) View.inflate(context, R.layout.pull_to_refresh_header, null); mRefreshViewText = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text); mRefreshViewImage = (ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image); mRefreshViewProgress = (ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress); mRefreshViewLastUpdated = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at); mRefreshState = PULL_TO_REFRESH; mRefreshViewImage.setMinimumHeight(50); //设置下拉最小的高度为50 setFadingEdgeLength(0); setHeaderDividersEnabled(false); //把refreshview加入到listview的头部 addHeaderView(mRefreshView); super.setOnScrollListener(this); mRefreshView.setOnClickListener(this); mRefreshView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); mRefreshViewHeight = mRefreshView.getMeasuredHeight(); mRefreshOriginalTopPadding = -mRefreshViewHeight; resetHeaderPadding(); } /** * Set the listener that will receive notifications every time the list scrolls. * * @param l The scroll listener. */ @Override public void setOnScrollListener(OnScrollListener l) { mOnScrollListener = l; } /** * 注册listview下拉刷新回到接口 * Register a callback to be invoked when this list should be refreshed. * * @param onRefreshListener The callback to run. */ public void setOnRefreshListener(OnRefreshListener onRefreshListener) { mOnRefreshListener = onRefreshListener; } /** * 进行设置设置上一次更新的时候 * * Set a text to represent when the list was last updated. * @param lastUpdated Last updated at. */ public void setLastUpdated(CharSequence lastUpdated) { if (lastUpdated != null) { mRefreshViewLastUpdated.setVisibility(View.VISIBLE); mRefreshViewLastUpdated.setText(lastUpdated); } else { mRefreshViewLastUpdated.setVisibility(View.GONE); } } /** * touch事件处理 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { final int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: int offsetY = (int) event.getY(); int deltY = Math.round(offsetY - mLastMotionY); mLastMotionY = offsetY; if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) { deltY = deltY / 2; mRefreshOriginalTopPadding += deltY; if (mRefreshOriginalTopPadding < -mRefreshViewHeight) { mRefreshOriginalTopPadding = -mRefreshViewHeight; } resetHeaderPadding(); } break; case MotionEvent.ACTION_UP: //当手指抬开得时候 进行判断下拉的距离 ,如果>=临界值,那么进行刷洗,否则回归原位 if (!isVerticalScrollBarEnabled()) { setVerticalScrollBarEnabled(true); } if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) { if (mRefreshView.getBottom() >= mRefreshViewHeight && mRefreshState == RELEASE_TO_REFRESH) { //准备开始刷新 prepareForRefresh(); } else { // Abort refresh resetHeader(); } } break; } return super.onTouchEvent(event); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && mRefreshState != REFRESHING) { if (firstVisibleItem == 0) { if ((mRefreshView.getBottom() >= mRefreshViewHeight) && mRefreshState != RELEASE_TO_REFRESH) { mRefreshViewText.setText(R.string.pull_to_refresh_release_label_it); mRefreshViewImage.clearAnimation(); mRefreshViewImage.startAnimation(mFlipAnimation); mRefreshState = RELEASE_TO_REFRESH; } else if (mRefreshView.getBottom() < mRefreshViewHeight && mRefreshState != PULL_TO_REFRESH) { mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it); mRefreshViewImage.clearAnimation(); mRefreshViewImage.startAnimation(mReverseFlipAnimation); mRefreshState = PULL_TO_REFRESH; } } } if (mOnScrollListener != null) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { mCurrentScrollState = scrollState; if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(view, scrollState); } } /** * Sets the header padding back to original size. */ private void resetHeaderPadding() { mRefreshView.setPadding( mRefreshView.getPaddingLeft(), mRefreshOriginalTopPadding, mRefreshView.getPaddingRight(), mRefreshView.getPaddingBottom()); } public void prepareForRefresh() { if (mRefreshState != REFRESHING) { mRefreshState = REFRESHING; mRefreshOriginalTopPadding = 0; resetHeaderPadding(); mRefreshViewImage.clearAnimation(); mRefreshViewImage.setVisibility(View.GONE); mRefreshViewProgress.setVisibility(View.VISIBLE); mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label_it); onRefresh(); } } private void resetHeader() { mRefreshState = PULL_TO_REFRESH; mRefreshOriginalTopPadding = -mRefreshViewHeight; resetHeaderPadding(); mRefreshViewImage.clearAnimation(); mRefreshViewImage.setVisibility(View.VISIBLE); mRefreshViewProgress.setVisibility(View.GONE); mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it); } /** * 开始回调刷新 */ public void onRefresh() { Log.d(TAG, "onRefresh"); if (mOnRefreshListener != null) { mOnRefreshListener.onRefresh(); } } /** * Resets the list to a normal state after a refresh. */ public void onRefreshComplete() { Log.d(TAG, "onRefreshComplete"); resetHeader(); } @Override public void onClick(View v) { Log.d(TAG, "onClick"); } /** * Interface definition for a callback to be invoked when list should be * refreshed. */ public interface OnRefreshListener { /** * Called when the list should be refreshed. * <p> * A call to {@link PullToRefreshListView #onRefreshComplete()} is * expected to indicate that the refresh has completed. */ public void onRefresh(); } }
上面代码就完成了,上下拉刷新工作, 直接拷贝到工程无需任何修改就可使用
当然它还需一个"头布局"
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pull_to_refresh_header" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#F3F3F3" android:orientation="vertical" > <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingTop="23dip" > <LinearLayout android:id="@+id/pull_to_refresh_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:orientation="vertical" > <TextView android:id="@+id/pull_to_refresh_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:text="@string/pull_to_refresh_pull_label" android:textColor="#777777" android:textSize="16sp" /> <TextView android:id="@+id/pull_to_refresh_updated_at" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:text="@string/pull_to_refresh_updated_at" android:textColor="#999999" android:textSize="14sp" /> </LinearLayout> <ProgressBar android:id="@+id/pull_to_refresh_progress" android:layout_width="30dip" android:layout_height="30dip" android:layout_marginRight="22dip" android:layout_marginTop="5dip" android:layout_toLeftOf="@+id/pull_to_refresh_view" android:indeterminate="true" android:indeterminateDrawable="@anim/ic_loading_refresh" android:visibility="gone" /> <ImageView android:id="@+id/pull_to_refresh_image" android:layout_width="32dip" android:layout_height="32dip" android:layout_marginRight="20dip" android:layout_marginTop="5dip" android:layout_toLeftOf="@+id/pull_to_refresh_view" android:contentDescription="@string/app_name" android:gravity="center" android:src="@drawable/ic_refresh_down" /> </RelativeLayout> <View android:layout_width="fill_parent" android:layout_height="15dip" /> </LinearLayout>
此外代码中还用到了 时间的工具类: 如下
public class SharedPreferencesHelper { private static final String SHARED_PATH = "fda_shared"; private static SharedPreferencesHelper instance; private SharedPreferences sp; private SharedPreferences.Editor editor; public static SharedPreferencesHelper getInstance(Context context) { if (instance == null && context != null) { instance = new SharedPreferencesHelper(context); } return instance; } private SharedPreferencesHelper(Context context) { sp = context.getSharedPreferences(SHARED_PATH, Context.MODE_PRIVATE); editor = sp.edit(); } public long getLongValue(String key) { if (key != null && !key.equals("")) { return sp.getLong(key, 0); } return 0; } public String getStringValue(String key) { if (key != null && !key.equals("")) { return sp.getString(key, null); } return null; } public int getIntValue(String key) { if (key != null && !key.equals("")) { return sp.getInt(key, 0); } return 0; } public int getIntValueByDefault(String key) { if (key != null && !key.equals("")) { return sp.getInt(key, 0); } return 0; } public boolean getBooleanValue(String key) { if (key != null && !key.equals("")) { return sp.getBoolean(key, false); } return true; } public float getFloatValue(String key) { if (key != null && !key.equals("")) { return sp.getFloat(key, 0); } return 0; } public void putStringValue(String key, String value) { if (key != null && !key.equals("")) { editor = sp.edit(); editor.putString(key, value); editor.commit(); } } public void putIntValue(String key, int value) { if (key != null && !key.equals("")) { editor = sp.edit(); editor.putInt(key, value); editor.commit(); } } public void putBooleanValue(String key, boolean value) { if (key != null && !key.equals("")) { editor = sp.edit(); editor.putBoolean(key, value); editor.commit(); } } public void putLongValue(String key, long value) { if (key != null && !key.equals("")) { editor = sp.edit(); editor.putLong(key, value); editor.commit(); } } public void putFloatValue(String key, Float value) { if (key != null && !key.equals("")) { editor = sp.edit(); editor.putFloat(key, value); editor.commit(); } } }
public class SharedPreferencesTag { public static final String DEMO_KEY="demo_key"; }
public class UIUtils { /** * 设置上次更新数据时间 * @param listView * @param key key表示具体某个列表 */ public static void setPullToRefreshLastUpdated(PullToRefreshListView listView, String key,Context pContext) { SharedPreferencesHelper spHelper = SharedPreferencesHelper.getInstance(pContext); long lastUpdateTimeStamp = spHelper.getLongValue(key); listView.setLastUpdated(getUpdateTimeString(lastUpdateTimeStamp)); } /** * 保存更新数据时间 * @param listView * @param key key表示具体某个列表 */ public static void savePullToRefreshLastUpdateAt(PullToRefreshListView listView, String key,Context pContext) { listView.onRefreshComplete(); SharedPreferencesHelper spHelper = SharedPreferencesHelper.getInstance(pContext); long lastUpdateTimeStamp=System.currentTimeMillis(); spHelper.putLongValue(key, lastUpdateTimeStamp); listView.setLastUpdated(getUpdateTimeString(lastUpdateTimeStamp)); } /** * 更新时间字符串 * @param timestamp * @return */ @SuppressLint("SimpleDateFormat") public static String getUpdateTimeString(long timestamp) { if (timestamp <= 0) { return "上次更新时间:"; } else { String textDate = "上次更新时间:"; Calendar now = Calendar.getInstance(); Calendar c = Calendar.getInstance(); c.setTimeInMillis(timestamp); if (c.get(Calendar.YEAR) == now.get(Calendar.YEAR) && c.get(Calendar.MONTH) == now.get(Calendar.MONTH) && c.get(Calendar.DATE) == now.get(Calendar.DATE)) { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); return textDate += sdf.format(c.getTime()); } else if (c.get(Calendar.YEAR) == now.get(Calendar.YEAR)) { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm"); return textDate += sdf.format(c.getTime()); } else { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm"); return textDate += sdf.format(c.getTime()); } } } }
这样就完成了, 测试效果图
源码地址:https://yunpan.cn/cryrD4r7PIgWb (提取码:fcb1)
--------------------------------------------------------------------------------------------------------
如果觉得上面的写法太麻烦,或者根本就不想自己写代码,那就用第三方库吧,PullToRefresh
使用步骤:
1. https://github.com/cardview/Android-PullToRefresh 下载源码
2. 解压下载文件, 把library 作为Module导入 Android studio
3. 把该library 最为库,引入 自己工程中
引用文章:http://blog.csdn.net/hantangsongming/article/details/42490277
--------------------------------------------------------------------------------------------------------
谷歌也推出了一种下拉刷新方式:
SwipeRefreshLayout ,但是它只支持下拉刷新,并不支持 上拉加载更多
public class MainActivity extends AppCompatActivity { private RecyclerView demo_recycler; private LinearLayoutManager linearLayoutManager; private RefreshRecyclerAdpater adapter; private SwipeRefreshLayout demo_swiperefreshlayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); demo_recycler=(RecyclerView)this.findViewById(R.id.demo_recycler); linearLayoutManager=new LinearLayoutManager(this); linearLayoutManager.setOrientation(OrientationHelper.VERTICAL); demo_recycler.setLayoutManager(linearLayoutManager); demo_recycler.setAdapter(adapter = new RefreshRecyclerAdpater(this)); demo_swiperefreshlayout=(SwipeRefreshLayout)this.findViewById(R.id.demo_swiperefreshlayout);
//这是设置圆圈背景色---白色 demo_swiperefreshlayout.setProgressBackgroundColorSchemeColor(getResources().getColor(android.R.color.white));
//设置进度条颜色,最多可以设置4种,每转一圈变换一种颜色 demo_swiperefreshlayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_green_light, android.R.color.holo_red_light, android.R.color.holo_orange_light);
//设置圆圈距离顶部距离 demo_swiperefreshlayout.setProgressViewOffset(false, 0, 50);
//模拟联网获取数据 demo_swiperefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { new Handler().postDelayed(new Runnable() { @Override public void run() { List<String> temp=new ArrayList<String>(); for(int i=0;i<5;i++){ temp.add("refresh item "+i); } adapter.addItem(temp); if(demo_swiperefreshlayout.isRefreshing()) { demo_swiperefreshlayout.setRefreshing(false); } } },5000); Toast.makeText(MainActivity.this,"下拉刷新",Toast.LENGTH_SHORT).show(); } }); } }
对应的RecyclerView 的adapter
public class RefreshRecyclerAdpater extends RecyclerView.Adapter<RefreshRecyclerAdpater.ViewHolder>{ private LayoutInflater mInflater; private List<String> mTitles=null; public RefreshRecyclerAdpater(Context context){ this.mInflater=LayoutInflater.from(context); this.mTitles=new ArrayList<String>(); for (int i=0;i<20;i++){ int index=i+1; mTitles.add("item"+index); } } /** * item显示类型 * @param parent * @param viewType * @return */ @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View view=mInflater.inflate(R.layout.item_recycler_layout,parent,false); //这边可以做一些属性设置,甚至事件监听绑定 //view.setBackgroundColor(Color.RED); ViewHolder viewHolder=new ViewHolder(view); return viewHolder; } /** * 数据的绑定显示 * @param holder * @param position */ @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.item_tv.setText(mTitles.get(position)); holder.itemView.setTag(position); } @Override public int getItemCount() { return mTitles.size(); } //自定义的ViewHolder,持有每个Item的的所有界面元素 public static class ViewHolder extends RecyclerView.ViewHolder { public TextView item_tv; public ViewHolder(View view){ super(view); item_tv = (TextView)view.findViewById(R.id.item_tv); } } //添加数据 /** * 添加最新数据 * @param newDatas */ public void addItem(List<String> newDatas) { newDatas.addAll(mTitles); mTitles.removeAll(mTitles); mTitles.addAll(newDatas); notifyDataSetChanged(); } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/demo_swiperefreshlayout" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/demo_recycler" android:layout_width="fill_parent" android:layout_height="fill_parent" > </android.support.v7.widget.RecyclerView> </android.support.v4.widget.SwipeRefreshLayout>