国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android仿淘寶商品瀏覽界面圖片滾動效果

2020-01-02 07:00:34
字體:
來源:轉載
供稿:網友

用手機淘寶瀏覽商品詳情時,商品圖片是放在后面的,在第一個ScrollView滾動到最底下時會有提示,繼續拖動才能瀏覽圖片。仿照這個效果寫一個出來并不難,只要定義一個Layout管理兩個ScrollView就行了,當第一個ScrollView滑到底部時,再次向上滑動進入第二個ScrollView。效果如下:

需要注意的地方是:

      1、如果是手動滑到底部需要再次按下才能繼續往下滑,自動滾動到底部則不需要

      2、在由上一個ScrollView滑動到下一個ScrollView的過程中多只手指相繼拖動也不會導致布局的劇變,也就是多個pointer的滑動不會導致move距離的劇變。

這個Layout的實現思路是:

     在布局中放置兩個ScrollView,并為其設置OnTouchListener,時刻判斷ScrollView的滾動距離,一旦第一個ScrollView滾動到底部,則標識改為可向上拖動,此時開始記錄滑動距離mMoveLen,根據mMoveLen重新layout兩個ScrollView;同理,監聽第二個ScrollView是否滾動到頂部,以往下拖動。

OK,明白了原理之后可以看代碼了:

package com.jingchen.tbviewer;  import java.util.Timer; import java.util.TimerTask;  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;  /**  * 包含兩個ScrollView的容器  *  * @author chenjing  *  */ 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();       } 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();       }      }   }  } 

注釋寫的很清楚了,有幾個關鍵點需要講一下
    1、由于這里為兩個ScrollView設置了OnTouchListener,所以在其他地方不能再設置了,否則就白搭了。

    2、兩個ScrollView的layout參數統一由mMoveLen決定。

    3、變量mEvents有兩個作用:一是防止手動滑到底部或頂部時繼續滑動而改變布局,必須再次按下才能繼續滑動;二是在新的pointer down或up時把mEvents設置成-1可以舍棄將要到來的第一個move事件,防止mMoveLen出現劇變。為什么會出現劇變呢?因為假設一開始只有一只手指在滑動,記錄的坐標值是這個pointer的事件坐標點,這時候另一只手指按下了導致事件又多了一個pointer,這時候到來的move事件的坐標可能就變成了新的pointer的坐標,這時計算與上一次坐標的差值就會出現劇變,變化的距離就是兩個pointer間的距離。所以要把這個move事件舍棄掉,讓mLastY值記錄這個pointer的坐標再開始計算mMoveLen。pointer up的時候也一樣。

理解了這幾點,看起來就沒什么難度了,代碼量也很小。

MainActivity的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"   android:layout_width="match_parent"   android:layout_height="match_parent" >    <com.jingchen.tbviewer.ScrollViewContainer     android:layout_width="match_parent"     android:layout_height="match_parent" >      <ScrollView       android:layout_width="match_parent"       android:layout_height="match_parent" >        <RelativeLayout         android:layout_width="wrap_content"         android:layout_height="wrap_content" >          <LinearLayout           android:id="@+id/imagesLayout"           android:layout_width="match_parent"           android:layout_height="wrap_content"           android:gravity="center_horizontal"           android:orientation="vertical" >            <ImageView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:background="@drawable/h" />            <ImageView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:background="@drawable/i" />            <ImageView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:background="@drawable/j" />            <ImageView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:background="@drawable/k" />            <ImageView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:background="@drawable/l" />            <ImageView             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:background="@drawable/m" />         </LinearLayout>          <TextView           android:layout_width="match_parent"           android:layout_height="60dp"           android:layout_below="@id/imagesLayout"           android:background="#eeeeee"           android:gravity="center"           android:text="繼續拖動,查看更多美女"           android:textSize="20sp" />       </RelativeLayout>     </ScrollView>      <ScrollView       android:layout_width="match_parent"       android:layout_height="match_parent"       android:background="#000000" >        <LinearLayout         android:layout_width="match_parent"         android:layout_height="match_parent"         android:gravity="center_horizontal"         android:orientation="vertical" >          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/a" />          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/b" />          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/c" />          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/d" />          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/e" />          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/f" />          <ImageView           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:background="@drawable/g" />       </LinearLayout>     </ScrollView>   </com.jingchen.tbviewer.ScrollViewContainer>  </RelativeLayout> 

在ScrollView中放了幾張圖片而已。
MainActivity的代碼:

package com.jingchen.tbviewer;  import android.app.Activity; import android.os.Bundle; import android.view.Menu;  public class MainActivity extends Activity {   @Override   protected void onCreate(Bundle savedInstanceState)   {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);   }    @Override   public boolean onCreateOptionsMenu(Menu menu)   {     getMenuInflater().inflate(R.menu.main, menu);     return true;   }  } 

以上就是本文的全部內容,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 堆龙德庆县| 蛟河市| 容城县| 仲巴县| 平和县| 蒙阴县| 苏尼特左旗| 株洲县| 报价| 拉孜县| 开鲁县| 安新县| 通海县| 洪湖市| 平邑县| 沾化县| 井研县| 雅江县| 宿迁市| 化隆| 龙州县| 青浦区| 囊谦县| 桐柏县| 梁平县| 福建省| 武冈市| 云阳县| 齐河县| 宁晋县| 乐清市| 阿拉善左旗| 洞头县| 永胜县| 广水市| 泽普县| 明光市| 石屏县| 台中市| 宁乡县| 偃师市|