Android imitates the layout architecture of the product details page of JD.com and Tmall app, as well as its functional implementation

Android imitates the layout architecture of the product details page of JD.com and Tmall app, as well as its functional implementation

Preface

In e-commerce apps, the focus is on product display on the detail page. Users should not only see pictures, but also various descriptions and related specifications.

If you need to make an e-commerce app, you can take a look at it. First, let’s take a look at the effect.

  • Third-party frameworks used in this project:
    • Fresco used to load network images
    • ConvenientBanner
    • Navigation bar switching PagerSlidingTabStrip

Let’s take a look at the effect first

Since there is too much code, I will not explain them one by one, but only introduce a few core custom controls)

If you don't want to read it, you can download the apk or download the source code on github.

  • github address
  • apk download
  • The outermost layout file
  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. <! -- Top title -->  
  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. <! -- Controls for switching between products, details, and reviews -->  
  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 is a child View of SlideDetailsLayout (SlideDetailsLayout has too much code, so it is placed at ***)
    • Webview that displays product descriptions
    • Prevent sliding directly to the first View when sliding up
    • When sliding to the top of WebView, let the parent control regain touch events
  1. /**
  2. * Webview at the bottom of the product details page
  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. //Slide to the top to let the parent control regain touch events
  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.  
  59. }

  • ItemListView is also a child View of SlideDetailsLayout
    • It has roughly the same functionality as ItemWebView

  1. /**
  2. * ListView at the bottom of the product details page
  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. //Slide to the top to let the parent control regain touch events
  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 view , int scrollState) {
  58. currentPosition = getFirstVisiblePosition();
  59. }
  60.  
  61. @Override
  62. public void onScroll(AbsListView view , int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  63.  
  64. }
  65. }

  • NoScrollViewPager is the outermost parent layout
    • When sliding to the image and text details module, the sliding event of ViewPager can be disabled
  1. /**
  2. * Provide a custom ViewPager that disables sliding function
  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.  
  51. }

The outermost layout of the product module is a custom ViewGroup named SlideDetailsLayout

SlideDetailsLayout has two Views, mFrontView (the first View) and mBehindView (the second View)

There are two states. If the state is set to close, the first product data View will be displayed. If the state is open, the second image and text details View will be displayed.

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

The structure of this product details page is also used by me in the online project

<<:  Android magnifying glass effect implementation

>>:  The express delivery arrives but I don’t know how to sign for it, what should I do?

Recommend

Why can’t Chinese video websites retain users and make money?

The profit model of video websites has been a dif...

How to deeply interpret those operational professional terms?

"After launching this activity, the daily UV...

Little dirty! How to make APP operation and promotion "hard" and "soft"

APP operation and promotion , to put it simply an...

Facebook corporate account promotion and operation tutorial!

The Facebook corporate accounts referred to by fo...

Traveling to Jupiter to find water

Opening words How do astronauts live in space? Wh...

Xiaohongshu marketing method!

The "Insight Report on the Rise of Domestic ...

The main tea tasting studio in the center of Chengdu is worth collecting

Reservation arrangements for Chengdu Tea Tasting ...

Forgotten interests, where is the operational value of mini programs?

In the open class on December 28, Zhang Xiaolong ...

2021 Clothing Short Video and Live Streaming Marketing Report

As one of the earliest categories to enter Douyin...

Baidu bidding promotion keyword ranking query method, a must-learn for novices!

For bidders, after setting the bid, the most impo...

Crystal, agate, glass, chalcedony...why are there such big differences?

We are all one family, all SiO2, why are we so di...