`

仿淘宝京东拖拽商品详情页上下滚动黏滞效果

 
阅读更多
比较常用的效果,有现成的,如此甚好!:)

import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.snail.labaffinity.R;
import com.snail.labaffinity.adapter.SlideFragmentPagerAdapter;

/**
 * 1 ViewPager+TabLayout as content
 * 2 FragmentTabHost+Fragment as content
 * 3 bug :can not assertain which View is inTouch
 */

public class DragScrollDetailsLayout extends LinearLayout {


    public interface OnSlideFinishListener {
        void onStatueChanged(CurrentTargetIndex status);
    }

    public enum CurrentTargetIndex {
        UPSTAIRS,
        DOWNSTAIRS;

        public static CurrentTargetIndex valueOf(int index) {
            return 1 == index ? DOWNSTAIRS : UPSTAIRS;
        }
    }

    private static final float DEFAULT_PERCENT = 0.3f;
    private static final int DEFAULT_DURATION = 400;


    private int mMaxFlingVelocity;
    private int mMiniFlingVelocity;
    private int mDefaultPanel = 0;
    private int mDuration = DEFAULT_DURATION;
    private float mTouchSlop;
    private float mDownMotionY;
    private float mDownMotionX;
    private float mInitialInterceptY;

    public void setPercent(float percent) {
        mPercent = percent;
    }

    private float mPercent = DEFAULT_PERCENT;
    /**
     * flag for listview or scrollview ,if child overscrolled ,do not judge view region 滚过头了,还是可以滚动
     */
    private boolean mChildHasScrolled;

    private View mUpstairsView;
    private View mDownstairsView;
    private View mCurrentTargetView;

    private Scroller mScroller;

    private VelocityTracker mVelocityTracker;
    private OnSlideFinishListener mOnSlideDetailsListener;
    private CurrentTargetIndex mCurrentViewIndex = CurrentTargetIndex.UPSTAIRS;

    public DragScrollDetailsLayout(Context context) {
        this(context, null);
    }

    public DragScrollDetailsLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragScrollDetailsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DragScrollDetailsLayout, defStyleAttr, 0);
        mPercent = a.getFloat(R.styleable.DragScrollDetailsLayout_percent, DEFAULT_PERCENT);
        mDuration = a.getInt(R.styleable.DragScrollDetailsLayout_duration, DEFAULT_DURATION);
        mDefaultPanel = a.getInt(R.styleable.DragScrollDetailsLayout_default_panel, 0);
        a.recycle();
        mScroller = new Scroller(getContext(),new DecelerateInterpolator());
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        mMaxFlingVelocity = ViewConfiguration.get(getContext()).getScaledMaximumFlingVelocity();
        mMiniFlingVelocity = ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();
        setOrientation(VERTICAL);
    }

    public void setOnSlideDetailsListener(OnSlideFinishListener listener) {
        this.mOnSlideDetailsListener = listener;
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        final int childCount = getChildCount();
        if (1 >= childCount) {
            throw new RuntimeException("SlideDetailsLayout only accept childs more than 1!!");
        }
        mUpstairsView = getChildAt(0);
        mDownstairsView = getChildAt(1);
    }

    /**
     * requestDisallowInterceptTouchEvent guarantee DragScrollDetailsLayout intercept event as wish
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        requestDisallowInterceptTouchEvent(false);
        return super.dispatchTouchEvent(ev);
    }

    /**
     * intercept rules:
     * 1. The vertical displacement is larger than the horizontal displacement;
     * 2. Panel stauts is UPSTAIRS:  slide up
     * 3. Panel status is DOWNSTAIRS:slide down
     * 4. child can requestDisallowInterceptTouchEvent();
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mDownMotionX = ev.getX();
                mDownMotionY = ev.getY();
                if (mVelocityTracker == null) {
                    mVelocityTracker = VelocityTracker.obtain();
                }
                mVelocityTracker.clear();
                mChildHasScrolled=false;
                break;
            case MotionEvent.ACTION_MOVE:
                adjustValidDownPoint(ev);
                return checkCanInterceptTouchEvent(ev);
            default:
                break;
        }
        return false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                flingToFinishScroll();
                recycleVelocityTracker();
                break;
            case MotionEvent.ACTION_MOVE:
                scroll(ev);
                break;
            default:
                break;
        }
        return true;
    }


    private boolean checkCanInterceptTouchEvent(MotionEvent ev) {
        final float xDiff = ev.getX() - mDownMotionX;
        final float yDiff = ev.getY() - mDownMotionY;
        if (!canChildScrollVertically((int) yDiff,ev)) {
            mInitialInterceptY = (int) ev.getY();
            if (Math.abs(yDiff) > mTouchSlop && Math.abs(yDiff) >= Math.abs(xDiff)
                    && !(mCurrentViewIndex == CurrentTargetIndex.UPSTAIRS && yDiff > 0
                    || mCurrentViewIndex == CurrentTargetIndex.DOWNSTAIRS && yDiff < 0)) {
                return true;
            }
        }
        return false;
    }



    private void adjustValidDownPoint(MotionEvent event) {
        if (mCurrentViewIndex == CurrentTargetIndex.UPSTAIRS && event.getY() > mDownMotionY
                || mCurrentViewIndex == CurrentTargetIndex.DOWNSTAIRS && event.getY() < mDownMotionY) {
            mDownMotionX = event.getX();
            mDownMotionY = event.getY();
        }
    }

    private void scroll(MotionEvent event) {
        if (mCurrentViewIndex == CurrentTargetIndex.UPSTAIRS) {
            if (getScrollY() <= 0 && event.getY() > mInitialInterceptY) {
                mInitialInterceptY = (int) event.getY();
            }
            scrollTo(0, (int) (mInitialInterceptY - event.getY()));
        } else {
            if (getScrollY() >= mUpstairsView.getMeasuredHeight() && event.getY() < mInitialInterceptY) {
                mInitialInterceptY = (int) event.getY();
            }
            scrollTo(0, (int) (mInitialInterceptY - event.getY() + mUpstairsView.getMeasuredHeight()));
        }
        mVelocityTracker.addMovement(event);
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.clear();
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    /**
     * if speed is enough even though offset is not enough go
     */

    private void flingToFinishScroll() {

        final int pHeight = mUpstairsView.getMeasuredHeight();
        final int threshold = (int) (pHeight * mPercent);
        float scrollY = getScrollY();
        if (CurrentTargetIndex.UPSTAIRS == mCurrentViewIndex) {
            if (scrollY <= 0) {
                scrollY = 0;
            } else if (scrollY <= threshold) {
                if (needFlingToToggleView()) {
                    scrollY = pHeight - getScrollY();
                    mCurrentViewIndex = CurrentTargetIndex.DOWNSTAIRS;
                } else
                    scrollY = -getScrollY();
            } else {
                scrollY = pHeight - getScrollY();
                mCurrentViewIndex = CurrentTargetIndex.DOWNSTAIRS;
            }
        } else if (CurrentTargetIndex.DOWNSTAIRS == mCurrentViewIndex) {
            if (pHeight - scrollY <= threshold) {
                if (needFlingToToggleView()) {
                    scrollY = -getScrollY();
                    mCurrentViewIndex = CurrentTargetIndex.UPSTAIRS;
                } else
                    scrollY = pHeight - scrollY;
            } else if (scrollY < pHeight) {
                scrollY = -getScrollY();
                mCurrentViewIndex = CurrentTargetIndex.UPSTAIRS;
            }
        }
        mScroller.startScroll(0, getScrollY(), 0, (int) scrollY, mDuration);
        if (mOnSlideDetailsListener != null) {
            mOnSlideDetailsListener.onStatueChanged(mCurrentViewIndex);
        }
        postInvalidate();
    }


    private boolean needFlingToToggleView() {
        mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
        if (mCurrentViewIndex == CurrentTargetIndex.UPSTAIRS) {
            if (-mVelocityTracker.getYVelocity() > mMiniFlingVelocity) {
                return true;
            }
        } else {
            if (mVelocityTracker.getYVelocity() > mMiniFlingVelocity) {
                return true;
            }
        }
        return false;
    }

    private View getCurrentTargetView() {
        return mCurrentViewIndex == CurrentTargetIndex.UPSTAIRS
                ? mUpstairsView : mDownstairsView;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            postInvalidate();
        }
    }

    /***
     * 复用已经实现的View,省却了测量布局之类的麻烦
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
    }

    protected boolean canChildScrollVertically(int offSet,MotionEvent ev) {
        mCurrentTargetView = getCurrentTargetView();
        return canScrollVertically(mCurrentTargetView, -offSet, ev);
    }

    /***
     * judge is event  is in current view
     * 判断MotionEvent是否处于View上面
     */
    protected boolean isTransformedTouchPointInView(MotionEvent ev, View view) {
        float x = ev.getRawX();
        float y = ev.getRawY();
        int[] rect = new int[2];
        view.getLocationInWindow(rect);
        float localX = x - rect[0];
        float localY = y - rect[1];
        return localX >= 0 && localX < (view.getRight() - view.getLeft())
                && localY >= 0 && localY < (view.getBottom() - view.getTop());
    }

    /***
     * first    can view self  ScrollVertically
     * seconde  if View is ViewPager only judge current page
     * third    if view is viewgroup check it`s children
     */
    private boolean canScrollVertically(View view, int offSet, MotionEvent ev) {

        if (!mChildHasScrolled && !isTransformedTouchPointInView(ev, view)) {
            return false;
        }
        if (ViewCompat.canScrollVertically(view, offSet)) {
            mChildHasScrolled = true;
            return true;
        }
        if (view instanceof ViewPager) {
            return canViewPagerScrollVertically((ViewPager) view, offSet, ev);
        }
        if (view instanceof ViewGroup) {
            ViewGroup vGroup = (ViewGroup) view;
            for (int i = 0; i < vGroup.getChildCount(); i++) {
                if (canScrollVertically(vGroup.getChildAt(i), offSet, ev)) {
                    mChildHasScrolled = true;
                    return true;
                }
            }
        }
        return false;
    }

    private boolean canViewPagerScrollVertically(ViewPager viewPager, int offset,MotionEvent ev) {
        View showView = ((SlideFragmentPagerAdapter) viewPager.getAdapter()).getPrimaryItem();
        return showView != null && canScrollVertically(showView, offset, ev);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
    }


}


