3764人阅读 评论(10)

简介

Android之实现妙趣横生的粘连布局

原理介绍

public void quadTo (float x1, float y1, float x2, float y2)
Add a quadratic bezier from the last point, approaching control point (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for this contour, the first point is automatically set to (0,0).
Parameters
x1 The x-coordinate of the control point on a quadratic curve
y1 The y-coordinate of the control point on a quadratic curve
x2 The x-coordinate of the end point on a quadratic curve
y2 The y-coordinate of the end point on a quadratic curve

/**
* 画贝塞尔曲线
*
* @param canvas
*/
private void drawBezier(Canvas canvas) {

/* 求三角函数 */
float sin = (float) Math.sin(atan);
float cos = (float) Math.cos(atan);

/* 四个点 */

float footerX1 = mFooterCircle.curx - mFooterCircle.curRadius * sin;
float footerY1 = mFooterCircle.cury + mFooterCircle.curRadius * cos;

float footerX2 = mFooterCircle.curx + mFooterCircle.curRadius * sin;
float footerY2 = mFooterCircle.cury - mFooterCircle.curRadius * cos;

float anchorX = ( mHeaderCircle.curx + mFooterCircle.curx ) / 2;
float anchorY = ( mHeaderCircle.cury + mFooterCircle.cury ) / 2;

/* 画贝塞尔曲线 */
mPath.reset();
mPath.lineTo(footerX2, footerY2);
canvas.drawPath(mPath, mPaint);
}          

设计思路

1、属性列表

public class AdherentLayout extends RelativeLayout {
private Circle mHeaderCircle = new Circle();
private Circle mFooterCircle = new Circle();

//画笔
private Paint mPaint = new Paint();
//画贝塞尔曲线的Path对象
private Path mPath = new Path();
//粘连的颜色
private int mColor = Color.rgb(247,82,49);
//是否粘连着
//本View初始宽度、高度
private int mOriginalWidth;
private int mOriginalHeight;
//是否第一次onSizeChanged
private boolean isFirst = true;
//用户添加的视图（可以不添加）
private View mView;
//是否正在进行动画中
private boolean isAnim = false;
//记录按下的x、y
float mDownX;
float mDownY;
//本View的左上角x、y
private float mX;
private float mY;
//父控件左、上内边距
//默认粘连的最大长度
//头部圆缩小时不能小于这个最小半径
//是否允许可以扯断
private boolean isDismissed = true;
//是否按下
boolean isDown = false;
...
}

/**
* 圆点类
*/
private class Circle{
/**
* 初始坐标x,y
*/
float ox;
float oy;
/**
* 当前坐标x,y
*/
float curx;
float cury;
//初始半径
//当前半径
}

2、圆点绘制

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (isFirst && w > 0 && h > 0) {
mView = getChildAt(0);
//记录初始宽高，用于复原
mOriginalWidth = w;
mOriginalHeight = h;
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
mX = getX()-lp.leftMargin;//起始位置
mY = getY()-lp.topMargin;
ViewGroup mViewGroup = (ViewGroup) getParent();
if(mViewGroup!=null){
}
reset();
isFirst = false;
}
}

/**
* 重置所有参数
*/
public void reset() {
setWidthAndHeight(mOriginalWidth, mOriginalHeight);
if (mView != null) {
if(isFirst){
mView.setX(0);
mView.setY(0);
}else{
}
}
isAnim = false;
}

**
* 根据内边距返回圆的半径
* @return
*/
return
(float)(Math.min(
}

3、圆点脱离

move时计算移动距离，然后根据这个距离去修改mFooterCircle的位置即可

@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);

if (isAnim) return true;

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setWidthAndHeight(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
if (mView != null) {
}
mDownX = event.getRawX();
mDownY = event.getRawY();
//标记按下
isDown = true;
break;
case MotionEvent.ACTION_MOVE:
if(!isDown) break;
//偏移
float detalX = event.getRawX()-mDownX;
float detalY = event.getRawY()-mDownY;

mFooterCircle.curx = mFooterCircle.ox+detalX;
mFooterCircle.cury = mFooterCircle.oy+detalY;
if (mView != null) {
}
break;
case MotionEvent.ACTION_UP:
...
}

4、绘制贝塞尔曲线，起始圆缩放

**
* 处理粘连效果逻辑
*/
//两圆心的距离
float distance = (float) Math.sqrt(Math.pow(mFooterCircle.curx - mHeaderCircle.ox, 2) + Math.pow(mFooterCircle.cury - mHeaderCircle.oy, 2));
//缩放比例
float scale = 1 - distance / mMaxAdherentLength;
if (distance > mMaxAdherentLength && isDismissed) {
}
else
}

5、松开手指，回弹动画

/* x方向 */
ValueAnimator xValueAnimator = ValueAnimator.ofFloat(mFooterCircle.curx, mFooterCircle.ox);
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mFooterCircle.curx = (float) (Float)animation.getAnimatedValue();
invalidate();
}
});

/**
* 开始粘连动画
*/
private void startAnim() {

/* x方向 */
ValueAnimator xValueAnimator = ValueAnimator.ofFloat(mFooterCircle.curx, mFooterCircle.ox);
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mFooterCircle.curx = (float) (Float)animation.getAnimatedValue();
invalidate();
}
});

/* y方向 */
ValueAnimator yValueAnimator = ValueAnimator.ofFloat(mFooterCircle.cury, mFooterCircle.oy);
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mFooterCircle.cury = (float) (Float)animation.getAnimatedValue();
invalidate();
}
});

/* 用户添加的视图x、y方向 */
ObjectAnimator objectAnimator = null;
if (mView != null) {
objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mView, pvhX, pvhY);
}

/* 动画集合 */
AnimatorSet animSet = new AnimatorSet();
if (mView != null)
animSet.playTogether(xValueAnimator,yValueAnimator,objectAnimator);
else
animSet.playTogether(xValueAnimator,yValueAnimator);
animSet.setInterpolator(new BounceInterpolator());
animSet.setDuration(400);
animSet.start();
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
reset();
}
});
}

写在最后

24
0

* 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场
个人资料
• 访问：570150次
• 积分：10200
• 等级：
• 排名：第1731名
• 原创：446篇
• 转载：34篇
• 译文：15篇
• 评论：197条
博客专栏
 leetcode题解(java实现) 文章：0篇 阅读：0
 异步网络请求框架Volley源码解析 文章：8篇 阅读：18865
 Android控件源码解析 文章：13篇 阅读：25892
 剑指offer题解 文章：63篇 阅读：53700
评论排行
最新评论