代码!
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.pulldown.PullDownScrollView
android:id="@+id/refresh_root"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:background="#161616"
android:orientation="vertical" >
<ScrollView
android:id="@+id/scrollview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="none" >
<LinearLayout
android:id="@+id/mainView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#1f1f1f"
android:orientation="vertical" >
<!-- 自已的布局 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
</LinearLayout>
</ScrollView>
</com.example.pulldown.PullDownScrollView>
lt;/LinearLayout>
2.UI使用:
首先,Activity实现接口:
implements RefreshListener
部分代码如下:
Java代码 收藏代码
package com.example.pulldown;
import com.example.pulldown.PullDownScrollView.RefreshListener;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity implements RefreshListener{
private PullDownScrollView mPullDownScrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPullDownScrollView = (PullDownScrollView) findViewById(R.id.refresh_root);
mPullDownScrollView.setRefreshListener(this);
mPullDownScrollView.setPullDownElastic(new PullDownElasticImp(this));
}
@Override
public void onRefresh(PullDownScrollView view) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mPullDownScrollView.finishRefresh("上次刷新时间:12:23");
}
}, 2000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
3.再来看看控件代码:
Java代码 收藏代码
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
/**
* @author xwangly@163.com
* @date 2013-7-9
*
*/
public class PullDownScrollView extends LinearLayout {
private static final String TAG = "PullDownScrollView";
private int refreshTargetTop = -60;
private int headContentHeight;
private RefreshListener refreshListener;
private RotateAnimation animation;
private RotateAnimation reverseAnimation;
private final static int RATIO = 2;
private int preY = 0;
private boolean isElastic = false;
private int startY;
private int state;
private String note_release_to_refresh = "松开更新";
private String note_pull_to_refresh = "下拉刷新";
private String note_refreshing = "正在更新...";
private IPullDownElastic mElastic;
public PullDownScrollView(Context context) {
super(context);
init();
}
public PullDownScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
animation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(250);
animation.setFillAfter(true);
reverseAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(200);
reverseAnimation.setFillAfter(true);
}
/**
* 刷新监听
* @param listener
*/
public void setRefreshListener(RefreshListener listener) {
this.refreshListener = listener;
}
/**
* 下拉布局
* @param elastic
*/
public void setPullDownElastic(IPullDownElastic elastic) {
mElastic = elastic;
headContentHeight = mElastic.getElasticHeight();
refreshTargetTop = - headContentHeight;
LayoutParams lp = new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, headContentHeight);
lp.topMargin = refreshTargetTop;
addView(mElastic.getElasticLayout(), 0, lp);
}
/**
* 设置更新提示语
* @param pullToRefresh 下拉刷新提示语
* @param releaseToRefresh 松开刷新提示语
* @param refreshing 正在刷新提示语
*/
public void setRefreshTips(String pullToRefresh, String releaseToRefresh, String refreshing) {
note_pull_to_refresh = pullToRefresh;
note_release_to_refresh = releaseToRefresh;
note_refreshing = refreshing;
}
/*
* 该方法一般和ontouchEvent 一起用 (non-Javadoc)
*
* @see
* android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Logger.d(TAG, "onInterceptTouchEvent");
printMotionEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
preY = (int) ev.getY();
}
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
Logger.d(TAG, "isElastic:" + isElastic + " canScroll:"+ canScroll() + " ev.getY() - preY:"+(ev.getY() - preY));
if (!isElastic && canScroll()
&& (int) ev.getY() - preY >= headContentHeight / (3*RATIO)
&& refreshListener != null && mElastic != null) {
isElastic = true;
startY = (int) ev.getY();
Logger.i(TAG, "在move时候记录下位置startY:" + startY);
return true;
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Logger.d(TAG, "onTouchEvent");
printMotionEvent(event);
handleHeadElastic(event);
return super.onTouchEvent(event);
}
private void handleHeadElastic(MotionEvent event) {
if (refreshListener != null && mElastic != null) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Logger.i(TAG, "down");
break;
case MotionEvent.ACTION_UP:
Logger.i(TAG, "up");
if (state != IPullDownElastic.REFRESHING && isElastic) {
if (state == IPullDownElastic.DONE) {
// 什么都不做
setMargin(refreshTargetTop);
}
if (state == IPullDownElastic.PULL_To_REFRESH) {
state = IPullDownElastic.DONE;
setMargin(refreshTargetTop);
changeHeaderViewByState(state, false);
Logger.i(TAG, "由下拉刷新状态,到done状态");
}
if (state == IPullDownElastic.RELEASE_To_REFRESH) {
state = IPullDownElastic.REFRESHING;
setMargin(0);
changeHeaderViewByState(state, false);
onRefresh();
Logger.i(TAG, "由松开刷新状态,到done状态");
}
}
isElastic = false;
break;
case MotionEvent.ACTION_MOVE:
Logger.i(TAG, "move");
int tempY = (int) event.getY();
if (state != IPullDownElastic.REFRESHING && isElastic) {
// 可以松手去刷新了
if (state == IPullDownElastic.RELEASE_To_REFRESH) {
if (((tempY - startY) / RATIO < headContentHeight)
&& (tempY - startY) > 0) {
state = IPullDownElastic.PULL_To_REFRESH;
changeHeaderViewByState(state, true);
Logger.i(TAG, "由松开刷新状态转变到下拉刷新状态");
} else if (tempY - startY <= 0) {
state = IPullDownElastic.DONE;
changeHeaderViewByState(state, false);
Logger.i(TAG, "由松开刷新状态转变到done状态");
}
}
if (state == IPullDownElastic.DONE) {
if (tempY - startY > 0) {
state = IPullDownElastic.PULL_To_REFRESH;
changeHeaderViewByState(state, false);
}
}
if (state == IPullDownElastic.PULL_To_REFRESH) {
// 下拉到可以进入RELEASE_TO_REFRESH的状态
if ((tempY - startY) / RATIO >= headContentHeight) {
state = IPullDownElastic.RELEASE_To_REFRESH;
changeHeaderViewByState(state, false);
Logger.i(TAG, "由done或者下拉刷新状态转变到松开刷新");
} else if (tempY - startY <= 0) {
state = IPullDownElastic.DONE;
changeHeaderViewByState(state, false);
Logger.i(TAG, "由DOne或者下拉刷新状态转变到done状态");
}
}
if (tempY - startY > 0) {
setMargin((tempY - startY)/2 + refreshTargetTop);
}
}
break;
}
}
}
/**
*
*/
private void setMargin(int top) {
LinearLayout.LayoutParams lp = (LayoutParams) mElastic.getElasticLayout()
.getLayoutParams();
lp.topMargin = top;
// 修改后刷新
mElastic.getElasticLayout().setLayoutParams(lp);
mElastic.getElasticLayout().invalidate();
}
private void changeHeaderViewByState(int state, boolean isBack) {
mElastic.changeElasticState(state, isBack);
switch (state) {
case IPullDownElastic.RELEASE_To_REFRESH:
mElastic.showArrow(View.VISIBLE);
mElastic.showProgressBar(View.GONE);
mElastic.showLastUpdate(View.VISIBLE);
mElastic.setTips(note_release_to_refresh);
mElastic.clearAnimation();
mElastic.startAnimation(animation);
Logger.i(TAG, "当前状态,松开刷新");
break;
case IPullDownElastic.PULL_To_REFRESH:
mElastic.showArrow(View.VISIBLE);
mElastic.showProgressBar(View.GONE);
mElastic.showLastUpdate(View.VISIBLE);
mElastic.setTips(note_pull_to_refresh);
mElastic.clearAnimation();
// 是由RELEASE_To_REFRESH状态转变来的
if (isBack) {
mElastic.startAnimation(reverseAnimation);
}
Logger.i(TAG, "当前状态,下拉刷新");
break;
case IPullDownElastic.REFRESHING:
mElastic.showArrow(View.GONE);
mElastic.showProgressBar(View.VISIBLE);
mElastic.showLastUpdate(View.GONE);
mElastic.setTips(note_refreshing);
mElastic.clearAnimation();
Logger.i(TAG, "当前状态,正在刷新...");
break;
case IPullDownElastic.DONE:
mElastic.showProgressBar(View.GONE);
mElastic.clearAnimation();
// arrowImageView.setImageResource(R.drawable.goicon);
// tipsTextview.setText("下拉刷新");
// lastUpdatedTextView.setVisibility(View.VISIBLE);
Logger.i(TAG, "当前状态,done");
break;
}
}
private void onRefresh() {
// downTextView.setVisibility(View.GONE);
// scroller.startScroll(0, i, 0, 0 - i);
// invalidate();
if (refreshListener != null) {
refreshListener.onRefresh(this);
}
}
/**
*
*/
@Override
public void computeScroll() {
// if (scroller.computeScrollOffset()) {
// int i = this.scroller.getCurrY();
// LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) this.refreshView
// .getLayoutParams();
// int k = Math.max(i, refreshTargetTop);
// lp.topMargin = k;
// this.refreshView.setLayoutParams(lp);
// this.refreshView.invalidate();
// invalidate();
// }
}
/**
* 结束刷新事件,UI刷新完成后必须回调此方法
* @param text 一般传入:“上次更新时间:12:23”
*/
public void finishRefresh(String text) {
if (mElastic == null) {
Logger.d(TAG, "finishRefresh mElastic:" + mElastic);
return;
}
state = IPullDownElastic.DONE;
mElastic.setLastUpdateText(text);
changeHeaderViewByState(state,false);
Logger.i(TAG, "执行了=====finishRefresh");
mElastic.showArrow(View.VISIBLE);
mElastic.showLastUpdate(View.VISIBLE);
setMargin(refreshTargetTop);
// scroller.startScroll(0, i, 0, refreshTargetTop);
// invalidate();
}
private boolean canScroll() {
View childView;
if (getChildCount() > 1) {
childView = this.getChildAt(1);
if (childView instanceof AbsListView) {
int top = ((AbsListView) childView).getChildAt(0).getTop();
int pad = ((AbsListView) childView).getListPaddingTop();
if ((Math.abs(top - pad)) < 3
&& ((AbsListView) childView).getFirstVisiblePosition() == 0) {
return true;
} else {
return false;
}
} else if (childView instanceof ScrollView) {
if (((ScrollView) childView).getScrollY() == 0) {
return true;
} else {
return false;
}
}
}
return canScroll(this);
}
/**
* 子类重写此方法可以兼容其它的子控件,目前只兼容AbsListView和ScrollView
* @param view
* @return
*/
public boolean canScroll(PullDownScrollView view) {
return false;
}
private void printMotionEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Logger.d(TAG, "down");
break;
case MotionEvent.ACTION_MOVE:
Logger.d(TAG, "move");
break;
case MotionEvent.ACTION_UP:
Logger.d(TAG, "up");
default:
break;
}
}
/**
* 刷新监听接口
*/
public interface RefreshListener {
public void onRefresh(PullDownScrollView view);
}
}
4.接口:
Java代码 收藏代码
import android.view.View;
import android.view.animation.Animation;
/**
* @author xwangly@163.com
* @date 2013-7-10
* 下拉控件接口
*/
public interface IPullDownElastic {
public final static int RELEASE_To_REFRESH = 0;
public final static int PULL_To_REFRESH = 1;
public final static int REFRESHING = 2;
public final static int DONE = 3;
public View getElasticLayout();
public int getElasticHeight();
public void showArrow(int visibility);
public void startAnimation(Animation animation);
public void clearAnimation();
public void showProgressBar(int visibility);
public void setTips(String tips);
public void showLastUpdate(int visibility);
public void setLastUpdateText(String text);
/**
* 可以不用实现此方法,PullDownScrollView会处理ElasticLayout布局中的状态
* 如果需要特殊处理,可以实现此方法进行处理
*
* @param state @see RELEASE_To_REFRESH
* @param isBack 是否是松开回退
*/
public void changeElasticState(int state, boolean isBack);
}
5.默认实现:
Java代码 收藏代码
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* @author xwangly@163.com
* @date 2013-7-10
* 默认下拉控件布局实现
*/
public class PullDownElasticImp implements IPullDownElastic {
private View refreshView;
private ImageView arrowImageView;
private int headContentHeight;
private ProgressBar progressBar;
private TextView tipsTextview;
private TextView lastUpdatedTextView;
private Context mContext;
public PullDownElasticImp(Context context) {
mContext = context;
init();
}
private void init() {
// 刷新视图顶端的的view
refreshView = LayoutInflater.from(mContext).inflate(
R.layout.refresh_top_item, null);
// 指示器view
arrowImageView = (ImageView) refreshView
.findViewById(R.id.head_arrowImageView);
// 刷新bar
progressBar = (ProgressBar) refreshView
.findViewById(R.id.head_progressBar);
// 下拉显示text
tipsTextview = (TextView) refreshView.findViewById(R.id.refresh_hint);
// 下来显示时间
lastUpdatedTextView = (TextView) refreshView
.findViewById(R.id.refresh_time);
headContentHeight = Utils.dip2px(mContext, 50);
}
/**
* @return
*
*/
@Override
public View getElasticLayout() {
return refreshView;
}
/**
* @return
*
*/
@Override
public int getElasticHeight() {
return headContentHeight;
}
/**
* @param show
*
*/
@Override
public void showArrow(int visibility) {
arrowImageView.setVisibility(visibility);
}
/**
* @param animation
*
*/
@Override
public void startAnimation(Animation animation) {
arrowImageView.startAnimation(animation);
}
/**
*
*
*/
@Override
public void clearAnimation() {
arrowImageView.clearAnimation();
}
/**
* @param show
*
*/
@Override
public void showProgressBar(int visibility) {
progressBar.setVisibility(visibility);
}
/**
* @param tips
*
*/
@Override
public void setTips(String tips) {
tipsTextview.setText(tips);
}
/**
* @param show
*
*/
@Override
public void showLastUpdate(int visibility) {
lastUpdatedTextView.setVisibility(visibility);
}
/**
* @param text
*
*/
public void setLastUpdateText(String text) {
lastUpdatedTextView.setText(text);
}
/**
* @param state
* @param isBack
*
*/
@Override
public void changeElasticState(int state, boolean isBack) {
// TODO Auto-generated method stub
}
}
6.默认实现的布局:
Java代码 收藏代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="-50.0dip"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0.0dip"
android:layout_weight="1.0"
android:gravity="center"
android:orientation="horizontal" >
<!-- 箭头图像、进度条 -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="30dip" >
<!-- 箭头 -->
<ImageView
android:id="@+id/head_arrowImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/goicon" />
<!-- 进度条 -->
<ProgressBar
android:id="@+id/head_progressBar"
style="@android:style/Widget.ProgressBar.Small.Inverse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<!-- 提示 -->
<TextView
android:id="@+id/refresh_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#f2f2f2"
android:textSize="16sp" />
<!-- 最近更新 -->
<TextView
android:id="@+id/refresh_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上次更新"
android:textColor="#b89766"
android:textSize="10sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
6.图片资源:
@drawable/goicon