上上周写的一个demo,仿照网易一元夺宝的下拉刷新效果。
原效果是(第一部分)一个小太阳拉下来,然后松开回弹上去,
(第二部分)再掉下来一个硬币进行中轴旋转。
本文实现的效果的是第一部分的,效果演示图如下:
Gif图看起来比较卡顿。。。其实真机演示效果还是很流畅的。
下面分析实现过程:
当时因为时间有限没有写在下拉刷新的组件中,也没有封装成一个单独的组件,只是在主布局后面写了一个View然后实现相应的操作,进行封装并不难,这里就不花时间BB了,下面是布局文件:
布局文件预览:
因为设置了透明,所以这里在没有item的情况下是能看到后面的布局的。
代码实现:
通过下拉刷新类NGReFlashListView进行事件判断,然后通过回调把相应的事件传递给NGImageView中改变视图显示。
下面是两个类的源代码:
NGReFlashListView类:
package location.haidian.com.wypulltoreflush.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
import location.haidian.com.wypulltoreflush.R;
public class NGReFlashListView extends ListView implements OnScrollListener {
private View header;
private int headerHeight;
private int firstVisibleItem;
private int scrollState;
private final int NONE = 0;
private final int PULL = 1;
private final int RELESE = 2;
private final int REFLASHING = 3;
public NGReFlashListView(Context context) {
super(context);
initView(context);
}
public NGReFlashListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public NGReFlashListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
/**
* @param context
*/
private void initView(Context context) {
LayoutInflater inflater = LayoutInflater.from(context);
header = inflater.inflate(R.layout.layout_heard, null);
measureView(header);
headerHeight = header.getMeasuredHeight();
topPadding(-headerHeight);
this.addHeaderView(header);
this.setOnScrollListener(this);
}
/**
* @param view
*/
private void measureView(View view) {
ViewGroup.LayoutParams p = view.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int height;
int tempHeight = p.height;
if (tempHeight > 0) {
height = MeasureSpec.makeMeasureSpec(tempHeight,
MeasureSpec.EXACTLY);
} else {
height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
view.measure(width, height);
}
/**
* @param topPadding
*/
private void topPadding(int topPadding) {
header.setPadding(header.getPaddingLeft(), topPadding,
header.getPaddingRight(), header.getPaddingBottom());
header.invalidate();
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.scrollState = scrollState;
}
boolean isRemark;//初始化标识
int startY;
int state;
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (firstVisibleItem == 0) {
onRlvScrollListener.onScrollYChangged(0, 0);
isRemark = true;
startY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
onMove(ev);
break;
case MotionEvent.ACTION_UP:
if (state == RELESE) {
onRlvScrollListener.onScrollYChangged(0, 3);
state = REFLASHING;
reflashViewByState();
//iReflashListener.onReflash();
} else if (state == PULL) {
state = NONE;
onRlvScrollListener.onScrollYChangged(0, 0);
isRemark = false;
reflashViewByState();
}
break;
}
return super.onTouchEvent(ev);
}
int space; //间距
int topPadding; //headview距离顶端的距离,初始-200
/**
* @param ev
*/
private void onMove(MotionEvent ev) {
if (!isRemark) {
return;
}
int tempY = (int) ev.getY();
space = tempY - startY;
if (space >= 230) {
space = 230;
}
topPadding = space - headerHeight;
onRlvScrollListener.onScrollYChangged(space, state);
switch (state) {
case NONE:
if (space > 0) {
state = PULL;
reflashViewByState();
}
break;
case PULL:
topPadding(topPadding);
if (space == headerHeight + 30
&& scrollState == SCROLL_STATE_TOUCH_SCROLL) {
state = RELESE;
reflashViewByState();
}
break;
case RELESE:
if (space < headerHeight + 30) {
state = PULL;
reflashViewByState();
} else if (space <= 0) {
state = NONE;
isRemark = false;
reflashViewByState();
}
break;
}
}
/**
* 控制下拉刷新的图像文字显示
*/
private void reflashViewByState() {
switch (state) {
case NONE:
//缓慢滑上去
topPadding(-headerHeight);
break;
case PULL:
break;
case RELESE:
break;
case REFLASHING:
break;
}
}
/**
* 刷新完成
*/
public void reflashComplete() {
state = NONE;
onRlvScrollListener.onScrollYChangged(0, state);
isRemark = false;
reflashViewByState();
}
//绘制背景的接口
public interface OnRlvScrollListener {
void onScrollYChangged(int Y, int state);
}
private OnRlvScrollListener onRlvScrollListener;
public void setOnRlvScrollListener(OnRlvScrollListener onRlvScrollListener) {
this.onRlvScrollListener = onRlvScrollListener;
}
}
实现比较简单,根据传来的回调状态改变进行小太阳和两只球球手以及手臂的绘制就可以了。
package location.haidian.com.wypulltoreflush.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
import location.haidian.com.wypulltoreflush.R;
/**
* Created by nangua on 2016/8/28.
*/
public class NGImgView extends ImageView implements NGReFlashListView.OnRlvScrollListener {
private float scale;
private final int NONE = 0;
private final int PULL = 1;
private final int RELESE = 2;
private final int REFLASHING = 3;
public int state = 0;
private float WITH; //屏幕总宽
private float scrollY;
private Bitmap sun0;
public float time = 0;
public NGImgView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private void initView() {
sun0 = ((BitmapDrawable) getResources().getDrawable(R.drawable.ic_sun0)).getBitmap();
scale = this.getResources().getDisplayMetrics().density;
}
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setAntiAlias(true);//抗锯齿
paint.setColor(Color.parseColor("#C4885A"));
Log.d("xiaojingyu","State:" + state);
if (state!=3) {
time = 0; //重置时间
//画两只球球手
canvas.drawCircle(WITH / 2 - 40 * scale, 55 * scale, 5 * scale, paint);
canvas.drawCircle(WITH / 2 + 40 * scale, 55 * scale, 5 * scale, paint);
//画笑脸
canvas.drawBitmap(sun0,
null,
new RectF(WITH / 2 - 50,
55 * scale + scrollY - (scrollY / 230) * 50,
WITH / 2 + 50,
55 * scale + scrollY - (scrollY / 230) * 50 + 100),
null
);
//画手臂
paint.setStrokeWidth(1);
canvas.drawLine(WITH / 2 - 40 * scale, 55 * scale,
WITH / 2 - 50, 55 * scale + scrollY,
paint
);
canvas.drawLine(WITH / 2 + 40 * scale, 55 * scale,
WITH / 2 + 50, 55 * scale + scrollY,
paint
);
} else if (state == 3) {
//1秒钟拉上去,2秒钟旋转
//第一秒
if (time < 30) {
time += 1;
//画两只球球手
canvas.drawCircle(WITH / 2 - 40 * scale, 55 * scale, 5 * scale, paint);
canvas.drawCircle(WITH / 2 + 40 * scale, 55 * scale, 5 * scale, paint);
//画笑脸
canvas.drawBitmap(sun0,
null,
new RectF(WITH / 2 - 50,
55 * scale + 180 - (time / 30) * 230,
WITH / 2 + 50,
55 * scale + 280 - (time / 30) * 230),
null
);
//画手臂
paint.setStrokeWidth(1);
canvas.drawLine(WITH / 2 - 40 * scale, 55 * scale,
WITH / 2 - 50,
55 * scale + 230 - (time / 30) * 230,
paint
);
canvas.drawLine(WITH / 2 + 40 * scale, 55 * scale,
WITH / 2 + 50,
55 * scale + 230 - (time / 30) * 230,
paint
);
postInvalidateDelayed(1);
} else {
if (!isBeginMainAnimation) {
isBeginMainAnimation = true;
iReflashListener.onReflash();
}
}
}
super.onDraw(canvas);
}
public static boolean isBeginMainAnimation = false;
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
WITH = this.getWidth();
}
@Override
public void onScrollYChangged(int Y, int state) {
this.state = state;
switch (state) {
case NONE:
break;
case PULL:
//下拉
scrollY = Y;
break;
case RELESE:
break;
case REFLASHING:
break;
}
}
private IReflashListener iReflashListener; //回调接口
public void setInterface(IReflashListener iReflashListener) {
this.iReflashListener = iReflashListener;
}
/**
* @author Administrator
*/
public interface IReflashListener {
void onReflash();
}
}