两种图片下拉放大效果实现(自定义CoordinatorLayout以及自定义Recylerview)


一、自定义CoordinatorLayout实现图片放大功能

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

 

实现原理:

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){ } } }

布局文件结构:

<?xml version="1.0" encoding="utf-8"?>
<com.ingtube.common.widget.CustomCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:id="@+id/cl_layout">

    <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">       //要放大的图片 <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/mine_iv_avatar" android:layout_width="match_parent" android:layout_height="230dp" android:alpha="0.4" android:scaleType="centerCrop" app:layout_collapseMode="parallax"
                />       //平移布局一 <RelativeLayout android:id="@+id/rl_layout" android:layout_width="match_parent" android:layout_height="230dp" android:layout_gravity="bottom" app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="0"
                >

                <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 android:id="@+id/ll_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"
            >     //代码省略 ... </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</com.ingtube.common.widget.CustomCoordinatorLayout>

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

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

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

二、自定义recylerView实现图片放大效果

实现效果如下:

实现原理:

重写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(); }

布局很简单:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">

    <com.ingtube.common.widget.ZoomRecyclerView android:id="@+id/rv_personal_info" android:layout_width="match_parent" android:layout_height="wrap_content" />

</RelativeLayout>

使用也非常简单了。

实现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() } } })

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM