美文网首页AndroidAndroid知识程序员
仿淘宝商品详情页面Android

仿淘宝商品详情页面Android

作者: 我是李小米 | 来源:发表于2017-03-12 15:04 被阅读1869次

    【致谢】:qifengdeqingchen
    【博客地址】:http://blog.csdn.net/qifengdeqingchen/article/details/51659735

    1、需求:

    要实现一个类似淘宝、京东的商品详情页面。首先是在看一些前辈的思路,查看之后,发现博主qifengdeqingchen的文章不错,然后去下载下来查看demo。

    2、查阅资料

    来看看前辈的思路图。使用两个scrollView,两个scrollView 竖直排列,通过自定义viewGroup来控制两个scrollView的竖直排列,以及滑动事件的处理。

    实现思路

    3、发现问题:

    我在scrollView2中添加一个TabLayout+ViewPager,然后在添加两个Fragment,在fragment中写一个scrollview.这时候发现在Fragment中只可以向下滑动,当想要去向上滑动的时候,就滑动到了scrollView1中。

    4、解决问题:

    首先来看一下前辈的代码,如下:

    public class PullUpToLoadMore extends ViewGroup {  
        public static String TAG = PullUpToLoadMore.class.getName();  
      
        MyScrollView topScrollView, bottomScrollView;  
        VelocityTracker velocityTracker = VelocityTracker.obtain();  
        Scroller scroller = new Scroller(getContext());  
      
        int currPosition = 0;  
        int position1Y;  
        int lastY;  
        public int scaledTouchSlop;//最小滑动距离  
        int speed = 200;  
        boolean isIntercept;  
      
        public boolean bottomScrollVIewIsInTop = false;  
        public boolean topScrollViewIsBottom = false;  
      
        public PullUpToLoadMore(Context context) {  
            super(context);  
            init();  
        }  
      
        public PullUpToLoadMore(Context context, AttributeSet attrs) {  
            super(context, attrs);  
            init();  
        }  
      
        public PullUpToLoadMore(Context context, AttributeSet attrs, int defStyleAttr) {  
            super(context, attrs, defStyleAttr);  
            init();  
        }  
      
        private void init() {  
      
            post(new Runnable() {  
                @Override  
                public void run() {  
                    topScrollView = (MyScrollView) getChildAt(0);  
                    bottomScrollView = (MyScrollView) getChildAt(1);  
                    topScrollView.setScrollListener(new MyScrollView.ScrollListener() {  
                        @Override  
                        public void onScrollToBottom() {  
                            topScrollViewIsBottom = true;  
                        }  
      
                        @Override  
                        public void onScrollToTop() {  
      
                        }  
      
                        @Override  
                        public void onScroll(int scrollY) {  
      
                        }  
      
                        @Override  
                        public void notBottom() {  
                            topScrollViewIsBottom = false;  
                        }  
      
                    });  
      
                    bottomScrollView.setScrollListener(new MyScrollView.ScrollListener() {  
                        @Override  
                        public void onScrollToBottom() {  
      
                        }  
      
                        @Override  
                        public void onScrollToTop() {  
      
                        }  
      
                        @Override  
                        public void onScroll(int scrollY) {  
                            if (scrollY == 0) {  
                                bottomScrollVIewIsInTop = true;  
                            } else {  
                                bottomScrollVIewIsInTop = false;  
                            }  
                        }  
      
                        @Override  
                        public void notBottom() {  
      
                        }  
                    });  
      
                    position1Y = topScrollView.getBottom();  
      
                    scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();  
                }  
            });  
        }  
      
        @Override  
        public boolean dispatchTouchEvent(MotionEvent ev) {  
            //防止子View禁止父view拦截事件  
            this.requestDisallowInterceptTouchEvent(false);  
            return super.dispatchTouchEvent(ev);  
        }  
      
        @Override  
        public boolean onInterceptTouchEvent(MotionEvent ev) {  
            int y = (int) ev.getY();  
      
            switch (ev.getAction()) {  
                case MotionEvent.ACTION_DOWN:  
                    lastY = y;  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    //判断是否已经滚动到了底部  
                    if (topScrollViewIsBottom) {  
                        int dy = lastY - y;  
      
                        //判断是否是向上滑动和是否在第一屏  
                        if (dy > 0 && currPosition == 0) {  
                            if (dy >= scaledTouchSlop) {  
                                isIntercept = true;//拦截事件  
                                lastY=y;  
                            }  
                        }  
                    }  
      
                    if (bottomScrollVIewIsInTop) {  
                        int dy = lastY - y;  
      
                        //判断是否是向下滑动和是否在第二屏  
                        if (dy < 0 && currPosition == 1) {  
                            if (Math.abs(dy) >= scaledTouchSlop) {  
                                isIntercept = true;  
                            }  
                        }  
                    }  
      
                    break;  
            }  
            return isIntercept;  
        }  
      
        @Override  
        public boolean onTouchEvent(MotionEvent event) {  
            int y = (int) event.getY();  
            velocityTracker.addMovement(event);  
      
            switch (event.getAction()) {  
                case MotionEvent.ACTION_MOVE:  
                    int dy = lastY - y;  
                    if (getScrollY() + dy < 0) {  
                        dy = getScrollY() + dy + Math.abs(getScrollY() + dy);  
                    }  
      
                    if (getScrollY() + dy + getHeight() > bottomScrollView.getBottom()) {  
                        dy = dy - (getScrollY() + dy - (bottomScrollView.getBottom() - getHeight()));  
                    }  
                    scrollBy(0, dy);  
                    break;  
                case MotionEvent.ACTION_UP:  
                    isIntercept = false;  
      
                    velocityTracker.computeCurrentVelocity(1000);  
                    float yVelocity = velocityTracker.getYVelocity();  
      
                    if (currPosition == 0) {  
                        if (yVelocity < 0 && yVelocity < -speed) {  
                            smoothScroll(position1Y);  
                            currPosition = 1;  
                        } else {  
                            smoothScroll(0);  
                        }  
                    } else {  
                        if (yVelocity > 0 && yVelocity > speed) {  
                            smoothScroll(0);  
                            currPosition = 0;  
                        } else {  
                            smoothScroll(position1Y);  
                        }  
                    }  
                    break;  
            }  
            lastY = y;  
            return true;  
        }  
      
        @Override  
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
            measureChildren(widthMeasureSpec, heightMeasureSpec);  
        }  
      
        @Override  
        protected void onLayout(boolean changed, int l, int t, int r, int b) {  
            int childCount = getChildCount();  
            int childTop = t;  
            for (int i = 0; i < childCount; i++) {  
                View child = getChildAt(i);  
                child.layout(l, childTop, r, childTop + child.getMeasuredHeight());  
                childTop += child.getMeasuredHeight();  
            }  
        }  
      
        //通过Scroller实现弹性滑动  
        private void smoothScroll(int tartY) {  
            int dy = tartY - getScrollY();  
            scroller.startScroll(getScrollX(), getScrollY(), 0, dy);  
            invalidate();  
        }  
      
        @Override  
        public void computeScroll() {  
            if (scroller.computeScrollOffset()) {  
                scrollTo(scroller.getCurrX(), scroller.getCurrY());  
                postInvalidate();  
            }  
        }  
    }  
    

    通过分析我们可以大概了解到,出现在个问题应该是在滑动事件的解决上。在自定义控件中的onInterceptTouchEvent方法中,可能拦截了滑动事件,才导致了问题的出现。于是我在onInterceptTouchEvent方法中做出了改动。当向下滑动时,判断是否在ScrollView2中,分别处理滑动事件。

    当页面没有在ScrollView2中,就要让自定义viewGroup中的Scrollview页面切换(这时候父控件拦截OnTouchEvent,不向子控件传递,让父控件滑动)。当页面在ScrollView2中,还要去判断viewpager里边的scrollview是否滑动到了顶部,如果在顶部,要去切换viewgroup中的页面(这时候父控件拦截OnTouchEvent,不向子控件传递,让父控件滑动),如果没有滑动到顶部,就要让viewpager中的scrollview滑动(这时候父控件不拦截OnTouchEvent,向子控件传递,让子控件滑动)。

    5、详细代码如下:

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int y = (int) ev.getY();
    
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    lastY = y;
                    break;
                case MotionEvent.ACTION_MOVE:
    
                    //判断是否已经滚动到了底部
                    if (topScrollViewIsBottom) {
                        int dy = lastY - y;
                        //判断是否是向上滑动和是否在第一屏
                        if (dy > 0 && currPosition == 0) {
                            if (dy >= scaledTouchSlop) {
                                isIntercept = true;//拦截事件
                                lastY=y;
                            }
                        }
                    }
    
                    if (bottomScrollVIewIsInTop) {
                        int dy = lastY - y;
                        //判断是否是向下滑动和是否在第二屏
                        if (dy < 0 && currPosition == 1) {
                            if (Math.abs(dy) >= scaledTouchSlop) {
                                if(PublicStaticClass.IsTop){//如果viewpager里边的scrollview在最顶部,,就让外边的scrollview获取焦点,否则,让最里边的scrollview获取焦点
                                    isIntercept = true;
                                }
                            }
                        }
    
                    }else{
                        int dy = lastY - y;
                        //判断是否是向上滑动和是否在第二屏   如果是在刚到第二屏的时候,向上滑动,也让父控件获取焦点
    //                    在onInterceptTouchEvent()方法中,如果返回true,父控件拦截事件,如果返回false,则向下传递
                        if (dy < 0 && currPosition == 1) {
                            if (Math.abs(dy) >= scaledTouchSlop) {
                                if(PublicStaticClass.IsTop){
                                //PublicStaticClass.IsTop  判断fragment中的scrollview时候滑动到了顶部。
                                //如果viewpager里边的scrollview在最顶部,,就让外边的scrollview获取焦点,否则,让最里边的scrollview获取焦点
                                    isIntercept = true;
                                }
                            }
                        }
                    }
                    break;
            }
            return isIntercept;
        }
    

    来看一下ViewPager中的实现:

    自定义控件:MyScrollView.java:

    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.ScrollView;
    
    /**
     * Created by baoyunlong on 16/6/8.
     */
    public class MyScrollView extends ScrollView {
        private static String TAG=MyScrollView.class.getName();
    
        public void setScrollListener(ScrollListener scrollListener) {
            this.mScrollListener = scrollListener;
        }
    
        private ScrollListener mScrollListener;
    
        public MyScrollView(Context context) {
            super(context);
        }
    
        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
    
            switch (ev.getAction()){
                case MotionEvent.ACTION_MOVE:
    
                    if(mScrollListener!=null){
                        int contentHeight=getChildAt(0).getHeight();
                        int scrollHeight=getHeight();
    
                        int scrollY=getScrollY();
                        mScrollListener.onScroll(scrollY);
    
                        if(scrollY+scrollHeight>=contentHeight||contentHeight<=scrollHeight){
                            mScrollListener.onScrollToBottom();
                        }else {
                            mScrollListener.notBottom();
                        }
    
                        if(scrollY==0){
                            mScrollListener.onScrollToTop();
                        }
    
                    }
    
                    break;
            }
            boolean result=super.onTouchEvent(ev);
            requestDisallowInterceptTouchEvent(false);
    
            return result;
        }
    
        public interface ScrollListener{
            void onScrollToBottom();
            void onScrollToTop();
            void onScroll(int scrollY);
            void notBottom();
        }
    }
    
    

    xml:

    <com.qfdqc.views.pulltoloadmoreview.utils.MyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
                                                           android:id="@+id/oneScrollview"
                                                           android:layout_width="match_parent"
                                                           android:layout_height="match_parent"
        >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
    
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="@dimen/px_300"
                android:text="我是第一个页面,向上滑动一下试试1"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="@dimen/px_300"
                android:text="我是第一个页面,向上滑动一下试试2"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="@dimen/px_300"
                android:text="我是第一个页面,向上滑动一下试试3"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="@dimen/px_300"
                android:text="我是第一个页面,向上滑动一下试试4"/>
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="@dimen/px_300"
                android:text="我是第一个页面,向上滑动一下试试5"/>
        </LinearLayout>
    </com.qfdqc.views.pulltoloadmoreview.utils.MyScrollView>
    

    Fragment.java:

    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.qfdqc.views.pulltoloadmoreview.R;
    import com.qfdqc.views.pulltoloadmoreview.utils.MyScrollView;
    import com.qfdqc.views.pulltoloadmoreview.utils.PublicStaticClass;
    
    public class OneFragment extends Fragment {
        View mView;
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            mView = inflater.inflate(R.layout.fragment_one, container, false);
            initView();
            return mView;
        }
    
        private void initView() {
            MyScrollView oneScrollView= (MyScrollView) mView.findViewById(R.id.oneScrollview);
            oneScrollView.setScrollListener(new MyScrollView.ScrollListener() {
                @Override
                public void onScrollToBottom() {
    
                }
    
                @Override
                public void onScrollToTop() {
    
                }
    
                @Override
                public void onScroll(int scrollY) {
                    //判断时候滑动到了顶部
                    if (scrollY == 0) {
                        PublicStaticClass.IsTop = true;
                    } else {
                        PublicStaticClass.IsTop = false;
                    }
                }
    
                @Override
                public void notBottom() {
    
                }
    
            });
        }
    }
    
    

    6、最后来上效果图:

    效果图

    7、又发现问题:

    在第二页上,左右滑动时,在水平方向上如果出现一点变化,就会滑动到第一页上。这里在onInterceptTouchEvent()方法中做了修改,判断滑动的距离。如果左右滑动距离大于上下滑动距离,我们就认为用户在左右滑动,这时候我们要让子控件获取到事件,去切换fragment。相反,如果上下滑动距离大于左右滑动距离,我们就认为用户在上下滑动,这时候让父控件拦截事件。具体代码如下:

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            int y = (int) ev.getY();
            int x = (int) ev.getX();
    
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    lastY = y;
                    lastX = x;
                    break;
                case MotionEvent.ACTION_MOVE:
    
                    //判断是否已经滚动到了底部
                    if (topScrollViewIsBottom) {
                        int dy = lastY - y;
                        //判断是否是向上滑动和是否在第一屏
                        if (dy > 0 && currPosition == 0) {
                            if (dy >= scaledTouchSlop) {
                                isIntercept = true;//拦截事件
                                lastY=y;
                                lastX=x;
                            }
                        }
                    }
    
                    if (bottomScrollVIewIsInTop) {
                        int dy = lastY - y;
                        //判断是否是向下滑动和是否在第二屏
                        if (dy < 0 && currPosition == 1) {
                            if (Math.abs(dy) >= scaledTouchSlop) {
                                if(PublicStaticClass.IsTop){//如果viewpager里边的scrollview在最顶部,,就让外边的scrollview获取焦点,否则,让最里边的scrollview获取焦点
                                    isIntercept = true;
                                }
                            }
                        }
    
                    }else{
                        int dy = lastY - y;//上下滑动的距离
                        int dx = lastX - x;//左右滑动的距离
    
                        //判断是否是向上滑动和是否在第二屏   如果是在刚到第二屏的时候,向上滑动,也让父控件获取焦点
    //                    在onInterceptTouchEvent()方法中,如果返回true,父控件拦截事件,如果返回false,则向下传递
                        if (dy < 0 && currPosition == 1) {
                            if (Math.abs(dy) >= scaledTouchSlop) {
                                if(PublicStaticClass.IsTop){//如果viewpager里边的scrollview在最顶部,,就让外边的scrollview获取焦点,否则,让最里边的scrollview获取焦点
    
                                    //这里加一个判断,如果左右滑动的距离小于上下滑动的距离,我们认为用户在上下滑动
                                     //如果左右滑动的距离大于上下滑动的距离,我们认为用户在左右滑动
                                    //上下滑动时,让父控件拦截事件
                                    //左右滑动时,让子控件拦截事件
                                    if(Math.abs(dy)>Math.abs(dx)){//上下滑动
                                        isIntercept = true;
                                    }else{//左右滑动
                                        isIntercept = false;
                                    }
                                }
                            }
                        }
                    }
                    break;
            }
            return isIntercept;
        }
    

    8、github已经更新,项目地址:

    https://github.com/LiQinglin007/PullToLoadMoreView-master

    相关文章

      网友评论

      • 60c8e0743465:你好,请问下方如果是recyclerview,会出现无法回到顶部的情况,不知道有遇到过吗
        我是李小米:@BingoRiver 你给recyclerview加一个滑动到顶部的监听就可以了。代码也更新了,你可以去看看。
      • d7b6c12dc57c:感谢大佬 ,我把layouttop的ImageView换成了一个Banner就报一大堆红,具体找不到是哪儿的问题,求大佬指点
        我是李小米:@联梦肖恩 我把顶部的图片改成了banner,代码更新了,你可以看一下。
      • uustrong:用了楼主的demo ,但是第一个ScrollView如果有动态改变子view的大小,会有显示不全的情况出现
        uustrong:@李清Lin 你可以试试看 在TextView上面加个Button来控制显示和隐藏Textview,TextView可以写长一点 ,我这样内容就可能显示不全 。还有 你要 先划到第二个ScrollView,这样来测 。

        我是李小米:@uustrong 能详细描述一下吗?我把顶部的imageview动态改变了大小,并没有发现问题。
      • 微微心凉L:感谢楼主
        微微心凉L:嗯嗯, 刚才运行了下。发现首页面是图片 我还随便点呢, 发现没反应 :joy:
        我是李小米:@微微心凉L 能用到就好!:smiley:
      • 我来打酱油的:你的下载地址不能用了
        我是李小米:感谢提醒!地址已更新!

      本文标题:仿淘宝商品详情页面Android

      本文链接:https://www.haomeiwen.com/subject/fagtnttx.html