1. 程式人生 > >上拉檢視詳情和下拉隱藏詳情

上拉檢視詳情和下拉隱藏詳情

專案商品詳情頁的需求,實現上拉顯示和下拉隱藏詳情的功能,最終效果圖如圖:

這裡寫圖片描述

實現思路:上下通過判斷兩個ScrollView的滑動位置及觸控事件發生的位置,對他們進行隱藏和顯示。
上拉部分,由於內部包裹了大量帶有點選事件的元件,故需要在上拉條上進行拖動,否則會衝突無法判斷。這部分是通過對其包裹內容動態的呼叫layout方法來實現拖拽效果;
下拉部分,通過對下拉頭動態設定marginTop的值,來改變其高度,並達到隱藏和顯示的效果。

核心程式碼


    //上拉元件
    @Bind(R.id.xscrollview)
    XScrollView mXscrollview;
    @Bind
(R.id.scrollContainer) LinearLayout scrollContainer; //下拉元件 @Bind(R.id.scrollContainer2) RelativeLayout scrollContainer2; @Bind(R.id.svBottomDetails) ScrollView svBottomDetails; @Bind({R.id.llDownScroll}) LinearLayout llDownScroll; //上拉部分 private Rect rect = null
;//記錄scrollView包裹元件的位置 private int fullScroll;//scrollView滾動到底部時ScrollView的scrollY值 private float mDeltaY;//(上拉模組)拖動的距離 private int downScrollY;//開始拖動的ScrollView的scrollY值 private float startY;//開始拖動的MotionEvent的y值 //下拉部分 private float startY2; private float downScrollY2; private
float mDeltaY2; private boolean touched;//是否觸控,在ACTION_MOVE中做標記,記錄按下時需要的值 private RelativeLayout.LayoutParams mLayoutParams; /*******************監聽詳情的隱藏和顯示*******************/ private OnGoodsDetailsListener mOnGoodsDetailsListener; public void setOnGoodsDetailsListener(OnGoodsDetailsListener onGoodsDetailsListener) { mOnGoodsDetailsListener = onGoodsDetailsListener; } public interface OnGoodsDetailsListener{ void onShow(boolean showDetails); } //下拉隱藏詳情 private void initPullDown() { mLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); svBottomDetails.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //可能的衝突,改為在ACTION_MOVE中獲取 break; case MotionEvent.ACTION_UP: //向下拖動距離大於100 if (mDeltaY2 > 120 && downScrollY2 == 0) { Log.d(TAG, "onTouch: 隱藏詳情了"); //顯示上部 mXscrollview.setVisibility(View.VISIBLE); mXscrollview.smoothScrollTo(0, fullScroll);//滾動到底部 //隱藏詳情 svBottomDetails.setVisibility(View.GONE); //還原標題-->商品 詳情 評價 //修改返回按鍵和小箭頭的事件->點選結束act //將狀態傳到activity,改變標題 mOnGoodsDetailsListener.onShow(false); } // //恢復原有marginTop高度,隱藏頭 mLayoutParams.setMargins(0, (int) getResources().getDimension(R.dimen.pulldown_head_margin), 0, 0); llDownScroll.setLayoutParams(mLayoutParams); //重置 mDeltaY2 = 0; downScrollY2 = 0; startY2 = 0; touched = false; break; case MotionEvent.ACTION_MOVE: //從頂部開始的滑動,且向下滑 //在此記錄按下位置,取代ACTION_DOWN中 if (!touched) { startY2 = event.getY(); downScrollY2 = svBottomDetails.getScrollY(); Log.d(TAG, "onTouch: startY2 = " + startY2 + " , downScrollY2 = " + downScrollY2); } touched = true; mDeltaY2 = 0.5f * (event.getY() - startY2); Log.d(TAG, "onTouch: downScrollY2 = " + downScrollY2); Log.d(TAG, "onTouch: startY2 = " + startY2); Log.d(TAG, "onTouch: mDeltaY2 = " + mDeltaY2); if (downScrollY2 == 0 && mDeltaY2 > 0) { //計算marginTop高度,動態顯示頭高度 int top = (int) (-120 + mDeltaY2); Log.d(TAG, "onTouch: marginTop = " + top); mLayoutParams.setMargins(0, top, 0, 0); llDownScroll.setLayoutParams(mLayoutParams); } break; default: break; } return false; } }); } /** * 上拉檢視詳情 */ private void initPullUp() { //這一步操作為,獲取ScrollView的完全高度,在上拉時,判斷是否從最底部開始 mXscrollview.setOnScrollToBottomLintener(new XScrollView.OnScrollToBottomListener() { @Override public void onScrollBottomListener(boolean isBottom) { if (isBottom) { fullScroll = mXscrollview.getScrollY(); Log.d(TAG, "onScrollBottomListener: scrollY = " + fullScroll); } } }); mXscrollview.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //記錄按下時的Y值 startY = event.getY(); downScrollY = mXscrollview.getScrollY(); Log.d(TAG, "onTouch: startY = " + startY + " , downScrollY = " + downScrollY); if (rect == null) { rect = new Rect(scrollContainer.getLeft(), scrollContainer.getTop(), scrollContainer.getRight(), scrollContainer.getBottom()); } break; case MotionEvent.ACTION_UP: //拖動距離大於120 if (Math.abs(mDeltaY) > 120) { Log.d(TAG, "onTouch: 顯示詳情了"); //顯示詳情 svBottomDetails.setVisibility(View.VISIBLE); svBottomDetails.smoothScrollTo(0, 0); //隱藏上部 mXscrollview.setVisibility(View.GONE); //改變標題-->圖文詳情 //修改返回按鍵和小箭頭的事件->點選還原 //將狀態傳到activity,改變標題 mOnGoodsDetailsListener.onShow(true); } // 恢復原有高度 if (rect != null) { scrollContainer.layout(rect.left, rect.top, rect.right, rect.bottom); Log.d(TAG, "onTouch: 鬆手了"); } //重置 mDeltaY = 0; downScrollY = 0; startY = 0; break; case MotionEvent.ACTION_MOVE: //downScrollY != 0:不是從頂部開始的滑動;fullScroll不為0且不為負值(bug?) if (downScrollY != 0 && fullScroll > 0 && downScrollY >= fullScroll - 20) { //deltaY<0,向上滑動 mDeltaY = 0.5f * (event.getY() - startY); Log.d(TAG, "onTouch: downScrollY = " + downScrollY); Log.d(TAG, "onTouch: fullScroll = " + fullScroll); Log.d(TAG, "onTouch: mDeltaY = " + mDeltaY); if (rect != null) { scrollContainer.layout(rect.left, (int) (rect.top + mDeltaY), rect.right, (int) (rect.bottom + mDeltaY)); } } break; default: break; } return false; } }); }

佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/rlContainer"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/nc_good_bg">

    <!--上部ScrollView包裹上部內容-->

    <ScrollView
        android:id="@+id/xscrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/tvLoading"
        android:visibility="visible">

        <LinearLayout
            android:id="@+id/scrollContainer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">


                <!--省略部分-->

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="10dp"
                    android:background="@color/app_white"
                    android:drawableLeft="@drawable/arrow_top"
                    android:gravity="center"
                    android:orientation="horizontal">

                    <ImageView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:background="@drawable/arrow_top"/>

                    <TextView
                        android:id="@+id/tvUpScroll"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:background="@color/app_white"
                        android:gravity="center"
                        android:paddingBottom="20dp"
                        android:paddingLeft="4dp"
                        android:paddingTop="20dp"
                        android:text="上拉檢視圖文詳情"/>
                </LinearLayout>


        </LinearLayout>


    </ScrollView>


    <ScrollView
        android:id="@+id/svBottomDetails"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone">

        <RelativeLayout
            android:id="@+id/scrollContainer2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="visible">

            <!--給marginTop一個高度的負值,隱藏下拉的頭-->
            <LinearLayout
                android:id="@+id/llDownScroll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/pulldown_head_margin"
                android:background="@color/app_white"
                android:gravity="center"
                android:orientation="horizontal"
                android:visibility="visible">

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@drawable/arrow_down"/>

                <TextView
                    android:id="@+id/tvDownScroll"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@color/app_white"
                    android:gravity="center"
                    android:paddingBottom="20dp"
                    android:paddingLeft="4dp"
                    android:paddingTop="20dp"
                    android:text="下拉收起圖文詳情"/>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/llTabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_below="@+id/llDownScroll"
                android:background="@color/app_white"
                android:orientation="horizontal"
                android:padding="@dimen/dp8"
                android:visibility="visible">

                <!--省略部分-->

            </LinearLayout>

            <WebView
                android:id="@+id/wvImg"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_below="@+id/llTabs"/>


        </RelativeLayout>

    </ScrollView>

</RelativeLayout>

重寫的ScrollView,可獲取最大滾動高度

//滾動到底部時,clampedY變為true,其餘情況為false,通過回撥將狀態傳出去即可。
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        if (scrollY != 0 && null != mOnScrollToBottomListener) {
            mOnScrollToBottomListener.onScrollBottomListener(clampedY);
        }
    }

重寫後的ViewPager,可禁止水平滑動

public class MyViewPager extends ViewPager {

    public MyViewPager(Context context) {
        super(context);
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    //預設為true
    private boolean isCanScroll = true;

    //通過設定為false,禁止ViewPager的水平滑動
    public void setCanScroll(boolean isCanScroll) {
        this.isCanScroll = isCanScroll;
    }

    @Override
    public void scrollTo(int x, int y) {
        if (isCanScroll) {
            super.scrollTo(x, y);
        }
    }
}

Activity中控制標題顯示

/*******************控制標題顯示*********************/
    @Override
    public void onShow(boolean showDetails) {
        this.showDetails = showDetails;
        if (showDetails){
            setCommonHeader("圖文詳情");
            tabGoods.setVisibility(View.GONE);
            //禁止ViewPager水平滑動
            vpFragment.setCanScroll(false);
        }else {
            setCommonHeader("");
            //顯示TabLayout
            tabGoods.setVisibility(View.VISIBLE);
            //恢復ViewPager水平滑動
            vpFragment.setCanScroll(true);
        }
    }