Android 自定義View 自定義viewpager Scroller處理詳解(從入門到巔峯 )

但是很多大家所熟知的控件在內部都是使用Scroller來實現的,如ViewPager、ListView等。

 

scrollBy方法

scrollTo方法

x:X軸(水平)方向移動到目的座標。正值向左移動,負值向右移動,單位是像素。

y:Y軸(垂直)方向移動到目的座標。正值向上移動,負值向下移動,單位是像素。 

總結:

移動是反的和座標系

 

view裏面有scrollTo方法

layout_wx_sync_btn.scrollBy(100,100);
share_newsport_layout.scrollBy(100,100);

 

scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//回彈的效果,會移動
mScroller.startScroll(getScrollX(), 0, dx, 0);
public void computeScroll() {}
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//回彈的效果,會移動

 

Scroller 的 getCurX() 和 getCurY() 來獲取當前時刻 View 應該滾動的位置

 

public class ScollerActivity extends Activity {

    private ScrollerLayout scrollerLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scoller);

        scrollerLayout = (ScrollerLayout) findViewById(R.id.activity_marginlayout_scrollerlayout);

        /**
         * 動態添加view
         */
        scrollerLayout.removeAllViewsInLayout();
        for (int i = 0; i < 3; i++) {
            TextView textView = new TextView(this);
            textView.setBackgroundColor(Color.RED);
            textView.setTextColor(Color.WHITE);
            textView.setClickable(true);
            ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);
            textView.setText("Scroller滑動Demo-" + "TextView-" + (i + 1));
            textView.setGravity(Gravity.CENTER);
            textView.setLayoutParams(layoutParams);
            scrollerLayout.addView(textView);
        }


    }
}

 


 
public class ScrollerLayout extends ViewGroup {

/**
* 用於完成滾動操作的實例
*/
private Scroller mScroller;

/**
* 判定爲拖動的最小移動像素數
*/
private int mTouchSlop;

/**
* 手機按下時的屏幕座標
*/
private float mXDown;

/**
* 手機當時所處的屏幕座標
*/
private float mXMove;

/**
* 上次觸發ACTION_MOVE事件時的屏幕座標
*/
private float mXLastMove;

/**
* 界面可滾動的左邊界
*/
private int leftBorder;

/**
* 界面可滾動的右邊界
*/
private int rightBorder;

public ScrollerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// 第一步,創建Scroller的實例
mScroller = new Scroller(context);
ViewConfiguration configuration = ViewConfiguration.get(context);
// 獲取TouchSlop值
/***
* 這個值在後面將用於判斷當前用戶的操作是否是拖動。
*/
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
}

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

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
// 爲ScrollerLayout中的每一個子控件在水平方向上進行佈局
childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
}
// 初始化左右邊界值
leftBorder = getChildAt(0).getLeft();//第一個控件的左邊
rightBorder = getChildAt(getChildCount() - 1).getRight();//最後一個控件的右邊
}
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mXDown = ev.getRawX();//相對屏幕的距離
mXLastMove = mXDown;
Log.d("peng","onInterceptTouchEvent ACTION_DOWN ");

break;
case MotionEvent.ACTION_MOVE:
mXMove = ev.getRawX();
float diff = Math.abs(mXMove - mXDown);
mXLastMove = mXMove;
// 當手指拖動值大於TouchSlop值時,認爲應該進行滾動,攔截子控件的事件
if (diff > mTouchSlop) {
Log.w("peng","diff > mTouchSlop"+mXMove+"mXLastMove:"+mXLastMove);
return true;
}
Log.d("peng","onInterceptTouchEventACTION_MOVE ");

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


/**
*
* getScrollX();
* 當前View視圖左上角座標與View視圖初始位置x軸方向上的距離。
*
*
* 那麼當我們把事件攔截掉之後,就會將事件交給ScrollerLayout的onTouchEvent()方法來處理
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:

mXMove = event.getRawX();//觸摸點距離x的距離
Log.i("peng","onTouchEvent_ACTION_MOVE"+mXMove);

//mXLastMove 開始拖動的距離
//mXMove 移動的距離
int scrolledX = (int) (mXLastMove - mXMove);
//不添加會滑出去
if (getScrollX() + scrolledX < leftBorder) {
scrollTo(leftBorder, 0);// 左邊-------左邊邊界條件,移動
Log.w("peng","scrollTo left"+leftBorder+"scrolledX="+scrolledX+"getScrollX();"+getScrollX());
return true;
} else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
scrollTo(rightBorder - getWidth(), 0);//-------右邊邊界條件
Log.w("peng","scrollTo right"+rightBorder);
return true;
}

scrollBy(scrolledX, 0);//移動的差值就是滾動的距離
mXLastMove = mXMove;
break;
case MotionEvent.ACTION_UP:
// 當手指擡起時,根據當前的滾動值來判定應該滾動到哪個子控件的界面
int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
int dx = targetIndex * getWidth() - getScrollX();
// 第二步,調用startScroll()方法來初始化滾動數據並刷新界面
/**
* 回彈,滾動到哪個位置
*/
mScroller.startScroll(getScrollX(), 0, dx, 0);
Log.d("peng","onTouchEventACTION_UP");
invalidate();
break;
}
return super.onTouchEvent(event);
}

/**
* 手滑動停止
*/
@Override
public void computeScroll() {
// 第三步,重寫computeScroll()方法,並在其內部完成平滑滾動的邏輯
if (mScroller.computeScrollOffset()) {
Log.d("peng","computeScroll");
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//回彈的效果,會移動
invalidate();
}
}
}

 

https://blog.csdn.net/weixin_37730482/article/details/80649317

https://www.cnblogs.com/andy-songwei/p/11213718.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章