亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 自定義布局豎向的ViewPager的實(shí)現(xiàn)

 更新時(shí)間:2017年05月17日 11:42:26   作者:Android_Study_OK  
這篇文章主要介紹了Android 自定義布局豎向的ViewPager的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下

Android 自定義布局豎向的ViewPager的實(shí)現(xiàn)

效果圖:

這個(gè)自定義控件涉及到的知識(shí)點(diǎn):

自定義ViewGroup中onMeasure和onLayout的寫法
彈性滾動(dòng)Scroller的用法
速度軌跡追蹤器VelocityTracker的用法
如何處理滑動(dòng)事件沖突

dispatchTouchEvent:(外部攔截)告訴此ScrollLayout的父布局,什么時(shí)候該攔截觸摸事件,什么時(shí)候不該攔截觸摸事件

onInterceptTouchEvent:(內(nèi)部攔截)ScrollLayout告訴自己什么時(shí)候要攔截內(nèi)部子View的觸摸事件,什么時(shí)候不要攔截內(nèi)部子View的觸摸事件

處理觸摸滑動(dòng)的思路:

  1. 先實(shí)現(xiàn)布局跟著手指的滑動(dòng)而滑動(dòng) scrollBy
  2. 處理好邊界條件(這次的處理邊界,僅適用于低速滑動(dòng)情況下)
  3. 如果是快速滑動(dòng)VelocityTracker,必須再次考慮邊界問題(上面考慮的邊界問題不適用于快速滑動(dòng)情況)
  4. 如果是低速滑動(dòng),要根據(jù)手指滑動(dòng)方向和布局滑動(dòng)的距離一起做判斷,來(lái)確定,頁(yè)面該滑動(dòng)到那個(gè)頁(yè)面,這里用到了彈性滑動(dòng)Scroller
  5. 難點(diǎn)來(lái)了:算法,
