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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值