本文介绍PullZoomView的简单实现,如图:
就是通过下拉ListView或者ScrollView或者更多的View如GridView,RecycleView等等,的时候对Header有一个放大缩小的效果
实现思路就是根据所需要封装的不同的下拉控件来做不同的实现,比如:
ListView:该控件本身有添加Header的功能,我们只需做简单的处理就可以用了,在满足一定条件时做事件拦截,让整个控件向下滚动的时候回传一个value用来改变Header的高度。
ScrollView:这就需要我们自己封装一个Header在ScrollView的孩子控件当中。滚动的时候和ListView做相同的操作即可。
IPullZoom 定义公共接口
PullZoomBase 抽象公共的方法
PullZoomListView ListView的实现
PullZoomScrollView ScrollView的实现
IPullZoom.java
public interface IPullZoom {
void initHeader(TypedArray a);
}
PullZoomBase.java
public abstract class PullZoomBase<T extends View> extends LinearLayout implements IPullZoom {
/**
* 根布局,用来装所有内容
*/
protected T mRootView;
/**
* 定义的显示伸缩效果的View
*/
protected View mZoomView;
/**
* 伸缩效果上展示的内容
*/
protected View mHeadView;
/**
* 是否允许下拉
*/
private boolean isPullEnable = true;
private boolean isZooming;
private boolean isHeadHide;
private boolean isDragging;
private float mLastX;
private float mLastY;
private float mInitX;
private float mInitY;
private int mTouchSlop;
public PullZoomBase(Context context) {
this(context, null);
}
public PullZoomBase(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullZoomBase(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
mRootView = initRootView(context, attrs);
LayoutInflater inflater = LayoutInflater.from(context);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullZoomView);
int zoomResId = a.getResourceId(R.styleable.PullZoomView_zoomview, 0);
if (zoomResId > 0) {
mZoomView = inflater.inflate(zoomResId, null, false);
}
int headResId = a.getResourceId(R.styleable.PullZoomView_headview, 0);
if (headResId > 0) {
mHeadView = inflater.inflate(headResId, null, false);
}
initHeader(a);
a.recycle();
addView(mRootView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!isPullEnable() || isHeadHide()) {
return false;
}
int action = ev.getAction();
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
isDragging = false;
return false;
}
if (action != MotionEvent.ACTION_DOWN && isDragging) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
if (allowStart()) {
mLastX = mInitX = ev.getX();
mLastY = mInitY = ev.getY();
isDragging = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (allowStart()) {
float x = ev.getX();
float y = ev.getY();
float diffX = x - mLastX;
float diffY = y - mLastY;
float diffYAds = Math.abs(diffY);
if (diffYAds > mTouchSlop && diffYAds > Math.abs(diffX)) {
if (diffY >= 1 && allowStart()) {
mLastX = x;
mLastY = y;
isDragging = true;
}
}
}
break;
}
return isDragging;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isPullEnable || isHeadHide()) {
return false;
}
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (allowStart()) {
mLastX = mInitX = event.getX();
mLastY = mInitY = event.getY();
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (allowStart()) {
mLastX = event.getX();
mLastY = event.getY();
final int newScrollValue = Math.round(Math.min(mInitY - mLastY, 0) / 2.0f);
pull(newScrollValue);
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (isDragging) {
isDragging = false;
smoothRestore();
}
break;
}
return false;
}
public boolean isPullEnable() {
return isPullEnable;
}
public void setIsPullEnable(boolean isPullEnable) {
this.isPullEnable = isPullEnable;
}
public boolean isHeadHide() {
return isHeadHide;
}
public void setIsHeadHide(boolean isHeadHide) {
this.isHeadHide = isHeadHide;
}
/**
* 创建根布局,例如ListView,GridView,RecycleView,ScrollView等等
*
* @param context
* @param set
* @return
*/
public abstract T initRootView(Context context, AttributeSet set);
/**
* 判定是否允许开始滚动
*
* @return
*/
public abstract boolean allowStart();
/**
* 传入一个计算值,用来对Header做放大缩小操作
*
* @param value
*/
public abstract void pull(int value);
/**
*
*/
public abstract void smoothRestore();
}
PullZoomListView.java
public class PullZoomListView extends PullZoomBase<ListView> {
private FrameLayout mHeaderContainer;
private int mHeaderHeight;
private SmoothRestore mSmoothRestore;
public static Interpolator mInterpolator = new Interpolator() {
@Override
public float getInterpolation(float input) {
float f = input - 1.0F;
return 1.0F + f * (f * (f * (f * f)));
}
};
public PullZoomListView(Context context) {
this(context, null);
}
public PullZoomListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullZoomListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mSmoothRestore = new SmoothRestore();
}
@Override
public ListView initRootView(Context context, AttributeSet set) {
ListView listview = new ListView(context, set);
return listview;
}
@Override
public void initHeader(TypedArray a) {
mHeaderContainer = new FrameLayout(getContext());
if (mZoomView != null) {
mHeaderContainer.addView(mZoomView);
}
if (mHeadView != null) {
mHeaderContainer.addView(mHeadView);
}
mRootView.addHeaderView(mHeaderContainer);
}
public void setAdapter(BaseAdapter adapter) {
mRootView.setAdapter(adapter);
}
public void setHeaderLayoutParams(AbsListView.LayoutParams params) {
if (mHeaderContainer != null) {
mHeaderContainer.setLayoutParams(params);
mHeaderHeight = params.height;
}
}
public void updateHeader() {
if (mHeaderContainer != null) {
mRootView.removeHeaderView(mHeaderContainer);
mHeaderContainer.removeAllViews();
if (mZoomView != null) {
mHeaderContainer.addView(mZoomView);
}
if (mHeadView != null) {
mHeaderContainer.addView(mHeadView);
}
mHeaderHeight = mHeaderContainer.getHeight();
mRootView.addHeaderView(mHeaderContainer);
}
}
@Override
public boolean allowStart() {
return isFirstItemVisiable();
}
private boolean isFirstItemVisiable() {
Adapter adapter = mRootView.getAdapter();
if (null == adapter || adapter.isEmpty()) {
return true;
} else {
if (mRootView.getFirstVisiblePosition() <= 1) {
View view = mRootView.getChildAt(0);
if (view != null) {
return view.getTop() >= mRootView.getTop();
}
}
}
return false;
}
@Override
public void pull(int value) {
if (mSmoothRestore != null && !mSmoothRestore.isFinish()) {
mSmoothRestore.abort();
}
ViewGroup.LayoutParams params = mHeaderContainer.getLayoutParams();
params.height = Math.abs(value) + mHeaderHeight;
mHeaderContainer.setLayoutParams(params);
}
@Override
public void smoothRestore() {
mSmoothRestore.start(200L);
}
class SmoothRestore implements Runnable {
protected long duration;
protected boolean isFinished;
protected float scale;
protected long starttime;
SmoothRestore() {
}
public void abort() {
isFinished = true;
}
public boolean isFinish() {
return isFinished;
}
public void start(long d) {
if (mZoomView != null) {
starttime = SystemClock.currentThreadTimeMillis();
duration = d;
scale = (float) mHeaderContainer.getBottom() / mHeaderHeight;
isFinished = false;
post(this);
}
}
@Override
public void run() {
if (mZoomView != null) {
float f2;
ViewGroup.LayoutParams params;
if (!isFinished && scale > 1.0D) {
float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) starttime) / (float) duration;
f2 = scale - (scale - 1.0F) * PullZoomListView.mInterpolator.getInterpolation(f1);
params = mHeaderContainer.getLayoutParams();
if (f2 > 1.0F) {
params.height = (int) (f2 * mHeaderHeight);
mHeaderContainer.setLayoutParams(params);
post(this);
return;
}
isFinished = true;
}
}
}
}
}