<declare-styleable name="DragScrollDetailsLayout">
        <!-- float value for indicate the moment of switch panel-->
        <attr name="percent" format="float"/>
        <!-- how long the animation keep-->
        <attr name="duration" format="integer"/>
        <!-- default panel to show after init-->
        <attr name="default_panel" format="enum">
            <enum name="front" value="0"/>
            <enum name="behind" value="1"/>
        </attr>
    </declare-styleable>




http://www.open-open.com/lib/view/open1482476959653.html

自定义商品详情页
http://blog.csdn.net/qq_22271479/article/details/68490868
  • 大小: 6 MB
分享到:
评论

相关推荐

    淘宝商品详情页面黏滞效果

    在Android开发中,实现“淘宝商品详情页面黏滞效果”是一项常见的需求,它是指当用户在浏览商品详情页面时,下拉滚动时某些元素(如商品标题、价格或导航栏)会固定在屏幕顶部,直到其他内容填充到它们的位置。...

    Android 仿淘宝、京东商品详情页向上拖动查看图文详情控件DEMO详解

    一、淘宝商品详情页效果 我们的效果 二、实现思路  使用两个scrollView,两个scrollView 竖直排列,通过自定义viewGroup来控制两个scrollView的竖直排列,以及滑动事件的处理。如下图 三、具体实现 1、继承...

    仿淘宝继续拖动显示详情页

    在IT行业中,"仿淘宝继续拖动显示详情页"是一种常见的网页或APP交互设计,通常被称为"无限滚动"或"瀑布流"加载。这种设计灵感来源于淘宝等电商平台,旨在提供流畅且连续的浏览体验,让用户在不离开当前页面的情况下...

    仿淘宝商品详情页向上拖动可以加载下一页

    在移动应用开发中,"仿淘宝商品详情页向上拖动可以加载下一页"是一种常见的页面滚动效果,通常称为“上拉加载更多”或“无限滚动”。这种设计模式被广泛应用于电商、社交媒体等需要大量数据展示的场景,因为它能提供...

    类似淘宝的商品详情页,继续拖动查看详情

    在Android开发中,创建一个类似淘宝商品详情页的滚动效果是一项常见的需求。这种效果通常被称为“无限滚动”或“滚动视图”,用户可以不断地上下滑动页面以查看更多的内容。这个"VerticalSlideView-master"项目可能...

    jquery插件库-jquery仿京东商品详情页图片放大效果.zip

    京东商品详情页的图片放大效果,通常涉及到图片预览、缩放和拖动等功能。在网页上,用户点击商品图片后,会出现一个放大镜效果,用户可以在这个放大镜中看到图片的细节部分。这种效果可以提高用户的购物体验,使他们...

    仿淘宝,京东商品详细图片的viewpager,点击图片放大,左右滑动

    本项目是针对"仿淘宝、京东商品详细图片的viewpager"进行的设计,用户点击图片可以放大,并且支持左右滑动查看更多的图片,提供了类似电商平台的商品图片浏览体验。 首先,我们来详细了解一下ViewPager的核心功能和...

    仿淘宝详情页继续拖动,查看图片详情

    "仿淘宝详情页继续拖动,查看图片详情"这一课题,主要是针对商品详情页中的图片浏览功能进行优化,实现用户可以滚动页面时,图片依然能持续显示,提供更流畅的浏览体验。 首先,我们需要理解淘宝详情页的核心功能。...

    仿淘宝的拖动查看商品图文详情

    "仿淘宝的拖动查看商品图文详情"是一个常见的功能设计,它旨在提供流畅且丰富的用户体验,让用户能够通过简单的手势操作深入了解商品信息。这个功能的实现涉及到了多个技术点,包括UI设计、触摸事件处理、图片加载...

    IOS 仿淘宝详情页面上下分页效果

    在iOS开发中,为了提供类似淘宝商品详情页的上下分页浏览效果,开发者通常会采用UIScrollView这一核心组件。本教程将深入探讨如何利用两个UIScrollView及其代理方法来实现这个功能,结合提供的"LSPageScrollView...

    京东商城商品详细页图片展示特效(滚动、切换、缩放查看)

    1. **图片滚动**:在商品详情页,用户通常会看到多张展示商品不同角度或特性的图片。图片滚动功能允许用户通过鼠标滚轮或触屏滑动来浏览这些图片,无需手动点击每一张。这提供了流畅的浏览体验,使用户能够快速浏览...

    安卓支付宝天猫淘宝相关相关-类似淘宝的商品详情页继续拖动查看详情.rar

    这个压缩包文件"类似淘宝的商品详情页,继续拖动查看详情.rar"显然包含了一个模仿淘宝商品详情页设计的示例项目,目的是帮助开发者理解和实现类似的功能。在Android应用开发中,创建一个动态、交互性强的商品详情页...

    vue左右拖动上下滚动组件

    这是一个vue,pc端上的局部上下滚动,左右拖动单元格元素组件

    Android仿淘宝商品拖动查看详情及标题栏渐变功能

    【Android仿淘宝商品拖动查看详情及标题栏渐变功能】 在Android开发中,为了提供类似淘宝商品详情页的用户体验,我们可以实现一个拖动查看详细信息的功能,并且在拖动过程中让标题栏渐变隐藏。本篇文章将探讨如何...

    微信小程序商品详情页交互源码(选择商品类型切换、预览商品图片)

    在微信小程序中,商品详情页是用户了解商品信息并进行购买决策的关键页面。这个源码主要涉及了两个关键功能:选择商品类型切换和预览商品图片。以下将详细阐述这两个功能实现的技术要点。 1. 商品类型切换: - **...

    文字上下滚动易语言源码 外形框+标签简单实现

    在易语言编程环境中,制作文字上下滚动的效果可以为软件界面增添动态感,提高用户体验。本文将详细介绍如何使用易语言中的外形框(Window)和标签(Label)组件来实现这一功能。 首先,我们要明白易语言是一种面向...

    FLASH动态滚动条 缓动滚动条 上下滚动

    在本主题中,“FLASH动态滚动条 缓动滚动条 上下滚动”主要关注的是如何在Flash环境中创建具有平滑缓动效果的滚动条,并实现上下滚动的功能。 一、Flash滚动条基础 滚动条是用户界面中常见的组件,用于显示超出视窗...

Global site tag (gtag.js) - Google Analytics