array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 两种图片下拉放大效果实现(自定义CoordinatorLayout以及自定义Recylerview) - 爱码网

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

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

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

 

实现原理:

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:
            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实现图片放大效果

实现效果如下:

两种图片下拉放大效果实现(自定义CoordinatorLayout以及自定义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:andro>
    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()
                }
            }
        })

 

相关文章: