仿淘宝商品详情页面Android
【致谢】: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、详细代码如下:
@Overridepublic 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);}@Overridepublic 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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="@dimen/px_300"android:text="我是第一个页面,向上滑动一下试试1"/><TextViewandroid:layout_width="match_parent"android:layout_height="@dimen/px_300"android:text="我是第一个页面,向上滑动一下试试2"/><TextViewandroid:layout_width="match_parent"android:layout_height="@dimen/px_300"android:text="我是第一个页面,向上滑动一下试试3"/><TextViewandroid:layout_width="match_parent"android:layout_height="@dimen/px_300"android:text="我是第一个页面,向上滑动一下试试4"/><TextViewandroid: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;@Overridepublic 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() {@Overridepublic void onScrollToBottom() {}@Overridepublic void onScrollToTop() {}@Overridepublic void onScroll(int scrollY) {//判断时候滑动到了顶部if (scrollY == 0) {PublicStaticClass.IsTop = true;} else {PublicStaticClass.IsTop = false;}}@Overridepublic void notBottom() {}});}
}
6、最后来上效果图:
7、又发现问题:
在第二页上,左右滑动时,在水平方向上如果出现一点变化,就会滑动到第一页上。这里在onInterceptTouchEvent()方法中做了修改,判断滑动的距离。如果左右滑动距离大于上下滑动距离,我们就认为用户在左右滑动,这时候我们要让子控件获取到事件,去切换fragment。相反,如果上下滑动距离大于左右滑动距离,我们就认为用户在上下滑动,这时候让父控件拦截事件。具体代码如下:
@Overridepublic 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
仿淘宝商品详情页面Android相关推荐
- ios仿淘宝商品详情页面粘贴商品规格弹出模板
- Android开发之仿淘宝商品详情页
看到有人在问如何实现淘宝商品详情页效果,手痒了就撸了一个,献上效果图 大致梳理一下思路,这里不提供源码 状态栏透明使用开源库StatusBarCompat,为了兼容手机4.4 dependencies ...
- Android 仿淘宝商品详情页下拉足迹Demo
DropDownMultiPager 仿淘宝等商品详情页下拉足迹效果SimpleDemo 可colne之后看MainActivity的调用,方便二次开发 依赖 compile 'com.nineold ...
- 仿淘宝商品详情-点击显示大图,可滑动
现在在做一个商城类的项目: 大家都用过淘宝,需求就是要求仿淘宝的效果做一个, 直接上图 用到了一个项目PhotoView 大家运行一下看最后一个项目,把单一的图片显示改成VIewpager就好.
- android app实现轮播的图片视频播放video,仿淘宝商品详情的视频播放(android)
这段时间在学习开发android app 记录一下实现仿淘宝图片视频切换的功能,直接拿来用即可,大家有什么问题可以共同交流 先看一下目录结构吧 思路: ViewPager 分别实现三个类型的滑动–fr ...
- 移动端实现swiper轮播的图片视频播放video,仿淘宝商品详情的视频播放(兼容ios和android)
前段时间项目里需要,实现仿淘宝图片视频切换的功能,在网上找了很久,根据网上所收集的信息,最后整合实现项目需求,这里兼容了ios和android,直接拿来用即可,这里是用来记录学习使用的,大家有什么问题 ...
- Android之仿淘宝商品详情浏览效果
一:先来几张效果图,没有弄gif动画,也就是商品详情滑动到底部继续上滑查看图文详情. 二:实现步骤: 1.自定义一个父容器ScrollViewContainer装载两个ScrollView. pack ...
- 仿淘宝商品详情页图片滑动并且数字也跟着变化
今天遇到需求需要做个淘宝那样的商品详情页如图(这里只差放图片了)支持移动端,当然用的是swiper.js支持左右滑动 上代码 html代码 <div class="swiper-con ...
- 仿淘宝商品详情页TabLayout+ListView
第一次写博客,我是一名Android的小码农写代码也有三四年了.有点好玩的跟大家分享一下 项目对商品详情页改版有新需求.顶部是一个渐变的Title包括"宝贝","详情&q ...
最新文章
- SQL Date 函数
- 史上最全的ubuntu16.04安装nvidia驱动+cuda9.0+cuDnn7.0
- 数字、字符串、列表类型及常用内置方法
- Follow My Logic 1048 PKU
- npkill(安全快速的清理电脑上的 node_modules)
- python解析原理_Python 中 -m 的典型用法、原理解析与发展演变
- Win10提示没有权限使用网络资源问题解决
- 树莓派串口通信编码_树莓派和STM32通过USB和串口通信记录
- 智能优化算法——遗传算法(C语言实现)
- Arquillian测试框架快速上手教程(四)- 使用Arquillian + Drone + Selenium + Graphene 进行Web自动化测试
- C语言函数定义和函数调用
- PhpStorm 2016.2 Mac破解版
- 人工智能对图书馆未来的影响,主要包含哪三个方面?
- 电感和磁珠有哪些区别
- python 定时运行 定时关闭_Python脚本用于定时关闭网易云音乐PC客户端
- 哈尔滨工业大学深圳计算机学院院长,哈工大计算机学院院长徐晓飞来访我院
- 艺赛旗RPA8.0-用户体验提升抢先看(一)
- 机器视觉中的像素、分辨率、灰度值等概念
- Vue项目使用splitpanes插件(垂直水平分割窗口)
- 谷歌浏览器(Google Chrome) v83.0.4103.97 正式版介绍及下载
热门文章
- Excel快速打印底端标题,教你一招,肯定行!
- 诸暨机器人餐厅价格_一起吃垮诸暨这家牛排自助餐厅!50+款自助,人均低至45元!...
- 【文献研究】轴辐式航线网络设计—Liner hub-and-spoke shipping network design
- 如何使用谷歌浏览器开发者工具中的Performance分析网页性能
- 阿里云蔡英华:云智一体,让产业全面迈向智能
- 常见的中间件及其特点
- 全自动过滤器:应用生物膜法利用全自动过滤器降解有机物
- 贪心算法实现活动安排问题
- 基于OpenCV实现对图片及视频中感兴趣区域颜色识别
- Romberg求积分算法