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); 

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




作者:coexist
来源:51CTO
目录
相关文章
|
1月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
103 0
|
1月前
|
数据库 Android开发 开发者
构建高性能微服务架构:从理论到实践构建高效Android应用:探究Kotlin协程的优势
【2月更文挑战第16天】 在当今快速迭代和竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性和独立部署能力而受到企业的青睐。本文将深入探讨如何构建一个高性能的微服务系统,涵盖从理论基础到具体实现的各个方面。我们将重点讨论服务拆分策略、通信机制、数据一致性以及性能优化等关键主题,为读者提供一个清晰、实用的指南,以便在复杂多变的业务环境中构建和维护健壮的微服务体系结构。 【2月更文挑战第16天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着技术的不断进步,Kotlin作为一种现代编程语言,在Android开发中被广泛采用,尤其是其协程特性为异步编程带来了革命性的改进。本文旨在深入
241 5
|
2月前
|
人工智能 监控 安全
java基于微服务架构的智慧工地监管平台源码带APP
劳务管理: 工种管理、分包商管理、信息采集、班组管理、花名册、零工采集、 现场统计、考勤管理、考勤明细、工资管理、零工签证
287 4
|
3月前
|
安全 Linux Android开发
Android 安全功能
Android 安全功能
37 0
|
3月前
|
供应链 搜索推荐 数据挖掘
拼多多根据ID取商品详情原数据 API (pinduoduo.item_get_app_pro)在电商中的应用
拼多多是一个非常受欢迎的电商平台,它提供了许多API接口来帮助开发者集成和扩展其功能。其中,pinduoduo.item_get_app_pro API接口是用于根据商品ID获取商品详情的。这个API接口在电商应用中具有广泛的应用场景,以下是几个例子: 个性化推荐:电商平台可以根据用户的浏览和购买历史,利用pinduoduo.item_get_app_pro API接口获取商品的详细信息,然后向用户推荐相似或相关的商品。这有助于提高用户的购物体验,增加用户的购买意愿。 库存管理:商家可以利用这个API接口实时查询商品库存情况,以便及时调整销售策略,避免库存积压或缺货。 商品详情页面优化:在商
|
3天前
|
传感器 Java Android开发
Android HAL深入探索(1): 架构概述
Android HAL深入探索(1): 架构概述
21 1
|
4天前
|
Java Android开发
Android Mediatek 应用层重置USB设备功能
Android Mediatek 应用层重置USB设备功能
11 0
|
25天前
|
移动开发 前端开发 数据管理
构建高效Android应用:采用MVVM架构与LiveData的全面指南
在移动开发领域,构建一个既快速又可靠的应用对于开发者来说至关重要。随着Android Jetpack组件的推出,MVVM(Model-View-ViewModel)架构和LiveData已成为实现响应式、可测试且易于维护应用的首选解决方案。本文将深入探讨如何在Android应用中实施MVVM模式,以及如何利用LiveData来优化UI组件的数据更新流程,确保用户界面与业务逻辑之间的高度解耦和流畅交互。
18 4
|
1月前
uni-app 4.12开发弹出层组件(一)基础架构
uni-app 4.12开发弹出层组件(一)基础架构
19 0
|
2月前
|
监控 安全 物联网
B/S架构智慧工地源码,两个终端:PC后台管理端、APP移动端
智慧工地系统充分利用计算机技术、互联网、物联网、云计算、大数据等新一代信息技术,以PC端,移动端,设备端三位一体的管控方式为企业现场工程管理提供了先进的技术手段。让劳务、设备、物料、安全、环境、能源、资料、计划、质量、视频监控等十大管理环节变得智慧可控。为建设集团、施工企业、政府监管部门等提供一站式工地现场管理信息化解决方案,是一种崭新的工程现场一体化管理模式。
31 0