商品详情页上拉查看详情

商品详情页上拉查看详情

目录介绍

  • 01.该库介绍
  • 02.效果展示
  • 03.如何使用
  • 04.注意要点
  • 05.优化问题
  • 06.部分代码逻辑
  • 07.参考案例

01.该库介绍

02.效果展示

  • slide.gif

2.1 使用SlideLayout效果

  • image

2.2 使用SlideAnimLayout带有加载动画效果

  • image
  • image

03.如何使用

3.1 第一种,直接上拉加载分页【SlideLayout有两个子ChildView】

  • SlideDetailsLayout有两个子ChildView:一个是商品页layout,一个是详情页layout
  • 在布局中
    <com.ycbjie.slide.SlideLayout
        android:id="@+id/slideDetailsLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:default_panel="front"
        app:duration="200"
        app:percent="0.1">
    
        <!--商品布局-->
        <FrameLayout
            android:id="@+id/fl_shop_main"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
        <!--分页详情webView布局-->
        <include layout="@layout/include_shop_detail"/>
    
    
    </com.ycbjie.slide.SlideLayout>
    
  • 在代码中
    mSlideDetailsLayout.setOnSlideDetailsListener(new SlideLayout.OnSlideDetailsListener() {
        @Override
        public void onStatusChanged(SlideLayout.Status status) {
            if (status == SlideLayout.Status.OPEN) {
                //当前为图文详情页
                Log.e("FirstActivity","下拉回到商品详情");
            } else {
                //当前为商品详情页
                Log.e("FirstActivity","继续上拉,查看图文详情");
            }
        }
    });
    
    //关闭商详页
    mSlideDetailsLayout.smoothClose(true);
    //打开详情页
    mSlideDetailsLayout.smoothOpen(true);
    

3.2 第一种,上拉加载有动画效果,然后展示分页【SlideAnimLayout有三个子ChildView】

  • SlideAnimLayout有三个子ChildView:一个是商品页layout,一个是上拉加载动画layout,一个是详情页layout
  • 在布局中
       <com.ycbjie.slide.SlideAnimLayout
            android:id="@+id/slideDetailsLayout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            app:default_panel="front"
            app:duration="200"
            app:percent="0.1">
    
            <!--商品布局-->
            <FrameLayout
                android:id="@+id/fl_shop_main2"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    
            <!--上拉加载动画布局-->
            <LinearLayout
                android:id="@+id/ll_page_more"
                android:orientation="vertical"
                android:background="@color/colorAccent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <ImageView
                    android:id="@+id/iv_more_img"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:rotation="180"
                    android:layout_gravity="center_horizontal"
                    android:src="@mipmap/icon_details_page_down_loading" />
                <TextView
                    android:id="@+id/tv_more_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginBottom="25dp"
                    android:gravity="center"
                    android:text="测试动画,继续上拉,查看图文详情"
                    android:textSize="13sp" />
            </LinearLayout>
    
            <!--分页详情webView布局-->
            <include layout="@layout/include_shop_detail"/>
    
    
        </com.ycbjie.slide.SlideAnimLayout>
    
  • 在代码中
    mSlideDetailsLayout.setScrollStatusListener(new SlideAnimLayout.onScrollStatusListener() {
        @Override
        public void onStatusChanged(SlideAnimLayout.Status mNowStatus, boolean isHalf) {
            if(mNowStatus== SlideAnimLayout.Status.CLOSE){
                //打开
                if(isHalf){
                    mTvMoreText.setText("释放,查看图文详情");
                    mIvMoreImg.animate().rotation(0);
                    LoggerUtils.i("onStatusChanged---CLOSE---释放"+isHalf);
                }else{//关闭
                    mTvMoreText.setText("继续上拉,查看图文详情");
                    mIvMoreImg.animate().rotation(180);
                    LoggerUtils.i("onStatusChanged---CLOSE---继续上拉"+isHalf);
                }
            }else{
                //打开
                if(isHalf){
                    mTvMoreText.setText("下拉回到商品详情");
                    mIvMoreImg.animate().rotation(0);
                    LoggerUtils.i("onStatusChanged---OPEN---下拉回到商品详情"+isHalf);
                }else{//关闭
                    mTvMoreText.setText("释放回到商品详情");
                    mIvMoreImg.animate().rotation(180);
                    LoggerUtils.i("onStatusChanged---OPEN---释放回到商品详情"+isHalf);
                }
            }
        }
    });
    
    //关闭商详页
    mSlideDetailsLayout.smoothClose(true);
    //打开详情页
    mSlideDetailsLayout.smoothOpen(true);
    

