昨天我写了二阶贝塞尔曲线之波浪图之后,结果装逼失败。理由是大佬说这个波浪图的效果很生硬,一般情况下波浪图是配合手势使用的。因此决定改写该控件,实现利用手势实现波浪图。先来一张效果图:
实现思路如下:
一、我本来打算在RecyclerView的item布局文件中自定义控件,但是发现获取不到控件,后来想了一下,这样会把最后一项Item布局撑大,于是还是加在了根布局上。
RecyclerView获取最后一个可见Item有两个方法,但是都需要实现同一个抽象类RecyclerView.OnScrollListener,然后我们可以使用一个boolean的标志位来记录RecyclerView是否加载到底部?代码如下:
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//当滚动结束
if(newState == RecyclerView.SCROLL_STATE_IDLE){
//当前分页加载的demo数据最多只有41条,所以下标值是40
check =((LinearLayoutManager)mRecyclerView.getLayoutManager()).findLastVisibleItemPosition() == 40;
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//得到当前显示的最后一个item的view
View lastChildView = recyclerView.getLayoutManager().getChildAt(recyclerView.getLayoutManager().getChildCount()-1);
//当前分页加载的demo数据最多只有41条,所以下标值是40
check = recyclerView.getLayoutManager().getPosition(lastChildView) == 40;
}
});
二、在onTouch事件判断当前事件为触摸还是还是点击、长按等其它事件,其实这个方法一般就两种:
由于为了不影响长按事件,这里选择通过滑动距离来判断,代码如下:
@Override
public boolean onTouch(View v, MotionEvent event) {
if(check){
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float moveY = event.getY();
if (Math.abs(moveX - downX) > dp5 && Math.abs(moveY - downY) > dp5) {
waveView.setPoint(moveX);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
float upX = event.getX();
float upY = event.getY();
//加载到顶部后的点击或者长按事件
if (Math.abs(upX - downX) < dp5 && Math.abs(upY - downY) < dp5) {
return false;
}
break;
}
return true;
}
return false;
}
三、在长按事件中将滑动过程中的X值传给自定义控件,然后自定义控件根据x值绘制贝塞尔曲线并显示控件。整个过程就是自定义控件有一个公共方法接收参数,并刷新视图。虽然自定义控件的核心还是绘制贝塞尔曲线,但是一些参数有了变化,这里做一个说明。
/**
* Created by 魏兴 on 2017/6/12.
*/
public class WaveView extends View {
private static final String TAG = "WaveView";
//波浪画笔
private Paint mPaint;
//波浪Path类
private Path mPath;
private float controlpoint;
//波纹的中间轴
private int mCenterY;
//屏幕高度
private int mScreenHeight;
//屏幕宽度
private int mScreenWidth;
public WaveView(Context context) {
super(context);
init();
}
public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public WaveView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mPath = new Path();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.LTGRAY);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mScreenHeight = h;
mScreenWidth = w;
mCenterY = mScreenHeight * 3/ 5;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
int start;
int end;
boolean isLeft = true;
if(controlpoint>=mScreenWidth/2){
isLeft = false;
}
start = isLeft ? mCenterY/3:mCenterY;
end = isLeft ? mCenterY:mCenterY/3;
mPath.moveTo(0, start);
mPath.quadTo(controlpoint,mCenterY/3, mScreenWidth,end);
//填充矩形
mPath.lineTo(mScreenWidth, mScreenHeight);
mPath.lineTo(0, mScreenHeight);
mPath.close();
canvas.drawPath(mPath, mPaint);
}
public void setPoint(float x ){
this.controlpoint = x;
setVisibility(VISIBLE);
invalidate();
}
public void rest(){
this.controlpoint = 0;
setVisibility(GONE);
}
}
感觉手势抬起以后贝塞尔曲线的图形直接隐藏还是很生硬,加一个动画:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
int startY;
int endY;
boolean isLeft = true;
if(controlpoint>=mScreenWidth/2){
isLeft = false;
}
startY = isLeft ? mCenterY/3:mCenterY;
endY = isLeft ? mCenterY:mCenterY/3;
mPath.moveTo(0, startY+offset);
mPath.quadTo(controlpoint,0+offset, mScreenWidth,endY+offset);
Log.e(TAG, "onDraw: "+(startY)+" " +0+" "+endY);
//填充矩形
mPath.lineTo(mScreenWidth, mScreenHeight);
mPath.lineTo(0, mScreenHeight);
mPath.close();
canvas.drawPath(mPath, mPaint);
}
public void setPoint(float x ){
if(!isRestting){
this.controlpoint = x;
setVisibility(VISIBLE);
invalidate();
}
}
private boolean isRestting = false;
public void rest(){
// setVisibility(GONE);
// controlpoint = 0;
isRestting = true;
ValueAnimator animator = ValueAnimator.ofInt(0, mScreenHeight);
animator.setDuration(400);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offset = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
setVisibility(GONE);
controlpoint = 0;
mScreenHeight = height;
mCenterY = mScreenHeight * 3/ 5;
offset = 0;
isRestting = false;
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.start();
}
再看看效果: