今天是周三,打开多点领取免邮券,然后就看到了他一个很好玩的控件。就是这个效果:
ScrollVIew下拉回弹,还遮住半张卡片,就不让我看,就不让我看,有趣,作为一个自身的A货达人,这不学习模仿一下不舒服啊,然后经过奋战一下午,咳咳,效果如下:
咦,暴露了我晚上十二点还制作Gif哎。
好了废话不多说,思路加代码:
1.看那个效果往上滑能带动卡片,这是一个整体的ScrollView
2.下拉能改变View形状,好里边在加个白色View,这里为了里边好添加东西就加个线性布局吧先
嗯!开码:
布局文件:
<com.example.renyuhang.fangduodian.DuoDianScrollView
android:id="@+id/main_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"> //这个never是必须的,有的手机自带能下拉一部分,不符合我的设定,我不让他动他居然动,砍掉
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="150dp"
android:elevation="1dp"
app:cardBackgroundColor="#292421"
app:cardCornerRadius="10dp" />
<com.example.renyuhang.fangduodian.DuoDianLayout
android:id="@+id/change_lin"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="190dp" //这里随便写的,盖住了190-150,40dp,本来我是写的50后台因为加上弧度就又缩小了
android:background="#00000000" //这个也是必须的,不然绘制完形状背景不透明
android:elevation="2dp"
android:minHeight="1000dp"></com.example.renyuhang.fangduodian.DuoDianLayout>
</RelativeLayout>
</com.example.renyuhang.fangduodian.DuoDianScrollView>
布局完之后:
然后加下拉回弹:
重写ScrollView:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (contentView == null) {
return super.dispatchTouchEvent(ev);
}
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
canPullUp = isCanPullUp();
canPullDown = isCanPullDown();
startY = ev.getY();
break;
case MotionEvent.ACTION_UP:
if (!isMove) break;//没有移动过 不用回弹
//View回弹
ValueAnimator valueAnimator = ValueAnimator.ofInt(changeView.getTop(), originalRect.top);
valueAnimator.setDuration(300);
valueAnimator.start();
valueAnimator.setInterpolator(new OvershootInterpolator(4f)); //
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
changeView.layout(originalRect.left, (int) valueAnimator.getAnimatedValue(), originalRect.right, changeView.getBottom() + (int) valueAnimator.getAnimatedValue() - changeView.getTop());
Log.i("ssssssssssssssssss", valueAnimator.getAnimatedValue() + "===" + originalRect.top);
//小于10是想让他回弹差10的时候开启动画,为了视觉效果好一点
if (originalRect.top - (int) valueAnimator.getAnimatedValue() <= 10) {
//当回到原位置时曲线跳动
profileAnimator();
}
}
});
// 设置回到正常的布局位置
//抬起手了就重置
canPullDown = false;
canPullUp = false;
isMove = false;
break;
case MotionEvent.ACTION_MOVE:
//在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度
if (!canPullDown && !canPullUp) {
startY = ev.getY();
canPullDown = isCanPullDown();
canPullUp = isCanPullUp();
break;
}
//计算手指移动的距离
float nowY = ev.getY();
int deltaY = (int) (nowY - startY);
//是否应该移动布局
boolean shouldMove =
(canPullDown && deltaY > 0) //可以下拉, 并且手指向下移动
|| (canPullUp && deltaY < 0) //可以上拉, 并且手指向上移动
|| (canPullUp && canPullDown); //既可以上拉也可以下拉(这种情况出现在ScrollView包裹的控件比ScrollView还小)
if (shouldMove) {
//计算偏移量
int offset = (int) (deltaY * 0.3);
//随着手指的移动而移动布局
if (deltaY < 150 * 5) {//防止下拉太多,随便给个距离,我demo里没加px2dp转换工具类
changeView.layout(originalRect.left, originalRect.top + offset, originalRect.right, originalRect.bottom + offset);
}
isMove = true; //记录移动了布局
}
}
return super.dispatchTouchEvent(ev);
}
//判断是否滚动到顶部
private boolean isCanPullDown() {
return getScrollY() == 0 ||
contentView.getHeight() < getHeight() + getScrollY();
}
本来想解释一下,一看写的时候都已经备注了,算了。现在有了滑动回弹,哦哦对注意一点, valueAnimator.setInterpolator(new OvershootInterpolator(4f));
这个回弹时动画的差值器就是原生的,如果觉得效果不太好可以重写一下,我懒得写了,我今天太累了,最近在辟谷,没什么力气。接下来绘制弧度:
也就是我的DuoDianeLayout里,oh,请原谅我的拼音起名,太随意了不会被多点律师函警告吧。
@Override
protected void onDraw(Canvas c) {
super.onDraw(c);
Paint paint = new Paint();
//设置抗锯齿
paint.setAntiAlias(true);
//获得画布宽高
int canvasWidth = c.getWidth();
int canvasHeight = c.getHeight();
//创建一个空布局的画布
Bitmap bitmap = Bitmap.createBitmap(canvasWidth, canvasHeight, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(Color.argb(0, 0, 0, 0));
Canvas canvas = new Canvas(bitmap);
//绘制第一层长方形也就是整个的背景
paint.setColor(Color.parseColor("#ffffff"));
canvas.drawRect(0, 0, canvasWidth, canvasHeight, paint);
//绘制第二层加入贝塞尔曲线,也就是和第一层交集去掉的部分,
paint.setColor(Color.parseColor("#000000"));//颜色无所谓因为不显示
Path pp = new Path();
pp.moveTo(0, 0);
pp.moveTo(x1, y1);
pp.quadTo(x2, y2, x3, y3);
pp.lineTo(x3, y3);
pp.lineTo(x3, 0);
pp.lineTo(0, 0);
//取两层交集显示先绘制的
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
canvas.drawPath(pp, paint);
//最后将画笔去除Xfermode
paint.setXfermode(null);
c.drawBitmap(bitmap, 0, 0, null);
}
ps:
这段比较费点时间,主要是我忘了PorterDuffXfermode这个写法了,上网现学一会。然后出现canvas不透明,又重新建了一个空布局,才搞定以上。
接下来就是改变以上贝塞尔曲线的动画了,哎?我刚才说了贝塞尔曲线?好高级,其实这个曲线是网上找的公式,ahaha
好了,动画就是:
private void profileAnimator() {
//曲线回弹
ay1 = changeView.y1;
ValueAnimator valueAnimator1 = ValueAnimator.ofInt(ly, ly / 2);
valueAnimator1.setDuration(100);
// valueAnimator1.setInterpolator(new OvershootInterpolator(2f));
valueAnimator1.start();
valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int yy = (int) valueAnimator.getAnimatedValue();
Log.i("ttttt", yy + "=" + changeView.y1 + "=" + changeView.y3);
changeView.setxyr(0, ay1, pingWith / 2, yy, pingWith, ay1);
if (yy == ly / 2) {
Log.i("ttttttt", "start");
ValueAnimator valueAnimator2 = ValueAnimator.ofInt(ly / 2, ly);
valueAnimator2.setDuration(100);
valueAnimator2.setInterpolator(new OvershootInterpolator(1f));
valueAnimator2.start();
valueAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int yy = (int) valueAnimator.getAnimatedValue();
Log.i("ttttt", yy + "=" + changeView.y1 + "=" + changeView.y3);
changeView.setxyr(0, ay1, pingWith / 2, yy, pingWith, ay1);
}
});
}
}
});
}
两个属性动画,上弹结束嵌套下弹,调节了半天差值器和回弹弧度,这还是比较满意吧。如果你们觉得不满意就自定义差值器吧,哈哈哈,终于写完了,凌晨一点,oh,最近在辟谷,第二天了,比较萎靡。睡觉。
oh,不!忘了上传demo,骚等骚等。。
怎么上传的都是5分,没积分评论,私发。
ps:z z Z…