Android仿京东、天猫app的商品详情页的布局架构, 以及功能实现

移动开发 Android
电商内app,重点在于详情页商品展示,用户不仅要看到图,可以看到各种描述,以及相关规格参数。

前言

电商内app,重点在于详情页商品展示,用户不仅要看到图,可以看到各种描述,以及相关规格参数。

有需要做电商类app的童鞋可以看看, 首先先看看效果实现

  • 本项目使用的第三方框架:
    • 加载网络图片使用的 Fresco
    • 头部的商品图轮播 ConvenientBanner
    • 导航栏切换 PagerSlidingTabStrip

先看看效果实现

 

 

 

 

由于代码量过多, 就不一一讲解只介绍几个核心的自定义控件)

不想看的童鞋可以下载apk或者在github上下载源码使用

  • github地址
  • apk下载
  • 最外层的布局文件
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     xmlns:app="http://schemas.android.com/apk/res-auto" 
  4.     xmlns:tools="http://schemas.android.com/tools" 
  5.     android:layout_width="match_parent" 
  6.     android:layout_height="match_parent" 
  7.     android:orientation="vertical"
  8.      <!-- 顶部标题 --> 
  9.     <LinearLayout 
  10.         android:id="@+id/ll_title_root" 
  11.         android:layout_width="match_parent" 
  12.         android:layout_height="wrap_content" 
  13.         android:background="#ec0f38" 
  14.         android:orientation="vertical"
  15.  
  16.         <LinearLayout 
  17.             android:layout_width="match_parent" 
  18.             android:layout_height="44dp" 
  19.             android:orientation="horizontal"
  20.  
  21.             <LinearLayout 
  22.                 android:id="@+id/ll_back" 
  23.                 android:layout_width="wrap_content" 
  24.                 android:layout_height="match_parent" 
  25.                 android:paddingLeft="15dp"
  26.  
  27.                 <ImageView 
  28.                     android:id="@+id/iv_back" 
  29.                     android:layout_width="22dp" 
  30.                     android:layout_height="22dp" 
  31.                     android:layout_gravity="center_vertical" 
  32.                     android:src="@mipmap/address_come_back" /> 
  33.             </LinearLayout> 
  34.  
  35.             <LinearLayout 
  36.                 android:layout_width="0dp" 
  37.                 android:layout_height="match_parent" 
  38.                 android:layout_weight="1" 
  39.                 android:gravity="center"
  40.  
  41.                 <!-- 商品、详情、评价切换的控件 --> 
  42.                 <com.gxz.PagerSlidingTabStrip 
  43.                     android:id="@+id/psts_tabs" 
  44.                     android:layout_width="wrap_content" 
  45.                     android:layout_height="32dp" 
  46.                     android:layout_gravity="center" 
  47.                     android:textColor="#ffffff" 
  48.                     android:textSize="15sp" 
  49.                     app:pstsDividerColor="@android:color/transparent" 
  50.                     app:pstsDividerPaddingTopBottom="0dp" 
  51.                     app:pstsIndicatorColor="#ffffff" 

 

  • ItemWebView是SlideDetailsLayout的子View (SlideDetailsLayout代码太多, 放到了***)
    • 功能为显示商品简介的webview
    • 防止往上滑动时会直接滑动到***个View
    • 实现滑动到WebView顶部时, 让父控件重新获得触摸事件
  1. /** 
  2.  * 商品详情页底部的webview 
  3.  */ 
  4. public class ItemWebView extends WebView { 
  5.     public float oldY; 
  6.     private int t; 
  7.     private float oldX; 
  8.  
  9.     public ItemWebView(Context context) { 
  10.         super(context); 
  11.     } 
  12.  
  13.     public ItemWebView(Context context, AttributeSet attrs) { 
  14.         super(context, attrs); 
  15.     } 
  16.  
  17.     public ItemWebView(Context context, AttributeSet attrs, int defStyleAttr) { 
  18.         super(context, attrs, defStyleAttr); 
  19.     } 
  20.  
  21.  
  22.     @Override 
  23.     public boolean onTouchEvent(MotionEvent ev) { 
  24.  
  25.         switch (ev.getAction()) { 
  26.             case MotionEvent.ACTION_MOVE: 
  27.                 float Y = ev.getY(); 
  28.                 float Ys = Y - oldY; 
  29.                 float X = ev.getX(); 
  30.  
  31.                 //滑动到顶部让父控件重新获得触摸事件 
  32.                 if (Ys > 0 && t == 0) { 
  33.                     getParent().getParent().requestDisallowInterceptTouchEvent(false); 
  34.                 } 
  35.                 break; 
  36.  
  37.             case MotionEvent.ACTION_DOWN: 
  38.                 getParent().getParent().requestDisallowInterceptTouchEvent(true); 
  39.                 oldY = ev.getY(); 
  40.                 oldX = ev.getX(); 
  41.                 break; 
  42.  
  43.             case MotionEvent.ACTION_UP: 
  44.                 getParent().getParent().requestDisallowInterceptTouchEvent(true); 
  45.                 break; 
  46.  
  47.             default
  48.                 break; 
  49.         } 
  50.         return super.onTouchEvent(ev); 
  51.     } 
  52.  
  53.     @Override 
  54.     protected void onScrollChanged(int l, int t, int oldl, int oldt) { 
  55.         this.t = t; 
  56.         super.onScrollChanged(l, t, oldl, oldt); 
  57.     } 
  58.  

 

  • ItemListView 也是SlideDetailsLayout的子View
    • 和ItemWebView功能大致一样

 

  1. /** 
  2.  * 商品详情页底部的ListView 
  3.  */ 
  4. public class ItemListView extends ListView implements AbsListView.OnScrollListener { 
  5.     private float oldX, oldY; 
  6.     private int currentPosition; 
  7.  
  8.     public ItemListView(Context context) { 
  9.         super(context); 
  10.         setOnScrollListener(this); 
  11.     } 
  12.  
  13.     public ItemListView(Context context, AttributeSet attrs) { 
  14.         super(context, attrs); 
  15.         setOnScrollListener(this); 
  16.     } 
  17.  
  18.     public ItemListView(Context context, AttributeSet attrs, int defStyleAttr) { 
  19.         super(context, attrs, defStyleAttr); 
  20.         setOnScrollListener(this); 
  21.     } 
  22.  
  23.  
  24.     @Override 
  25.     public boolean onTouchEvent(MotionEvent ev) { 
  26.         switch (ev.getAction()) { 
  27.             case MotionEvent.ACTION_MOVE: 
  28.                 float Y = ev.getY(); 
  29.                 float Ys = Y - oldY; 
  30.                 float X = ev.getX(); 
  31.                 int [] location = new int [2]; 
  32.                 getLocationInWindow(location); 
  33.  
  34.                 //滑动到顶部让父控件重新获得触摸事件 
  35.                 if (Ys > 0 && currentPosition == 0) { 
  36.                     getParent().getParent().requestDisallowInterceptTouchEvent(false); 
  37.                 } 
  38.                 break; 
  39.  
  40.             case MotionEvent.ACTION_DOWN: 
  41.                 getParent().getParent().requestDisallowInterceptTouchEvent(true); 
  42.                 oldY = ev.getY(); 
  43.                 oldX = ev.getX(); 
  44.                 break; 
  45.  
  46.             case MotionEvent.ACTION_UP: 
  47.                 getParent().getParent().requestDisallowInterceptTouchEvent(true); 
  48.                 break; 
  49.  
  50.             default
  51.                 break; 
  52.         } 
  53.         return super.onTouchEvent(ev); 
  54.     } 
  55.  
  56.     @Override 
  57.     public void onScrollStateChanged(AbsListView viewint scrollState) { 
  58.         currentPosition = getFirstVisiblePosition(); 
  59.     } 
  60.  
  61.     @Override 
  62.     public void onScroll(AbsListView viewint firstVisibleItem, int visibleItemCount, int totalItemCount) { 
  63.  
  64.     } 

 

  • NoScrollViewPager为最外层的父布局
    • 当滑动到图文详情模块时, 能禁止掉ViewPager的滑动事件
  1. /** 
  2.  * 提供禁止滑动功能的自定义ViewPager 
  3.  */ 
  4. public class NoScrollViewPager extends ViewPager { 
  5.     private boolean noScroll = false
  6.  
  7.     public NoScrollViewPager(Context context, AttributeSet attrs) { 
  8.         super(context, attrs); 
  9.     } 
  10.  
  11.  
  12.     public NoScrollViewPager(Context context) { 
  13.         super(context); 
  14.     } 
  15.  
  16.     public void setNoScroll(boolean noScroll) { 
  17.         this.noScroll = noScroll; 
  18.     } 
  19.  
  20.     @Override 
  21.     public void scrollTo(int x, int y) { 
  22.         super.scrollTo(x, y); 
  23.     } 
  24.  
  25.     @Override 
  26.     public boolean onTouchEvent(MotionEvent arg0) { 
  27.         if (noScroll) 
  28.             return false
  29.         else 
  30.             return super.onTouchEvent(arg0); 
  31.     } 
  32.  
  33.     @Override 
  34.     public boolean onInterceptTouchEvent(MotionEvent arg0) { 
  35.         if (noScroll) 
  36.             return false
  37.         else 
  38.             return super.onInterceptTouchEvent(arg0); 
  39.     } 
  40.  
  41.     @Override 
  42.     public void setCurrentItem(int item, boolean smoothScroll) { 
  43.         super.setCurrentItem(item, smoothScroll); 
  44.     } 
  45.  
  46.     @Override 
  47.     public void setCurrentItem(int item) { 
  48.         super.setCurrentItem(item); 
  49.     } 
  50.  

 

商品模块最外层的布局是一个自定义的ViewGroup名为SlideDetailsLayout

SlideDetailsLayout内容有两个View, mFrontView(***个View)和mBehindView(第二个View)

有两种状态, 状态设置为close就显示***个商品数据View, open状态就显示第二个图文详情View

  1. @SuppressWarnings("unused"
  2. public class SlideDetailsLayout extends ViewGroup { 
  3.  
  4.     /** 
  5.      * Callback for panel OPEN-CLOSE status changed. 
  6.      */ 
  7.     public interface OnSlideDetailsListener { 
  8.         /** 
  9.          * Called after status changed. 
  10.          * 
  11.          * @param status {@link Status} 
  12.          */ 
  13.         void onStatucChanged(Status status); 
  14.     } 
  15.  
  16.     public enum Status { 
  17.         /** Panel is closed */ 
  18.         CLOSE
  19.         /** Panel is opened */ 
  20.         OPEN
  21.  
  22.         public static Status valueOf(int stats) { 
  23.             if (0 == stats) { 
  24.                 return CLOSE
  25.             } else if (1 == stats) { 
  26.                 return OPEN
  27.             } else { 
  28.                 return CLOSE
  29.             } 
  30.         } 
  31.     } 
  32.  
  33.     private static final float DEFAULT_PERCENT = 0.2f; 
  34.     private static final int DEFAULT_DURATION = 300; 
  35.  
  36.     private View mFrontView; 
  37.     private View mBehindView; 
  38.  
  39.     private float mTouchSlop; 
  40.     private float mInitMotionY; 
  41.     private float mInitMotionX; 
  42.  
  43.  
  44.     private View mTarget; 
  45.     private float mSlideOffset; 
  46.     private Status mStatus = Status.CLOSE
  47.     private boolean isFirstShowBehindView = true
  48.     private float mPercent = DEFAULT_PERCENT; 
  49.     private long mDuration = DEFAULT_DURATION; 
  50.     private int mDefaultPanel = 0; 
  51.  
  52.     private OnSlideDetailsListener mOnSlideDetailsListener; 
  53.  
  54.     public SlideDetailsLayout(Context context) { 
  55.         this(context, null); 
  56.     } 
  57.  
  58.     public SlideDetailsLayout(Context context, AttributeSet attrs) { 
  59.         this(context, attrs, 0); 
  60.     } 
  61.  
  62.     public SlideDetailsLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
  63.         super(context, attrs, defStyleAttr); 
  64.  
  65.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideDetailsLayout, defStyleAttr, 0); 
  66.         mPercent = a.getFloat(R.styleable.SlideDetailsLayout_percent, DEFAULT_PERCENT); 
  67.         mDuration = a.getInt(R.styleable.SlideDetailsLayout_duration, DEFAULT_DURATION); 
  68.         mDefaultPanel = a.getInt(R.styleable.SlideDetailsLayout_default_panel, 0); 

 

这个商品详情页的架构也是本人在已上线的项目中使用 

责任编辑:庞桂玉 来源: 安卓巴士Android开发者门户
相关推荐

2017-01-15 18:51:57

京东手机商品详情页

2016-11-28 09:58:53

京东服务闭环实践

2016-01-04 15:16:01

京东详情页实践

2016-11-23 15:26:01

详情页单品页前端

2024-07-12 14:52:20

2023-07-31 22:02:17

客服订单详情

2024-06-03 14:37:22

锐捷天蝎路由器

2015-11-13 10:25:04

京东商品搜索架构

2022-07-21 08:00:00

京东数据存储汽车行业

2018-02-26 06:33:53

京东商品系统商品架构

2009-07-29 11:46:22

asp.net1.1

2015-04-16 14:17:53

2023-05-31 07:57:12

笔记本电脑信誉度

2017-04-27 21:00:33

Android滑动分析

2015-12-14 15:12:42

京东

2018-06-13 15:47:10

阿里文学

2017-06-30 17:48:59

互联网

2013-08-02 10:20:03

android时间轴

2015-12-09 11:07:16

商城源码android

2010-09-14 14:11:35

室外WLAN
点赞
收藏

51CTO技术栈公众号