<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      自己定義ViewGroup實現仿淘寶的商品詳情頁

      近期公司在新版本號上有一個須要。 要在首頁加入一個滑動效果, 詳細就是仿照X寶的商品詳情頁, 拉到頁面底部時有一個粘滯效果,

       例如以下圖 X東的商品詳情頁,假設用戶繼續向上拉的話就進入商品圖文描寫敘述界面:



      剛開始是想拿來主義。直接從網上找個現成的demo來用, 可是網上無一例外的答案都特別統一: 差點兒所有是ScrollView中再套兩個ScrollView,或者是一個LinearLayout中套兩個ScrollView。 通過指定父view和子view的focus來切換滑動的處理界面---即通過view的requestDisallowInterceptTouchEvent方法來決定是哪一個ScrollView來處理滑動事件。

      使用以上方法盡管能夠解一時之渴, 可是存在幾點缺陷:

      1  擴展性不強 : 假設興許產品要求不止是兩頁滑動呢。是三頁滑動呢。 難道要嵌3個ScrollView并通過N個推斷來實現嗎

      2  兼容性不強 : 假設須要在某一個子頁中須要處理左右滑動事件或者雙指操作事件呢, 此方法就無法實現了

      3 個人原因 : 個人喜歡自己掌握主動性,事件的處理自己來控制更靠譜一些(PS:就如同一份感情一樣,須要細心去經營^_^)


      總和以上原因, 自己實現了一個ViewGroup,實現文章開頭提到的效果。 廢話不多說  直接上源代碼,下面僅僅是部分主要源代碼,并對每個方法都做了凝視,能夠參照凝視理解。

        文章最后對這個ViewGroup加了一點實現的細節以及怎樣使用此VIewGroup。 以及demo地址

      package com.mcoy.snapscrollview;
      
      import android.content.Context;
      import android.util.AttributeSet;
      import android.util.Log;
      import android.view.MotionEvent;
      import android.view.VelocityTracker;
      import android.view.View;
      import android.view.ViewConfiguration;
      import android.view.ViewGroup;
      import android.widget.Scroller;
      
      /**
       * @author jiangxinxing---mcoy in English
       * 
       * 了解此ViewGroup之前。 有兩點一定要做到心中有數
       * 一個是對Scroller的使用。 還有一個是對onInterceptTouchEvent和onTouchEvent要做到非常熟悉
       * 下面幾個站點能夠做參考用
       * http://blog.csdn.net/bigconvience/article/details/26697645
       * http://blog.csdn.net/androiddevelop/article/details/8373782
       * http://blog.csdn.net/xujainxing/article/details/8985063
       */
      public class McoySnapPageLayout extends ViewGroup {
      	
              。。

      。。

      public interface McoySnapPage { /** * 返回page根節點 * * @return */ View getRootView(); /** * 是否滑動到最頂端 * 第二頁必須自己實現此方法。來推斷是否已經滑動到第二頁的頂部 * 并決定是否要繼續滑動到第一頁 */ boolean isAtTop(); /** * 是否滑動到最底部 * 第一頁必須自己實現此方法,來推斷是否已經滑動到第二頁的底部 * 并決定是否要繼續滑動到第二頁 */ boolean isAtBottom(); } public interface PageSnapedListener { /** * @mcoy * 當從某一頁滑動到還有一頁完畢時的回調函數 */ void onSnapedCompleted(int derection); } 。

      。。。。。

      /** * 設置上下頁面 * @param pageTop * @param pageBottom */ public void setSnapPages(McoySnapPage pageTop, McoySnapPage pageBottom) { mPageTop = pageTop; mPageBottom = pageBottom; addPagesAndRefresh(); } private void addPagesAndRefresh() { // 設置頁面id mPageTop.getRootView().setId(0); mPageBottom.getRootView().setId(1); addView(mPageTop.getRootView()); addView(mPageBottom.getRootView()); postInvalidate(); } /** * @mcoy add * computeScroll方法會調用postInvalidate()方法。 而postInvalidate()方法中系統 * 又會調用computeScroll方法, 因此會一直在循環互相調用。 循環的終結點是在computeScrollOffset() * 當computeScrollOffset這種方法返回false時。說明已經結束滾動。 * * 重要:真正的實現此view的滾動是調用scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); */ @Override public void computeScroll() { //先推斷mScroller滾動是否完畢 if (mScroller.computeScrollOffset()) { if (mScroller.getCurrY() == (mScroller.getFinalY())) { if (mNextDataIndex > mDataIndex) { mFlipDrection = FLIP_DIRECTION_DOWN; makePageToNext(mNextDataIndex); } else if (mNextDataIndex < mDataIndex) { mFlipDrection = FLIP_DIRECTION_UP; makePageToPrev(mNextDataIndex); }else{ mFlipDrection = FLIP_DIRECTION_CUR; } if(mPageSnapedListener != null){ mPageSnapedListener.onSnapedCompleted(mFlipDrection); } } //這里調用View的scrollTo()完畢實際的滾動 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //必須調用該方法。否則不一定能看到滾動效果 postInvalidate(); } } private void makePageToNext(int dataIndex) { mDataIndex = dataIndex; mCurrentScreen = getCurrentScreen(); } private void makePageToPrev(int dataIndex) { mDataIndex = dataIndex; mCurrentScreen = getCurrentScreen(); } public int getCurrentScreen() { for (int i = 0; i < getChildCount(); i++) { if (getChildAt(i).getId() == mDataIndex) { return i; } } return mCurrentScreen; } public View getCurrentView() { for (int i = 0; i < getChildCount(); i++) { if (getChildAt(i).getId() == mDataIndex) { return getChildAt(i); } } return null; } /* * (non-Javadoc) * * @see * android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent) * 重寫了父類的onInterceptTouchEvent()。主要功能是在onTouchEvent()方法之前處理 * touch事件。包含:down、up、move事件。 * 當onInterceptTouchEvent()返回true時進入onTouchEvent()。 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { return true; } final float x = ev.getX(); final float y = ev.getY(); switch (action) { case MotionEvent.ACTION_MOVE: // 記錄y與mLastMotionY差值的絕對值。 // yDiff大于gapBetweenTopAndBottom時就覺得界面拖動了足夠大的距離,屏幕就能夠移動了。 final int yDiff = (int)(y - mLastMotionY); boolean yMoved = Math.abs(yDiff) > gapBetweenTopAndBottom; if (yMoved) { if(MCOY_DEBUG) { Log.e(TAG, "yDiff is " + yDiff); Log.e(TAG, "mPageTop.isFlipToBottom() is " + mPageTop.isAtBottom()); Log.e(TAG, "mCurrentScreen is " + mCurrentScreen); Log.e(TAG, "mPageBottom.isFlipToTop() is " + mPageBottom.isAtTop()); } if(yDiff < 0 && mPageTop.isAtBottom() && mCurrentScreen == 0 || yDiff > 0 && mPageBottom.isAtTop() && mCurrentScreen == 1){ Log.e("mcoy", "121212121212121212121212"); mTouchState = TOUCH_STATE_SCROLLING; } } break; case MotionEvent.ACTION_DOWN: // Remember location of down touch mLastMotionY = y; Log.e("mcoy", "mScroller.isFinished() is " + mScroller.isFinished()); mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // Release the drag mTouchState = TOUCH_STATE_REST; break; } boolean intercept = mTouchState != TOUCH_STATE_REST; Log.e("mcoy", "McoySnapPageLayout---onInterceptTouchEvent return " + intercept); return intercept; } /* * (non-Javadoc) * * @see android.view.View#onTouchEvent(android.view.MotionEvent) * 主要功能是處理onInterceptTouchEvent()返回值為true時傳遞過來的touch事件 */ @Override public boolean onTouchEvent(MotionEvent ev) { Log.e("mcoy", "onTouchEvent--" + System.currentTimeMillis()); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); final int action = ev.getAction(); final float x = ev.getX(); final float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; case MotionEvent.ACTION_MOVE: if(mTouchState != TOUCH_STATE_SCROLLING){ // 記錄y與mLastMotionY差值的絕對值。 // yDiff大于gapBetweenTopAndBottom時就覺得界面拖動了足夠大的距離,屏幕就能夠移動了。 final int yDiff = (int) Math.abs(y - mLastMotionY); boolean yMoved = yDiff > gapBetweenTopAndBottom; if (yMoved) { mTouchState = TOUCH_STATE_SCROLLING; } } // 手指拖動屏幕的處理 if ((mTouchState == TOUCH_STATE_SCROLLING)) { // Scroll to follow the motion event final int deltaY = (int) (mLastMotionY - y); mLastMotionY = y; final int scrollY = getScrollY(); if(mCurrentScreen == 0){//顯示第一頁。僅僅能上拉時使用 if(mPageTop != null && mPageTop.isAtBottom()){ scrollBy(0, Math.max(-1 * scrollY, deltaY)); } }else{ if(mPageBottom != null && mPageBottom.isAtTop()){ scrollBy(0, deltaY); } } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // 彈起手指后。切換屏幕的處理 if (mTouchState == TOUCH_STATE_SCROLLING) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int velocityY = (int) velocityTracker.getYVelocity(); if (Math.abs(velocityY) > SNAP_VELOCITY) { if( velocityY > 0 && mCurrentScreen == 1 && mPageBottom.isAtTop()){ snapToScreen(mDataIndex-1); }else if(velocityY < 0 && mCurrentScreen == 0){ snapToScreen(mDataIndex+1); }else{ snapToScreen(mDataIndex); } } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } }else{ } mTouchState = TOUCH_STATE_REST; break; default: break; } return true; } private void clearOnTouchEvents(){ mTouchState = TOUCH_STATE_REST; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } private void snapToDestination() { // 計算應該去哪個屏 final int flipHeight = getHeight() / 8; int whichScreen = -1; final int topEdge = getCurrentView().getTop(); if(topEdge < getScrollY() && (getScrollY()-topEdge) >= flipHeight && mCurrentScreen == 0){ //向下滑動 whichScreen = mDataIndex + 1; }else if(topEdge > getScrollY() && (topEdge - getScrollY()) >= flipHeight && mCurrentScreen == 1){ //向上滑動 whichScreen = mDataIndex - 1; }else{ whichScreen = mDataIndex; } Log.e(TAG, "snapToDestination mDataIndex = " + mDataIndex); Log.e(TAG, "snapToDestination whichScreen = " + whichScreen); snapToScreen(whichScreen); } private void snapToScreen(int dataIndex) { if (!mScroller.isFinished()) return; final int direction = dataIndex - mDataIndex; mNextDataIndex = dataIndex; boolean changingScreens = dataIndex != mDataIndex; View focusedChild = getFocusedChild(); if (focusedChild != null && changingScreens) { focusedChild.clearFocus(); } //在這里推斷是否已到目標位置~ int newY = 0; switch (direction) { case 1: //須要滑動到第二頁 Log.e(TAG, "the direction is 1"); newY = getCurrentView().getBottom(); // 終于停留的位置 break; case -1: //須要滑動到第一頁 Log.e(TAG, "the direction is -1"); Log.e(TAG, "getCurrentView().getTop() is " + getCurrentView().getTop() + " getHeight() is " + getHeight()); newY = getCurrentView().getTop() - getHeight(); // 終于停留的位置 break; case 0: //滑動距離不夠, 因此不造成換頁。回到滑動之前的位置 Log.e(TAG, "the direction is 0"); newY = getCurrentView().getTop(); //第一頁的top是0, 第二頁的top應該是第一頁的高度 break; default: break; } final int cy = getScrollY(); // 啟動的位置 Log.e(TAG, "the newY is " + newY + " cy is " + cy); final int delta = newY - cy; // 滑動的距離,正值是往左滑<—。負值是往右滑—> mScroller.startScroll(0, cy, 0, delta, Math.abs(delta)); invalidate(); } 。。。

      }


      McoySnapPage是定義在VIewGroup的一個接口, 比方說我們須要類似某東商品詳情那樣,有上下兩頁的效果。 那我就須要自定義兩個類實現這個接口。并實現接口的方法。getRootView須要返回當前頁須要顯示的布局內容;isAtTop須要返回當前頁是否已經在頂端; isAtBottom須要返回當前頁是否已經在底部

      onInterceptTouchEventonTouchEvent決定當前的滑動狀態, 并決定是有當前VIewGroup攔截touch事件還是由子view去消費touch事件


      Demo地址: http://download.csdn.net/detail/zxm317122667/8926295

      PS: Mcoy是本人的英文名稱, 希望不要引起誤會^_^


      posted on 2017-04-09 12:40  gcczhongduan  閱讀(291)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产精品久久久久aaaa| 永久免费AV无码网站大全| 精品国产亚洲第一区二区三区| 亚洲熟妇无码av另类vr影视| 日韩乱码视频一区二区三区 | 在线免费观看毛片av| 国产 浪潮av性色四虎| 伊人久久久av老熟妇色| 性少妇tubevⅰdeos高清| 中文字幕少妇人妻精品| 色狠狠综合天天综合综合| 亚洲精品在线二区三区| 色午夜一av男人的天堂| 在线aⅴ亚洲中文字幕| 欧美巨大极度另类| 久久一日本道色综合久久| 精品国产亚洲区久久露脸| 国产在线国偷精品免费看| 精品日韩亚洲av无码| 成人福利国产午夜AV免费不卡在线 | 精品中文人妻在线不卡| 国产一区二区亚洲精品| 亚洲区福利视频免费看| 一本一道av中文字幕无码| 久久99九九精品久久久久蜜桃| 亚洲精品理论电影在线观看| 亚洲欧美日韩国产精品专区| 日本免费一区二区三区日本| 日韩精品成人网页视频在线| 成人av午夜在线观看| 少妇人妻精品无码专区视频| 精品无码黑人又粗又大又长 | 欧美人与动欧交视频| 亚洲av二区国产精品| 亚洲国产精品一区二区久久| 黑人猛精品一区二区三区| 97欧美精品系列一区二区| 蜜芽久久人人超碰爱香蕉| 久久久久四虎精品免费入口| 亚洲黄色第一页在线观看| 无码午夜福利片|