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
- <?xml version= "1.0" encoding= "utf-8" ?>
- <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
- xmlns:app= "http://schemas.android.com/apk/res-auto"
- xmlns:tools= "http://schemas.android.com/tools"
- android:layout_width= "match_parent"
- android:layout_height= "match_parent"
- android:orientation= "vertical" >
- <!
- <LinearLayout
- android:id= "@+id/ll_title_root"
- android:layout_width= "match_parent"
- android:layout_height= "wrap_content"
- android:background= "#ec0f38"
- android:orientation= "vertical" >
-
- <LinearLayout
- android:layout_width= "match_parent"
- android:layout_height= "44dp"
- android:orientation= "horizontal" >
-
- <LinearLayout
- android:id= "@+id/ll_back"
- android:layout_width= "wrap_content"
- android:layout_height= "match_parent"
- android:paddingLeft= "15dp" >
-
- <ImageView
- android:id= "@+id/iv_back"
- android:layout_width= "22dp"
- android:layout_height= "22dp"
- android:layout_gravity= "center_vertical"
- android:src= "@mipmap/address_come_back" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width= "0dp"
- android:layout_height= "match_parent"
- android:layout_weight= "1"
- android:gravity= "center" >
-
- <!
- <com.gxz.PagerSlidingTabStrip
- android:id= "@+id/psts_tabs"
- android:layout_width= "wrap_content"
- android:layout_height= "32dp"
- android:layout_gravity= "center"
- android:textColor= "#ffffff"
- android:textSize= "15sp"
- app:pstsDividerColor= "@android:color/transparent"
- app:pstsDividerPaddingTopBottom= "0dp"
- 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
- /**
- * Webview at the bottom of the product details page
- */
- public class ItemWebView extends WebView {
- public float oldY;
- private int t;
- private float oldX;
-
- public ItemWebView(Context context) {
- super(context);
- }
-
- public ItemWebView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ItemWebView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
-
- switch (ev.getAction()) {
- case MotionEvent.ACTION_MOVE:
- float Y = ev.getY();
- float Ys = Y - oldY;
- float X = ev.getX();
-
- //Slide to the top to let the parent control regain touch events
- if (Ys > 0 && t == 0) {
- getParent().getParent().requestDisallowInterceptTouchEvent( false );
- }
- break;
-
- case MotionEvent.ACTION_DOWN:
- getParent().getParent().requestDisallowInterceptTouchEvent( true );
- oldY = ev.getY();
- oldX = ev.getX();
- break;
-
- case MotionEvent.ACTION_UP:
- getParent().getParent().requestDisallowInterceptTouchEvent( true );
- break;
-
- default :
- break;
- }
- return super.onTouchEvent(ev);
- }
-
- @Override
- protected void onScrollChanged( int l, int t, int oldl, int oldt) {
- this.t = t;
- super.onScrollChanged(l, t, oldl, oldt);
- }
-
- }
- ItemListView is also a child View of SlideDetailsLayout
- It has roughly the same functionality as ItemWebView
- /**
- * ListView at the bottom of the product details page
- */
- public class ItemListView extends ListView implements AbsListView.OnScrollListener {
- private float oldX, oldY;
- private int currentPosition;
-
- public ItemListView(Context context) {
- super(context);
- setOnScrollListener(this);
- }
-
- public ItemListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setOnScrollListener(this);
- }
-
- public ItemListView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOnScrollListener(this);
- }
-
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- switch (ev.getAction()) {
- case MotionEvent.ACTION_MOVE:
- float Y = ev.getY();
- float Ys = Y - oldY;
- float X = ev.getX();
- int [] location = new int [2];
- getLocationInWindow(location);
-
- //Slide to the top to let the parent control regain touch events
- if (Ys > 0 && currentPosition == 0) {
- getParent().getParent().requestDisallowInterceptTouchEvent( false );
- }
- break;
-
- case MotionEvent.ACTION_DOWN:
- getParent().getParent().requestDisallowInterceptTouchEvent( true );
- oldY = ev.getY();
- oldX = ev.getX();
- break;
-
- case MotionEvent.ACTION_UP:
- getParent().getParent().requestDisallowInterceptTouchEvent( true );
- break;
-
- default :
- break;
- }
- return super.onTouchEvent(ev);
- }
-
- @Override
- public void onScrollStateChanged(AbsListView view , int scrollState) {
- currentPosition = getFirstVisiblePosition();
- }
-
- @Override
- public void onScroll(AbsListView view , int firstVisibleItem, int visibleItemCount, int totalItemCount) {
-
- }
- }
- NoScrollViewPager is the outermost parent layout
- When sliding to the image and text details module, the sliding event of ViewPager can be disabled
- /**
- * Provide a custom ViewPager that disables sliding function
- */
- public class NoScrollViewPager extends ViewPager {
- private boolean noScroll = false ;
-
- public NoScrollViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
-
- public NoScrollViewPager(Context context) {
- super(context);
- }
-
- public void setNoScroll(boolean noScroll) {
- this.noScroll = noScroll;
- }
-
- @Override
- public void scrollTo( int x, int y) {
- super.scrollTo(x, y);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent arg0) {
- if (noScroll)
- return false ;
- else
- return super.onTouchEvent(arg0);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent arg0) {
- if (noScroll)
- return false ;
- else
- return super.onInterceptTouchEvent(arg0);
- }
-
- @Override
- public void setCurrentItem( int item, boolean smoothScroll) {
- super.setCurrentItem(item, smoothScroll);
- }
-
- @Override
- public void setCurrentItem( int item) {
- super.setCurrentItem(item);
- }
-
- }
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. - @SuppressWarnings( "unused" )
- public class SlideDetailsLayout extends ViewGroup {
-
- /**
- * Callback for panel OPEN - CLOSE status changed.
- */
- public interface OnSlideDetailsListener {
- /**
- * Called after status changed.
- *
- * @param status {@link Status}
- */
- void onStatucChanged(Status status);
- }
-
- public enum Status {
- /** Panel is closed */
- CLOSE ,
- /** Panel is opened */
- OPEN ;
-
- public static Status valueOf( int stats) {
- if (0 == stats) {
- return CLOSE ;
- } else if (1 == stats) {
- return OPEN ;
- } else {
- return CLOSE ;
- }
- }
- }
-
- private static final float DEFAULT_PERCENT = 0.2f;
- private static final int DEFAULT_DURATION = 300;
-
- private View mFrontView;
- private View mBehindView;
-
- private float mTouchSlop;
- private float mInitMotionY;
- private float mInitMotionX;
-
-
- private View mTarget;
- private float mSlideOffset;
- private Status mStatus = Status.CLOSE ;
- private boolean isFirstShowBehindView = true ;
- private float mPercent = DEFAULT_PERCENT;
- private long mDuration = DEFAULT_DURATION;
- private int mDefaultPanel = 0;
-
- private OnSlideDetailsListener mOnSlideDetailsListener;
-
- public SlideDetailsLayout(Context context) {
- this(context, null );
- }
-
- public SlideDetailsLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SlideDetailsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlideDetailsLayout, defStyleAttr, 0);
- mPercent = a.getFloat(R.styleable.SlideDetailsLayout_percent, DEFAULT_PERCENT);
- mDuration = a.getInt(R.styleable.SlideDetailsLayout_duration, DEFAULT_DURATION);
- 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 |