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

iOS 14 tips: How to get bike directions in Apple Maps

Google Maps has been offering detailed bike route...

It took only 9 hours to return to Earth. How come Shenzhou-13 is so fast?

Mixed Knowledge Specially designed to cure confus...

Beginner’s tips on how to use TikTok for educational institutions!

The topic I want to share with you today is: How ...

Title creation method: How to choose a title for a recommendation channel?

Regarding the skills of choosing titles , I think...

Startups "falsify data" and valuation impulse is hard to suppress

A reporter from the "Daily Economic News&quo...