Android開發仿淘寶商品詳情瀏覽效果 兩步曲
阿新 • • 發佈:2018-12-09
效果圖:
第一步佈局檔案:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/containerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff" android:orientation="vertical"> <!--父容器--> <com.little.monkey.myapplication.ScrollViewContainer android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/bottom" android:layout_below="@+id/lintonei" android:layout_marginBottom="-27dp"> <!--商品詳情--> <ScrollView android:id="@+id/mymyscrollview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffffff" android:scrollbars="none"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:id="@+id/layout_product" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/detial_product_img" android:layout_width="match_parent" android:layout_height="500dp" android:background="@mipmap/ic_launcher_round" android:scaleType="fitXY" /> </RelativeLayout> <View android:layout_width="fill_parent" android:layout_height="1dp" android:background="#f3f2f8" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="127dp" android:background="#ffffff"> <RelativeLayout android:id="@+id/layout_1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/detial_product_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="12dp" android:layout_marginTop="17dp" android:ellipsize="end" android:lines="2" android:maxLines="2" android:text="暫無商品 " android:textColor="#262626" android:textSize="16dp" /> <TextView android:id="@+id/detial_product_num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/detial_product_name" android:layout_marginLeft="12dp" android:layout_marginTop="6dp" android:text="已售00件" android:textColor="#999999" android:textSize="12dp" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="15dp" android:layout_marginRight="17dp"> <TextView android:id="@+id/textjrgwc" android:layout_width="105dp" android:layout_height="33dp" android:background="@color/colorPrimary" android:gravity="center" android:text="加入購物車" android:textColor="#ffffff" android:textSize="14dp" android:visibility="gone" /> <LinearLayout android:id="@+id/linearjjgwc" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/tvMinus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/ic_launcher_round" android:clickable="true" android:gravity="center" /> <TextView android:id="@+id/count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginBottom="3dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:gravity="center" android:text="0" android:textColor="#1a1a1a" android:textSize="18dp" /> <TextView android:id="@+id/tvAdd" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="7dp" android:background="@mipmap/ic_launcher_round" android:clickable="true" android:gravity="center" /> </LinearLayout> </RelativeLayout> <TextView android:id="@+id/detial_product_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/detial_product_num" android:layout_marginLeft="12dp" android:layout_marginTop="15dp" android:text="¥0.00" android:textColor="#f33637" android:textSize="18dp" android:textStyle="bold" /> <TextView android:id="@+id/textyuanjia" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/detial_product_num" android:layout_marginLeft="12dp" android:layout_marginTop="19dp" android:layout_toRightOf="@+id/detial_product_price" android:text="原價:" android:textSize="12dp" /> <TextView android:id="@+id/zhejia" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/detial_product_num" android:layout_marginTop="19dp" android:layout_toRightOf="@+id/textyuanjia" android:text="¥0.00" android:textSize="12dp" /> </RelativeLayout> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#d9d9d9" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="140dp" android:visibility="gone"> <TextView android:id="@+id/textspgg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="12dp" android:layout_marginTop="16dp" android:textColor="#676767" android:textSize="14dp" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="110dp" android:visibility="gone"> <TextView android:id="@+id/textname" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="12dp" android:layout_marginTop="21dp" android:textColor="#262626" android:textSize="14dp" /> <TextView android:id="@+id/textdz" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textname" android:layout_marginLeft="12dp" android:layout_marginTop="12dp" android:maxLength="20" android:textColor="#989898" android:textSize="13dp" /> <RelativeLayout android:id="@+id/relativedh" android:layout_width="64dp" android:layout_height="37dp" android:layout_alignParentRight="true" android:layout_below="@+id/textname"> <TextView android:layout_width="1dp" android:layout_height="fill_parent" android:background="#d8d8d8" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:src="@mipmap/ic_launcher_round" /> </RelativeLayout> <ImageView android:id="@+id/imagdw" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textdz" android:layout_marginLeft="10dp" android:layout_marginTop="11dp" android:src="@mipmap/ic_launcher_round" /> <TextView android:id="@+id/textmishu" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textdz" android:layout_marginLeft="5dp" android:layout_marginTop="12dp" android:layout_toRightOf="@+id/imagdw" android:textColor="#999999" android:textSize="13dp" /> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="9dp" android:background="#f2f2f2" /> <RelativeLayout android:id="@+id/linearpl" android:layout_width="fill_parent" android:layout_height="50dp" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:text="商品評價" android:textColor="#303030" android:textSize="15dp" /> <TextView android:id="@+id/textsong_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="15dp" android:layout_toLeftOf="@+id/xiangyou2_id" android:text="全部評價" android:textColor="#999999" android:textSize="12dp" /> <ImageView android:id="@+id/xiangyou2_id" android:layout_width="15dp" android:layout_height="15dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="13dp" android:src="@mipmap/ic_launcher_round" /> <Button android:id="@+id/chakangengduo" android:layout_width="wrap_content" android:layout_height="25dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="@color/colorPrimaryDark" android:text="檢視全部評價" android:textColor="#CDCDCD" android:textSize="12sp" android:visibility="gone" /> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="9dp" android:background="#f2f2f2" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="50dp" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="15dp" android:text="商品詳情" android:textColor="#303030" android:textSize="15dp" /> <ImageView android:layout_width="15dp" android:layout_height="15dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="13dp" android:src="@mipmap/ic_launcher_round" /> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#d9d9d9" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="90dp" android:background="#ffffff"> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginTop="20dp" android:gravity="center_horizontal" android:text="上滑檢視圖文詳情" android:textColor="#989898" android:textSize="14dp" /> </LinearLayout> </LinearLayout> </ScrollView> <!--圖文詳情--> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="-50dp" android:background="#f78ddd"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="asdfasdfas" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="asdfasdfas" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="asdfasdfas" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="asdfasdfas" /> </LinearLayout> </ScrollView> </com.little.monkey.myapplication.ScrollViewContainer> <!--標題欄--> <LinearLayout android:id="@+id/lintonei" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:id="@+id/title_layout_product" android:layout_width="match_parent" android:layout_height="45dp" android:background="#fff"> <ImageView android:id="@+id/iv_title_left" android:layout_width="wrap_content" android:layout_height="match_parent" android:padding="3dp" android:src="@mipmap/ic_launcher_round" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerInParent="true" android:gravity="center" android:text="商品詳情" android:textColor="@color/colorAccent" android:textSize="18sp" /> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#d9d9d9" /> </LinearLayout> <!--底部購物車--> <RelativeLayout android:id="@+id/bottom" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentBottom="true" android:background="#3b3b38" android:gravity="center_vertical" android:orientation="horizontal"> <TextView android:id="@+id/tvCost" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="89dp" android:text="¥ 0.0" android:textColor="#fff" android:textSize="16dp" /> <TextView android:id="@+id/tvSubmit" android:layout_width="130dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:background="#4a4a47" android:gravity="center" android:text="去結算" android:textColor="#fff" android:textSize="16dp" /> </RelativeLayout> <RelativeLayout android:id="@+id/relative_gwc" android:layout_width="89dp" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp"> <ImageView android:id="@+id/imgCart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:src="@mipmap/ic_launcher_round" /> <TextView android:id="@+id/tvount" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="13dp" android:background="@color/colorAccent" android:gravity="center" android:text="0" android:textColor="#fff" android:textSize="12sp" android:visibility="gone" /> </RelativeLayout> </RelativeLayout>
第二步封裝一個父容器ScrollViewContainer:
package com.little.monkey.myapplication; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.widget.RelativeLayout; import android.widget.ScrollView; import java.util.Timer; import java.util.TimerTask; /** * Created by Administrator on 2018/9/13 0013. */ public class ScrollViewContainer extends RelativeLayout{ /** * 自動上滑 */ public static final int AUTO_UP = 0; /** * 自動下滑 */ public static final int AUTO_DOWN = 1; /** * 動畫完成 */ public static final int DONE = 2; /** * 動畫速度 */ public static final float SPEED = 6.5f; private boolean isMeasured = false; /** * 用於計算手滑動的速度 */ private VelocityTracker vt; private int mViewHeight; private int mViewWidth; private View topView; private View bottomView; private boolean canPullDown; private boolean canPullUp; private int state = DONE; /** * 記錄當前展示的是哪個view,0是topView,1是bottomView */ private int mCurrentViewIndex = 0; /** * 手滑動距離,這個是控制佈局的主要變數 */ private float mMoveLen; private MyTimer mTimer; private float mLastY; /** * 用於控制是否變動佈局的另一個條件,mEvents==0時佈局可以拖拽了,mEvents==-1時可以捨棄將要到來的第一個move事件, * 這點是去除多點拖動劇變的關鍵 */ private int mEvents; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (mMoveLen != 0) { if (state == AUTO_UP) { mMoveLen -= SPEED; if (mMoveLen <= -mViewHeight) { mMoveLen = -mViewHeight; state = DONE; mCurrentViewIndex = 1; } } else if (state == AUTO_DOWN) { mMoveLen += SPEED; if (mMoveLen >= 0) { mMoveLen = 0; state = DONE; mCurrentViewIndex = 0; } } else { mTimer.cancel(); } } requestLayout(); } }; public ScrollViewContainer(Context context) { super(context); init(); } public ScrollViewContainer(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollViewContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mTimer = new MyTimer(handler); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: if (vt == null) vt = VelocityTracker.obtain(); else vt.clear(); mLastY = ev.getY(); vt.addMovement(ev); mEvents = 0; break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: // 多一隻手指按下或擡起時捨棄將要到來的第一個事件move,防止多點拖拽的bug mEvents = -1; break; case MotionEvent.ACTION_MOVE: vt.addMovement(ev); if (canPullUp && mCurrentViewIndex == 0 && mEvents == 0) { mMoveLen += (ev.getY() - mLastY); // 防止上下越界 if (mMoveLen > 0) { mMoveLen = 0; mCurrentViewIndex = 0; } else if (mMoveLen < -mViewHeight) { mMoveLen = -mViewHeight; mCurrentViewIndex = 1; } if (mMoveLen < -8) { // 防止事件衝突 ev.setAction(MotionEvent.ACTION_CANCEL); } } else if (canPullDown && mCurrentViewIndex == 1 && mEvents == 0) { mMoveLen += (ev.getY() - mLastY); // 防止上下越界 if (mMoveLen < -mViewHeight) { mMoveLen = -mViewHeight; mCurrentViewIndex = 1; } else if (mMoveLen > 0) { mMoveLen = 0; mCurrentViewIndex = 0; } if (mMoveLen > 8 - mViewHeight) { // 防止事件衝突 ev.setAction(MotionEvent.ACTION_CANCEL); } } else mEvents++; mLastY = ev.getY(); requestLayout(); break; case MotionEvent.ACTION_UP: mLastY = ev.getY(); vt.addMovement(ev); vt.computeCurrentVelocity(700); // 獲取Y方向的速度 float mYV = vt.getYVelocity(); if (mMoveLen == 0 || mMoveLen == -mViewHeight) break; if (Math.abs(mYV) < 500) { // 速度小於一定值的時候當作靜止釋放,這時候兩個View往哪移動取決於滑動的距離 if (mMoveLen <= -mViewHeight / 2) { state = AUTO_UP; } else if (mMoveLen > -mViewHeight / 2) { state = AUTO_DOWN; } } else { // 擡起手指時速度方向決定兩個View往哪移動 if (mYV < 0) state = AUTO_UP; else state = AUTO_DOWN; } mTimer.schedule(2); try { vt.recycle(); vt = null;//防止第二次報錯 } catch (Exception e) { e.printStackTrace(); } break; } super.dispatchTouchEvent(ev); return true; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { topView.layout(0, (int) mMoveLen, mViewWidth, topView.getMeasuredHeight() + (int) mMoveLen); bottomView.layout(0, topView.getMeasuredHeight() + (int) mMoveLen, mViewWidth, topView.getMeasuredHeight() + (int) mMoveLen + bottomView.getMeasuredHeight()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!isMeasured) { isMeasured = true; mViewHeight = getMeasuredHeight(); mViewWidth = getMeasuredWidth(); topView = getChildAt(0); bottomView = getChildAt(1); bottomView.setOnTouchListener(bottomViewTouchListener); topView.setOnTouchListener(topViewTouchListener); } } private OnTouchListener topViewTouchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { ScrollView sv = (ScrollView) v; if (sv.getScrollY() == (sv.getChildAt(0).getMeasuredHeight() - sv .getMeasuredHeight()) && mCurrentViewIndex == 0) canPullUp = true; else canPullUp = false; return false; } }; private OnTouchListener bottomViewTouchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { ScrollView sv = (ScrollView) v; if (sv.getScrollY() == 0 && mCurrentViewIndex == 1) canPullDown = true; else canPullDown = false; return false; } }; class MyTimer { private Handler handler; private Timer timer; private MyTask mTask; public MyTimer(Handler handler) { this.handler = handler; timer = new Timer(); } public void schedule(long period) { if (mTask != null) { mTask.cancel(); mTask = null; } mTask = new MyTask(handler); timer.schedule(mTask, 0, period); } public void cancel() { if (mTask != null) { mTask.cancel(); mTask = null; } } class MyTask extends TimerTask { private Handler handler; public MyTask(Handler handler) { this.handler = handler; } @Override public void run() { handler.obtainMessage().sendToTarget(); } } } }
搞定!