04.注意要点

  • 针对SlideDetailsLayout仅获取子节点中的前两个View
    • 其中第一个作为Front,即商品页;第二个作为Behind,即图文详情WebView页面。具体看代码:
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        final int childCount = getChildCount();
        if (1 >= childCount) {
            throw new RuntimeException("SlideDetailsLayout only accept child more than 1!!");
        }
        mFrontView = getChildAt(0);
        mBehindView = getChildAt(1);
        if(mDefaultPanel == 1){
            post(new Runnable() {
                @Override
                public void run() {
                    //默认是关闭状态的
                    smoothOpen(false);
                }
            });
        }
    }
    
  • 针对SlideAnimLayout仅获取子节点中三个View,且第二个为动画节点View
    • 其中第一个作为Front,即商品页;第二个作为anim,即上拉动画view。第三个作为Behind,即图文详情WebView页面。具体看代码:
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        final int childCount = getChildCount();
        if (1 >= childCount) {
            throw new RuntimeException("SlideDetailsLayout only accept childs more than 1!!");
        }
        mFrontView = getChildAt(0);
        mAnimView = getChildAt(1);
        mBehindView = getChildAt(2);
        mAnimView.post(new Runnable() {
            @Override
            public void run() {
                animHeight = mAnimView.getHeight();
                LoggerUtils.i("获取控件高度"+animHeight);
            }
        });
        if(mDefaultPanel == 1){
            post(new Runnable() {
                @Override
                public void run() {
                    //默认是关闭状态的
                    smoothOpen(false);
                }
            });
        }
    }
    

05.优化问题

  • 异常情况保存状态
    @Override
    protected Parcelable onSaveInstanceState() {
        SavedState ss = new SavedState(super.onSaveInstanceState());
        ss.offset = mSlideOffset;
        ss.status = mStatus.ordinal();
        return ss;
    }
    
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        mSlideOffset = ss.offset;
        mStatus = Status.valueOf(ss.status);
        if (mStatus == Status.OPEN) {
            mBehindView.setVisibility(VISIBLE);
        }
        requestLayout();
    }
    
  • 当页面销毁的时候,移除listener监听,移除动画资源
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        setScrollStatusListener(null);
        setOnSlideStatusListener(null);
        if (animator!=null){
            animator.cancel();
            animator = null;
        }
    }
    

06.部分代码逻辑

6.1 如何实现ScrollView在最顶部或者最底部的时候,不消费事件

  • 具体逻辑在dispatchTouchEvent分发事件中,当滑动到顶部或者底部的时候,则直接让父View消费事件。其他情况是自己是将事件会向上返还给View的父节点。
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = ev.getX();
                downY = ev.getY();
                //如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许
                //如果子节点不希望父进程拦截触摸事件,则为true。
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = ev.getX() - downX;
                float dy = ev.getY() - downY;
                boolean allowParentTouchEvent;
                if (Math.abs(dy) > Math.abs(dx)) {
                    if (dy > 0) {
                        //位于顶部时下拉,让父View消费事件
                        allowParentTouchEvent = isTop();
                    } else {
                        //位于底部时上拉,让父View消费事件
                        allowParentTouchEvent = isBottom();
                    }
                } else {
                    //水平方向滑动
                    allowParentTouchEvent = true;
                }
                getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent);
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    

