两种图片下拉放大效果实现(自定义CoordinatorLayout以及自定义Recylerview)
阅读原文时间:2023年07月12日阅读:1

本文是基于折叠布局实现的图片上拉滑动,下拉图片放大,松手放大的效果,先看下效果图。

实现原理:

1.使用CoordinatorLayout自带效果实现上滑。

2.重写CoordinatorLayout触摸事件,在分发之前判断当前是否是在最顶部,并且是下拉操作。

是:进行图片放大,平移下面布局;松手后执行图片回弹动画,恢复布局。

否:不处理事件,滑动事件自动交给下面的Nestscrollview

代码实现如下:

public class CustomCoordinatorLayout extends CoordinatorLayout {
private View mZoomView;
private int mZoomViewWidth;
private int mZoomViewHeight;

private float firstPosition;//记录第一次按下的位置  
private boolean isScrolling;//是否正在缩放  
private boolean isScrollDown;//是否下滑

private float mScrollRate = 0.6f;//缩放系数,缩放系数越大,变化的越大  
private float mReplyRate = 0.3f;//回调系数,越大,回调越慢  
private View mMoveView;  
private View mMoveView2;  
private int height,height2;

public CustomCoordinatorLayout(@NonNull Context context) {  
    super(context);  
}

public CustomCoordinatorLayout(@NonNull Context context, @Nullable AttributeSet attrs) {  
    super(context, attrs);  
}

public CustomCoordinatorLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {  
    super(context, attrs, defStyleAttr);  
}

public void setmZoomView(View mZoomView) {  
    this.mZoomView = mZoomView;  
}

public void setmMoveView(View mMoveView1,View mMoveView2) {  
    this.mMoveView = mMoveView1;  
    this.mMoveView2 = mMoveView2;  
    height = mMoveView.getMeasuredHeight();  
    height2 = mMoveView2.getMeasuredHeight();  
}

@Override  
public boolean dispatchTouchEvent(MotionEvent ev) {  
    int\[\] location = new int\[2\];  
    mZoomView.getLocationOnScreen(location);  
    int y = location\[1\];

    if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {  
        mZoomViewWidth = mZoomView.getMeasuredWidth();  
        mZoomViewHeight = mZoomView.getMeasuredHeight();  
    }

    switch (ev.getAction()) {  
        case MotionEvent.ACTION\_UP:  
            if(isScrollDown) break;  
            //手指离开后恢复图片  
            isScrolling = false;  
            replyImage();  
            break;  
        case MotionEvent.ACTION\_MOVE:  
            if(y != 0) return super.dispatchTouchEvent(ev);  
            isScrollDown = false;  
            if (!isScrolling) {  
                if (getScrollY() == 0) {  
                    firstPosition = ev.getY();// 滚动到顶部时记录位置,否则正常返回  
                } else {  
                    break;  
                }  
            }  
            int distance = (int) ((ev.getY() - firstPosition) \* mScrollRate); // 滚动距离乘以一个系数  
            if (distance < 0) { // 当前位置比记录位置要小,正常返回  
                isScrollDown = true;  
                break;  
            }  
            // 处理放大  
            isScrolling = true;  
            setZoom(distance);

            return super.dispatchTouchEvent(ev);  
    }  
    return super.dispatchTouchEvent(ev);

}

private void scrollDown(float zoom) {  
    mMoveView2.setScrollY(-(int)(height2 \* ((height2 + zoom) / height2)));  
}

//回弹动画  
private void replyImage() {  
    float distance = mZoomView.getMeasuredWidth() - mZoomViewWidth;  
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(distance, 0f).setDuration((long) (distance \* mReplyRate));  
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
        @Override  
        public void onAnimationUpdate(ValueAnimator animation) {  
            setZoom((Float) animation.getAnimatedValue());  
        }

    });  
    valueAnimator.start();  
    mMoveView.setScrollY(height);  
    mMoveView2.setScrollY(height2);  
}

public void setZoom(float zoom) {  
    if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {  
        return;  
    }  
    ViewGroup.LayoutParams lp = mZoomView.getLayoutParams();  
    lp.width = (int) (mZoomViewWidth \* ((mZoomViewWidth + zoom) / mZoomViewWidth));  
    lp.height = (int) (mZoomViewHeight \* ((mZoomViewWidth + zoom) / mZoomViewWidth));  
    ((MarginLayoutParams) lp).setMargins(-(lp.width - mZoomViewWidth) / 2, 0, 0, 0);  
    mZoomView.setLayoutParams(lp);  
    try {  
        CollapsingToolbarLayout parent = (CollapsingToolbarLayout) (mMoveView.getParent());  
        ViewGroup.LayoutParams layoutParams = parent.getLayoutParams();  
        layoutParams.height = lp.height;  
        parent.setLayoutParams(layoutParams);  
    }catch (Exception e){

    }

}  

}

布局文件结构:


<com.google.android.material.appbar.AppBarLayout  
    android:id="@+id/appbar\_layout"  
    android:layout\_width="match\_parent"  
    android:layout\_height = "wrap\_content"  
    >  
    <com.google.android.material.appbar.CollapsingToolbarLayout  
        android:id="@+id/csl\_layout"  
        android:layout\_width="match\_parent"  
        android:layout\_height="wrap\_content"  
        android:fitsSystemWindows="true"  
        app:layout\_scrollFlags="scroll|exitUntilCollapsed">  

      //要放大的图片

      //平移布局一

            <LinearLayout  
                android:id="@+id/mine\_ll\_name"  
                android:layout\_width="match\_parent"  
                android:layout\_height="wrap\_content"  
                android:layout\_marginStart="24dp"  
                android:layout\_marginTop="50dp"  
                android:gravity="center\_vertical">

                <TextView  
                    android:id="@+id/mine\_tv\_name"  
                    android:layout\_width="wrap\_content"  
                    android:layout\_height="wrap\_content"  
                    android:maxLines="1"  
                    android:textColor="@color/yt\_color\_white"  
                    android:textSize="@dimen/yt\_text\_name"  
                    android:textStyle="normal"  
                    tools:text="节操君" />

                <ImageView  
                    android:id="@+id/iv\_ensure\_icon"  
                    android:layout\_width="20dp"  
                    android:layout\_height="20dp"  
                    android:layout\_marginStart="14dp"  
                    android:src="@drawable/ic\_ensure\_icon" />  
            </LinearLayout>

            <TextView  
                android:id="@+id/mine\_tv\_info"  
                android:layout\_width="wrap\_content"  
                android:layout\_height="wrap\_content"  
                android:layout\_below="@id/mine\_ll\_name"  
                android:layout\_alignStart="@id/mine\_ll\_name"  
                android:layout\_marginTop="6dp"  
                android:alpha="0.6"  
                android:background="@drawable/shape\_bg\_gray\_round\_stroke"  
                android:paddingLeft="6dp"  
                android:paddingTop="2dp"  
                android:paddingEnd="6dp"  
                android:paddingBottom="2dp"  
                android:text="我的主页"  
                android:textColor="@color/yt\_color\_white"  
                android:textSize="@dimen/yt\_text\_t1" />

            <View  
                android:id="@+id/v\_personal\_info\_dot"  
                android:layout\_width="8dp"  
                android:layout\_height="8dp"  
                android:layout\_alignTop="@+id/mine\_tv\_info"  
                android:layout\_alignEnd="@+id/mine\_tv\_info"  
                android:background="@drawable/shape\_red\_dot" />

            <TextView  
                android:id="@+id/tv\_mine\_user\_setting"  
                android:layout\_width="wrap\_content"  
                android:layout\_height="wrap\_content"  
                android:layout\_alignBottom="@id/mine\_tv\_info"  
                android:layout\_marginStart="8dp"  
                android:layout\_toEndOf="@+id/mine\_tv\_info"  
                android:alpha="0.6"  
                android:background="@drawable/shape\_bg\_gray\_round\_stroke"  
                android:paddingStart="6dp"  
                android:paddingTop="2dp"  
                android:paddingEnd="6dp"  
                android:paddingBottom="2dp"  
                android:text="编辑主页"  
                android:textColor="@color/yt\_color\_white"  
                android:textSize="@dimen/yt\_text\_t1" />

    //代码省略  
      ...  
        </RelativeLayout>  
    </com.google.android.material.appbar.CollapsingToolbarLayout>

</com.google.android.material.appbar.AppBarLayout>

<androidx.core.widget.NestedScrollView  
    android:id="@+id/nestedScrollView"  
    android:layout\_width="match\_parent"  
    android:layout\_height="match\_parent"  
    android:fillViewport="true"  
    app:layout\_behavior="@string/appbar\_scrolling\_view\_behavior”>

    //平移布局二

    //代码省略

    </LinearLayout>  
</androidx.core.widget.NestedScrollView>

使用就非常简单了,在代码中设置要放大的view以及需要平移的view就可以了。

private fun zoomImage() {
cl_layout.setmMoveView(rl_layout,ll_layout)
cl_layout.setmZoomView(mine_iv_avatar)
}

以上只是个例子用于实现特定布局的动画效果,实际应用可根据场景来自定义view进行操作。

实现效果如下:

实现原理:

重写recylerview的onTouchEvent方法,在顶部往下滑的时候,进行第一个item图片放大及布局下移操作。

好处:多布局中自带滑动,不用处理其他item的滑动,流畅程度100%

代码实现如下:

public class ZoomRecyclerView extends RecyclerView {

private View mZoomView;  
private int mZoomViewWidth;  
private int mZoomViewHeight;  
private int mViewParentHeight;  
private float mScrollRate = 0.3f;//缩放系数,缩放系数越大,变化的越大  
private float mReplyRate = 0.3f;//回调系数,越大,回调越慢  
// 记录首次按下位置  
private float mFirstPosition = 0;  
// 是否正在放大  
private Boolean mScaling = false;  
LinearLayoutManager mLinearLayoutManager ;

public ZoomRecyclerView( Context context) {  
    super(context);  
}

public ZoomRecyclerView( Context context,  AttributeSet attrs) {  
    super(context, attrs);  
}

public ZoomRecyclerView( Context context,  AttributeSet attrs, int defStyleAttr) {  
    super(context, attrs, defStyleAttr);  
}

public void setZoomView(SimpleDraweeView v, LinearLayoutManager linearLayoutManager) {  
    this.mZoomView = v;  
    mLinearLayoutManager = linearLayoutManager ;

}

@Override  
public boolean onTouchEvent(MotionEvent event) {  
    if(mZoomView !=null){  
        if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {  
            mZoomViewWidth = mZoomView.getMeasuredWidth();  
            mZoomViewHeight = mZoomView.getMeasuredHeight();  
        }  
        if(mViewParentHeight <= 0) {  
            try {  
                RelativeLayout parent = (RelativeLayout) mZoomView.getParent();  
                ViewGroup.LayoutParams layoutParams = parent.getLayoutParams();  
                mViewParentHeight = layoutParams.height;  
            }catch (Exception e){}

        }  
        //判断触摸事件  
        switch (event.getAction()) {  
            //触摸结束  
            case MotionEvent.ACTION\_UP:  
                mScaling = false;  
                replyImage();  
                break;  
            //触摸中  
            case MotionEvent.ACTION\_MOVE:  
                //判断是否正在放大 mScaling 的默认值为false  
                if (!mScaling) {  
                    //当图片也就是第一个item完全可见的时候,记录触摸屏幕的位置  
                    if (mLinearLayoutManager.findViewByPosition(mLinearLayoutManager.findFirstVisibleItemPosition()).getTop() == 0) {  
                        //记录首次按下位置  
                        mFirstPosition = event.getY();  
                    } else {  
                        break;  
                    }  
                }  
                // 滚动距离乘以一个系数  
                int distance = (int) ((event.getY() - mFirstPosition) \* mScrollRate);  
                if (distance < 0) {  
                    break;  
                }  
                // 处理放大  
                mScaling = true;  
                setZoom(distance);  
            default:  
                break;  
        }  
    }

    return super.onTouchEvent(event);  
}

private void setZoom(float distance) {  
    if (mZoomViewWidth <= 0 || mZoomViewHeight <= 0) {  
        return;  
    }  
    ViewGroup.LayoutParams lp = mZoomView.getLayoutParams();

    lp.width = (int) (mZoomViewWidth \* ((mZoomViewWidth + distance) / mZoomViewWidth));  
    lp.height = (int) (mZoomViewHeight \* ((mZoomViewWidth + distance) / mZoomViewWidth));  
    mZoomView.setLayoutParams(lp);  
    try {  
        RelativeLayout parent = (RelativeLayout)mZoomView.getParent();  
        ViewGroup.LayoutParams layoutParams = parent.getLayoutParams();  
        layoutParams.height = (int) (mViewParentHeight \* ((mZoomViewWidth + distance) / mZoomViewWidth));  
        parent.setLayoutParams(layoutParams);  
    }catch (Exception e){

    }  
}

/\*\*  
 \* 图片回弹动画  
 \*/  
private void replyImage() {  
    float distance = mZoomView.getMeasuredWidth() - mZoomViewWidth;  
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(distance, 0f).setDuration((long) (distance \* mReplyRate));  
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
        @Override  
        public void onAnimationUpdate(ValueAnimator animation) {  
            setZoom((Float) animation.getAnimatedValue());  
        }

    });  
    valueAnimator.start();

}

布局很简单:


<com.ingtube.common.widget.ZoomRecyclerView  
    android:id="@+id/rv\_personal\_info"  
    android:layout\_width="match\_parent"  
    android:layout\_height="wrap\_content" />

使用也非常简单了。

实现recylerview的滑动监听,在布局为0的时候,设置图片放大及布局下移操作。

rv_personal_info.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
this@PersonalInfoActivity.scrollY = recyclerView.computeVerticalScrollOffset()

                if (scrollY == 0 && pageItems.size != 0) {  
                   rv\_personal\_info.setZoomView(personalPageHeadViewBinder!!.getZoomView(), rv\_personal\_info.layoutManager as? LinearLayoutManager)  
                }  
            }catch (e:Exception){  
                e.printStackTrace()  
            }  
        }  
    })

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器