//即確定當(dāng)前顯示的子控件的位置,
//確定彈性滑動(dòng)滑向那個(gè)位置 
if (Math.abs(velocityY) > criticalVelocityY) {//當(dāng)手指滑動(dòng)速度快時(shí),按照速度方向直接翻頁(yè) 
// 重點(diǎn)二、快速滑動(dòng)時(shí),如何判斷當(dāng)前顯示的是第幾個(gè)控件,并且再次包含邊界判斷(必須包含邊界判斷,因?yàn)榍懊娴倪吔缗袛啵贿m用于低速滑動(dòng)時(shí)) 
if (shouZhiXiangXiaHuaDong) { 
if (currentPage > 1) {//★★★★★★★★邊界限制,防止滑倒第一個(gè),還繼續(xù)滑動(dòng),注意★(currentPage-2) 
mScroller.startScroll(0, getScrollY(), 0, childHeight * (currentPage - 2) - getScrollY()); 
currentPage--; 
} 
} else { 
if (currentPage < childCount) {//★★★★★★★邊界限制,防止滑倒最后一個(gè),還繼續(xù)滑動(dòng),注意★currentPage 
mScroller.startScroll(0, getScrollY(), 0, childHeight * currentPage - getScrollY()); 
currentPage++; 
} 
} 
Log.e("eee", currentPage + "");

總結(jié)

當(dāng)要寫一個(gè)算法時(shí),不要著急試圖一下子寫出來(lái),這樣往往陷入盲目,應(yīng)該是一步一步地推導(dǎo),一步一步實(shí)現(xiàn)代碼,指導(dǎo)最后找到規(guī)律,類似于歸納總結(jié)、通項(xiàng)公式的方法。

代碼如下:(注釋很全)

package beautlful.time.com.beautlfultime.view;

import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
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;

/**
 * 注意:此自定義viewgroup只適用于每個(gè)子控件為match_parent的情況,其實(shí)一般情況也都是這種情況
 * 注意:此自定義viewgroup,沒有考慮padding的情況,使用者不要在ScrollerLayout里使用任何padding,否則你看到的不是你想要的,
 * 為了實(shí)現(xiàn)padding效果,你可以為ScrollerLayout的外層再套一層線性布局(或其他布局),在外層布局里使用padding值
 * 此自定義viewgroup基于郭霖博客改編,想了解具體實(shí)現(xiàn)細(xì)節(jié),請(qǐng)參照:
 * Android Scroller完全解析,關(guān)于Scroller你所需知道的一切
 * http://blog.csdn.net/guolin_blog/article/details/48719871
 */
public class VerticalViewPager extends ViewGroup {
  int currentPage = 1;

  /**
   * 速度軌跡追蹤器
   */
  private VelocityTracker mVelocityTracker;

  /**
   * 此次計(jì)算速度你想要的最大值
   */
  private final int mMaxVelocity;

  /**
   * 第一個(gè)觸點(diǎn)的id, 此時(shí)可能有多個(gè)觸點(diǎn),但至少一個(gè)
   */
  private int mPointerId;

  /**
   * 計(jì)算出的豎向滾動(dòng)速率
   */
  private float velocityY;

  /**
   * 手指橫向滑動(dòng)的速率臨界值,大于這個(gè)值時(shí),不考慮手指滑動(dòng)的距離,直接滾動(dòng)到最左邊或者最右邊
   */
  private int criticalVelocityY = 2500;

  /**
   * 用于完成滾動(dòng)操作的實(shí)例
   */
  private Scroller mScroller;

  /**
   * 判定為拖動(dòng)的最小移動(dòng)像素?cái)?shù)
   */
  private int mTouchSlop;

  /**
   * 手機(jī)按下時(shí)的屏幕坐標(biāo)
   */
  private float mYDown;

  /**
   * 手機(jī)當(dāng)時(shí)所處的屏幕坐標(biāo)
   */
  private float mYMove;

  /**
   * 上次觸發(fā)ACTION_MOVE事件時(shí)的屏幕坐標(biāo)
   */
  private float mYLastMove;

  /**
   * 界面可滾動(dòng)的頂部邊界
   */
  private int topBorder;

  /**
   * 界面可滾動(dòng)的底部邊界
   */
  private int bottomBorder;


  /**
   * 子控件的高度(這里每個(gè)子控件高度都一樣,都是match_parent)
   */
  private int childHeight;


  /**
   * 手指是否是向下滑動(dòng)
   */
  private boolean shouZhiXiangXiaHuaDong;
  private int childCount;


  public VerticalViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    // 第一步,創(chuàng)建Scroller的實(shí)例
    mScroller = new Scroller(context);
    ViewConfiguration configuration = ViewConfiguration.get(context);
    // 獲取TouchSlop值
    mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
    //此次計(jì)算速度你想要的最大值
    mMaxVelocity = ViewConfiguration.get(context).getMaximumFlingVelocity();
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View childView = getChildAt(i);
      // 為ScrollerLayout中的每一個(gè)子控件測(cè)量大小
      measureChild(childView, widthMeasureSpec, heightMeasureSpec);
    }
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (changed) {
      /**
       * 當(dāng)前子控件之前的所有子控件的總寬度
       */
      int preChildViewTotalHeight = 0;
      for (int i = 0; i < childCount; i++) {
        View childView = getChildAt(i);
        // 為ScrollerLayout中的每一個(gè)子控件在豎直方向上進(jìn)行布局
        if (i == 0) {
          childView.layout(
              0,
              0,
              childView.getMeasuredWidth(),
              childView.getMeasuredHeight());

        } else {
          childView.layout(
              0,
              preChildViewTotalHeight,
              childView.getMeasuredWidth(),
              preChildViewTotalHeight + childView.getMeasuredHeight());
        }
        preChildViewTotalHeight += childView.getMeasuredHeight();

      }
      // 初始化上下邊界值
      topBorder = getChildAt(0).getTop();
      bottomBorder = getChildAt(getChildCount() - 1).getBottom();

      childHeight = getChildAt(0).getMeasuredHeight();


    }
  }


  private int downX;
  private int downY;

  //    告訴此ScrollLayout的父布局,什么時(shí)候該攔截觸摸事件,什么時(shí)候不該攔截觸摸事件
  public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //讓當(dāng)前ScrollerLayout對(duì)應(yīng)的父控件不要去攔截事件
        getParent().requestDisallowInterceptTouchEvent(true);
        downX = (int) ev.getX();
        downY = (int) ev.getY();
        break;
      case MotionEvent.ACTION_MOVE:
        int moveX = (int) ev.getX();
        int moveY = (int) ev.getY();

        //請(qǐng)求父控件ViewPager攔截觸摸事件,ViewPager左右滾動(dòng)時(shí),不要觸發(fā)該布局的上下滑動(dòng)
        if (Math.abs(moveY - downY) < Math.abs(moveX - downX)) {
          getParent().requestDisallowInterceptTouchEvent(false);
        } else {
          //請(qǐng)求父控件ViewPager不要攔截觸摸事件,ScrollerLayout自己可以上下滑動(dòng)
          getParent().requestDisallowInterceptTouchEvent(true);
        }

        break;

      case MotionEvent.ACTION_CANCEL:


        break;
      case MotionEvent.ACTION_UP:


        break;
    }
    return super.dispatchTouchEvent(ev);
  }


  //   ScrollLayout告訴自己什么時(shí)候要攔截內(nèi)部子View的觸摸事件,什么時(shí)候不要攔截內(nèi)部子View的觸摸事件
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //▲▲▲1.求第一個(gè)觸點(diǎn)的id, 此時(shí)可能有多個(gè)觸點(diǎn),但至少一個(gè)
        mPointerId = ev.getPointerId(0);
        mYDown = ev.getRawY();
        mYLastMove = mYDown;
        break;
      case MotionEvent.ACTION_MOVE:
        mYMove = ev.getRawY();
        float diff = Math.abs(mYMove - mYDown);
        mYLastMove = mYMove;
        // 當(dāng)手指拖動(dòng)值大于TouchSlop值時(shí),認(rèn)為應(yīng)該進(jìn)行滾動(dòng),攔截子控件的事件
        if (diff > mTouchSlop) {
          return true;
        }
        break;
    }
    return super.onInterceptTouchEvent(ev);
  }


  @Override
  public boolean onTouchEvent(MotionEvent event) {
    //▲▲▲2.向VelocityTracker添加MotionEvent
    acquireVelocityTracker(event);
    switch (event.getAction()) {
      case MotionEvent.ACTION_MOVE:

        //▲▲▲3.求偽瞬時(shí)速度
        mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
        velocityY = mVelocityTracker.getYVelocity(mPointerId);

        mYMove = event.getRawY();
        int scrolledY = (int) (mYLastMove - mYMove);//注意取的是負(fù)值,因?yàn)槭钦麄€(gè)布局在動(dòng),而不是控件在動(dòng)


        if (getScrollY() + scrolledY < topBorder) {// 如果已經(jīng)在最上端了,就不讓再往上滑動(dòng)了(重點(diǎn)一、邊界判斷,直接照著這個(gè)模板抄就行)
          scrollTo(0, topBorder);
          return true;//★★★★★★★★★★★★★★★★這里返回true或者false實(shí)踐證明都可以,但是不能什么都不返回。
        } else if (getScrollY() + getHeight() + scrolledY > bottomBorder) {//如果已經(jīng)在最底部了,就不讓再往底部滑動(dòng)了
          scrollTo(0, bottomBorder - getHeight());
          return true;//★★★★★★★★★★★★★★★★★這里返回true或者false實(shí)踐證明都可以,但是不能什么都不返回。
        }

        scrollBy(0, scrolledY);//手指move時(shí),布局跟著滾動(dòng)
        if (mYDown <= mYMove) {//★★★判斷手指向上滑動(dòng),還是向下滑動(dòng),要用mYDown,而不是mYLastMove
          shouZhiXiangXiaHuaDong = true;//手指往下滑動(dòng)
        } else {
          shouZhiXiangXiaHuaDong = false;//手指往上滑動(dòng)
        }
        mYLastMove = mYMove;
        break;
      case MotionEvent.ACTION_UP:
//        4.▲▲▲釋放VelocityTracker
        releaseVelocityTracker();

        // 第二步,當(dāng)手指抬起時(shí),根據(jù)當(dāng)前的滾動(dòng)值以及滾動(dòng)方向來(lái)判定應(yīng)該滾動(dòng)到哪個(gè)子控件的界面,并且記得調(diào)用invalidate();
        if (Math.abs(velocityY) > criticalVelocityY) {//當(dāng)手指滑動(dòng)速度快時(shí),按照速度方向直接翻頁(yè)
//          重點(diǎn)二、快速滑動(dòng)時(shí),如何判斷當(dāng)前顯示的是第幾個(gè)控件,并且再次包含邊界判斷(必須包含邊界判斷,因?yàn)榍懊娴倪吔缗袛?,只適用于低速滑動(dòng)時(shí))
          if (shouZhiXiangXiaHuaDong) {
            if (currentPage > 1) {//★★★★★★★★邊界限制,防止滑倒第一個(gè),還繼續(xù)滑動(dòng),注意★(currentPage-2)
              mScroller.startScroll(0, getScrollY(), 0, childHeight * (currentPage - 2) - getScrollY());
              currentPage--;
            }
          } else {
            if (currentPage < childCount) {//★★★★★★★邊界限制,防止滑倒最后一個(gè),還繼續(xù)滑動(dòng),注意★currentPage
              mScroller.startScroll(0, getScrollY(), 0, childHeight * currentPage - getScrollY());
              currentPage++;
            }
          }
          Log.e("eee", currentPage + "");
        } else {//當(dāng)手指滑動(dòng)速度不夠快時(shí),按照松手時(shí),已經(jīng)滑動(dòng)的位置來(lái)決定翻頁(yè)

//       重點(diǎn)三、低速滑動(dòng)時(shí),如何根據(jù)位置來(lái)判斷當(dāng)前顯示的是第幾個(gè)控件,(這里不必再次進(jìn)行邊界判斷,因?yàn)榈谝淮蔚倪吔缗袛?,在這里會(huì)起到作用)
          if ((getScrollY() >= childHeight * (currentPage - 1) + childHeight / 2 && !shouZhiXiangXiaHuaDong)) {
//           手指向上滑動(dòng)并且,滾動(dòng)距離過(guò)了屏幕一半的距離
            mScroller.startScroll(0, getScrollY(), 0, childHeight * (currentPage) - getScrollY());
            currentPage++;

          } else if ((getScrollY() < childHeight * (currentPage - 1) + childHeight / 2 && !shouZhiXiangXiaHuaDong)) {
//           手指向上滑動(dòng)并且,滾動(dòng)距離沒有過(guò)屏幕一半的距離
            mScroller.startScroll(0, getScrollY(), 0, childHeight * (currentPage - 1) - getScrollY());

          } else if
              ((getScrollY() <= childHeight * (currentPage - 2) + childHeight / 2
                  && shouZhiXiangXiaHuaDong)) {
//            手指向下滑動(dòng)并且,滾動(dòng)距離過(guò)了屏幕一半的距離
            mScroller.startScroll(0, getScrollY(), 0, childHeight * (currentPage - 2) - getScrollY());
            currentPage--;
          } else if
              ((getScrollY() > childHeight * (currentPage - 2) + childHeight / 2
                  && shouZhiXiangXiaHuaDong)) {
//            手指向下滑動(dòng)并且,滾動(dòng)距離沒有過(guò)屏幕一半的距離
            mScroller.startScroll(0, getScrollY(), 0, childHeight * (currentPage - 1) - getScrollY());
          }

 

 


          /* if ((getScrollY() >= childHeight && !shouZhiXiangXiaHuaDong)//手指往左滑動(dòng),并且滑動(dòng)完全顯示第二個(gè)控件時(shí),viewgroup滑動(dòng)到最右端
              || ((getScrollY() >= (totalChildHeight - firstChildHeight - lastChildHeight) && shouZhiXiangXiaHuaDong))) {//手指往右滑動(dòng),并且當(dāng)滑動(dòng)沒有完全隱藏最后一個(gè)控件時(shí),viewgroup滑動(dòng)到最右端
//          當(dāng)滾動(dòng)值大于某個(gè)數(shù)字時(shí)(大于第二個(gè)控件的寬度,即完全顯示第二個(gè)控件時(shí))并且是向左滑動(dòng),讓這個(gè)viewgroup滑動(dòng)到整個(gè)Viewgroup的最右側(cè),
//          因?yàn)橛覀?cè)的所有控件寬度是600,而現(xiàn)在已經(jīng)滑動(dòng)的距離是getScrollX,
//          那么,還應(yīng)該繼續(xù)滑動(dòng)的距離是600-getScrollX(),這里正值表示向右滑動(dòng)
            mScroller.startScroll(0,getScrollY(), 0, (totalChildHeight - firstChildHeight) - getScrollY());
          } else if ((getScrollY() <= (totalChildHeight - firstChildHeight - lastChildHeight) && shouZhiXiangXiaHuaDong)//手指往右滑動(dòng),并且當(dāng)滑動(dòng)完全隱藏最后一個(gè)控件時(shí),viewgroup滑動(dòng)到最左端
              || (getScrollY() <= childHeight && !shouZhiXiangXiaHuaDong)) {//手指往左滑動(dòng),并且滑動(dòng)沒有完全顯示第二個(gè)控件時(shí),viewgroup滑動(dòng)到最左端

//          當(dāng)滾動(dòng)值小于某個(gè)數(shù)字時(shí),讓這個(gè)viewgroup滑動(dòng)到整個(gè)Viewgroup的最左側(cè),
//          因?yàn)榛瑒?dòng)到最左側(cè)時(shí),就是讓整個(gè)viewgroup的滑動(dòng)量為0,而現(xiàn)在已經(jīng)滑動(dòng)的距離是getScrollX,
//          那么,還應(yīng)該繼續(xù)滑動(dòng)的距離是0-getScrollX(),這里負(fù)值表示向左滑動(dòng)
            mScroller.startScroll(0,getScrollY(), 0, 0 - getScrollY());
          }*/
        }
//        必須調(diào)用invalidate()重繪
        invalidate();
        break;

      case MotionEvent.ACTION_CANCEL:
//       5.▲▲▲釋放VelocityTracker
        releaseVelocityTracker();

        break;
    }
    return super.onTouchEvent(event);
  }


  @Override
  public void computeScroll() {
    // 第三步,重寫computeScroll()方法,并在其內(nèi)部完成平滑滾動(dòng)的邏輯
    if (mScroller.computeScrollOffset()) {
      scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      invalidate();
    }
  }

  /**
   * @param event 向VelocityTracker添加MotionEvent
   * @see VelocityTracker#obtain()
   * @see VelocityTracker#addMovement(MotionEvent)
   */
  private void acquireVelocityTracker(final MotionEvent event) {
    if (null == mVelocityTracker) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(event);
  }

  /**
   * 釋放VelocityTracker
   *
   * @see VelocityTracker#clear()
   * @see VelocityTracker#recycle()
   */
  private void releaseVelocityTracker() {
    if (null != mVelocityTracker) {
      mVelocityTracker.clear();
      mVelocityTracker.recycle();
      mVelocityTracker = null;
    }
  }


   /*  getScrollX()指的是由viewgroup調(diào)用View的scrollTo(int x, int y)或者scrollBy(int x, int y)產(chǎn)生的X軸的距離
//        換句話說(shuō),就是你手指每次滑動(dòng),引起的是viewgroup累計(jì)滑動(dòng)的距離,右為正
//        指的是相當(dāng)于控件的左上角的為原點(diǎn)的坐標(biāo)值
        Log.e("qqq","getX():"+event.getX());
//        指的是相當(dāng)于屏幕的左上角的為原點(diǎn)的坐標(biāo)值
        Log.e("qqq","getRawX():"+event.getRawX());*/
}

布局文件

 <beautlful.time.com.beautlfultime.view.VerticalViewPager
      android:id="@+id/verticalViewPager"
      android:layout_width="match_parent"
      android:layout_height="150dp">
      <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
      android:background="@android:color/holo_orange_dark"
        android:text="聊天具體的信息喲" />

      <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:text="置頂" />

      <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary"
        android:text="刪除" />
      <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:text="美好" />

 

    </beautlful.time.com.beautlfultime.view.VerticalViewPager>


 感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

最新評(píng)論