6.2 如何实现商品页和详情页之间的滑动,如何处理上拉加载控件的动画效果

  • SlideAnimLayout有三个子ChildView:一个是商品页layout,一个是上拉加载动画layout,一个是详情页layout
  • 通过onInterceptTouchEvent进行事件拦截后,在onTouchEvent方法中对触摸信息做进一步处理可以实现竖直方向的滑动
    • 当商品页ScrollView滑动到底部时,则直接让父View消费事件,该父View也就是SlideAnimLayout
    • 在onInterceptTouchEvent中,当打开详情页后(也就是CLOSE状态),向下拉动,当y轴滑动位移绝对值大于触摸移动的像素距离,并且当y轴滑动位移大于0,则拦截事件分发自己消费事件
    • 在onInterceptTouchEvent中,当关闭详情页后(也就是OPEN状态),向上拉动,当y轴滑动位移绝对值大于触摸移动的像素距离,并且当y轴滑动位移小于0,则拦截事件分发自己消费事件
    • 当处在商品页时,向上拉动;或者处于详情页时,向下拉动,在拉动过程中去改变mSlideOffset值,并且调用requestLayout()方法去绘制
    • 在屏幕区域滑动两个面板只需要改变两个面板在y轴方向的位移(有正负方向)即可。滑动的标尺是控件相对于Top的移动,且所有的位移计算都是基于该标尺。在切换面板时只需要知道对应的offset值即可……
  • 如何处理上拉加载控件的动画效果
    • 添加一个listener监听,可以监听到状态,以及是否达到一半距离,主要是和offset比较,当到达一半距离的时候,这个时候用属性动画将箭头view旋转180度即可实现。
    • 既然要监听滑动距离,则首先要获取该加载控件的高度animHeight,那么在哪里获取比较合适呢?可以在onFinishInflate()方法中,用post形式获取控件高度。
  • 那么如何使滑动生效,并且看上去比较连贯
    • 自定义布局中有非常重要的两个环节onMeasure(测量)和onLayout(布局)。测量决定了View的所占的大小,布局决定了View所处的位置。实现滑动的关键思路就在这里,我们在onLayout方法中根据通过onInterceptTouchEvent、onTouchEvent得到的滑动信息进行计算而得到布局的位置信息,并把这个位置信息设置到子View上面即可实现滑动。
  • 滑动后松开手指如何实现滚动效果
    • 也就是说,当处在商品页时,向上拉动,拉动位移大于一半时,松开手指,则直接滑动到下一页详情页页面
    • 具体逻辑在finishTouchEvent方法中,它主要是记录offset值,以及close或open状态下视图的高度,还有是否发生切换变化
    • 最后开启动画,在动画过程中添加动画update的监听,在该方法中去requestLayout()控件,这样就达到滚动效果了。动画滚动结束后,如果是open状态并且是第一次显示,则设置详情页控件可见。
  • 如何使滚动效果比较自然,或者如何调整滚动时长
    • 可以自定义设置时间,直接在布局中设置……

07.参考案例

08.其他更多

01.关于博客汇总链接

02.关于我的博客

项目地址:https://github.com/yangchong211/YCShopDetailLayout

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,327评论 0 17
  • 自己总结的Android开源项目及库。 github排名https://github.com/trending,g...
    passiontim阅读 2,526评论 1 26
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,665评论 0 3
  • 东西有点多,但是资源绝对nice,自己都全部亲身体验过了,大家可放心使用 github排名: https://gi...
    Rance935阅读 11,162评论 26 312
  • 看书的时候感觉文思泉涌,合上书本一无所有,这是我的状态,归结原因应该是我看书时没有明确的目的 被朋友推着加了一个任...
    筱艺整理阅读 187评论 0 0